Django里面的model继承

Django的ORM(Object-Relational Mapping)将对象和数据库表格做了映射关系,在大多数情况下,都不需要额外使用SQL语句对数据库进行访问。

Django在Model是使用中还支持继承方式。

Model的继承在Django里面有三种方式:

  1. 如果希望父类里面定义的信息可以在子类里面不需要重复写,比如有好多的表,都需要a字段,那么可以定义父类具有a字段,所有的子类所映射的表就会都具有a字段,这种方式在Django里面称为抽象基类(Abstract base classes)。
  2. 现在已经有一个model,希望扩展这个model的属性,但又不希望破环原有的model,通常我们通过1:1映射建立一张新表,而Django的多表继承(Multi-table inheritance),能够自动完成这件事。
  3. 最后如果想使得一个model具有一些额外的功能,但又不想破环原有的model,那么就需要代理model(Proxy models),这样不会有额外的表出现,但又可以增加额外的对象行为。

抽象基类:

编写一下model, 并且People类的内部Meta class设置成abstract = True

from django.db import models
# Create your models here.
class People(models.Model):
name = models.CharField(max_length=200)
class Meta:
abstract = True
class Teacher(People):
teacher_info = models.CharField(max_length=200)

class Student(People):
student_info = models.CharField(max_length=200)

然后调用makemigrations

(py37) C:\tmp\mysite>python manage.py makemigrations
Migrations for 'mytest':
mytest\migrations\0001_initial.py
- Create model Student
- Create model Teacher

看一下生成的migrations文件,完整的生成两个表格

(py37) C:\tmp\mysite>more mytest\migrations\0001_initial.py
# Generated by Django 2.1.7 on 2019-02-12 15:19
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Student',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('student_info', models.CharField(max_length=200)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Teacher',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('teacher_info', models.CharField(max_length=200)),
],
options={
'abstract': False,
},
),
]

完成migrate

(py37) C:\tmp\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, mytest, sessions
Running migrations:
....
Applying mytest.0001_initial... OK
....

可以看到生成的表格

(py37) C:\tmp\mysite>c:\sqlite\sqlite3.exe db.sqlite3
SQLite version 3.27.1 2019-02-08 13:17:39
Enter ".help" for usage hints.
sqlite> .tables
....
mytest_student
...
mytest_teacher
sqlite> .schema mytest_student
CREATE TABLE IF NOT EXISTS "mytest_student" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(200) NOT NULL, "student_info" varchar(200) NOT NULL);
sqlite> .schema mytest_teacher
CREATE TABLE IF NOT EXISTS "mytest_teacher" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(200) NOT NULL, "teacher_info" varchar(200) NOT NULL);

使用python manage.py shell测试一下这两个model

(py37) C:\tmp\mysite>python manage.py shell
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from mytest.models import *
>>> t = Teacher(name="teacher1", teacher_info="aaaa")
>>> t.save()
>>> s = Student(name="student1", student_info="bbbb")
>>> s.save()
>>> p = People(name="people1")
>>> p.save() Traceback (most recent call last):
File "<console>", line 1, in <module>
File "c:\py37\lib\site-packages\django\db\models\base.py", line 718, in save
force_update=force_update, update_fields=update_fields)
File "c:\py37\lib\site-packages\django\db\models\base.py", line 748, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "c:\py37\lib\site-packages\django\db\models\base.py", line 797, in _save_table
pk_val = self._get_pk_val(meta)
File "c:\py37\lib\site-packages\django\db\models\base.py", line 553, in _get_pk_val

return getattr(self, meta.pk.attname)
AttributeError: 'NoneType' object has no attribute 'attname'
>>> People.objects.all() Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: type object 'People' has no attribute 'objects'
>>> Teacher.objects.all()
<queryset>]>
>>> Student.objects.all()
<queryset>]>
/<queryset>/<queryset>/<module>/<console>/<module>/<console>

查看一下存入的数据

(py37) C:\tmp\mysite>c:\sqlite\sqlite3.exe db.sqlite3
SQLite version 3.27.1 2019-02-08 13:17:39
Enter ".help" for usage hints.
sqlite> select * from mytest_student;
1|student1|bbbb
sqlite> select * from mytest_teacher;
1|teacher1|aaaa
sqlite> .exit

多表继承

多表继承则会自动产生1:1的映射关系。

如下代码

from django.db import models
# Create your models here.
class People(models.Model):
name = models.CharField(max_length=200)
class Teacher(People):
teacher_info = models.CharField(max_length=200)
class Student(People):
student_info = models.CharField(max_length=200)

调用makemigrations,可以看到会生成三个表

(py37) C:\tmp\mysite>python manage.py makemigrations
Migrations for 'mytest':
mytest\migrations\0001_initial.py
- Create model People
- Create model Student

- Create model Teacher

具体可以看一下migration文件, 可以看到自动生成的people_ptr 1:1的映射

(py37) C:\tmp\mysite>more mytest\migrations\0001_initial.py
# Generated by Django 2.1.7 on 2019-02-12 15:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='People',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
],
),
migrations.CreateModel(
name='Student',
fields=[
('people_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mytest.People')),
('student_info', models.CharField(max_length=200)),
],
bases=('mytest.people',),
),
migrations.CreateModel(
name='Teacher',
fields=[
('people_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mytest.People')),
('teacher_info', models.CharField(max_length=200)),
],
bases=('mytest.people',),
),
]

migrate之后,去数据库可以看到具体产生的表结构, people表含有name字段,student和teacher表都没有name字段,但是都有一个指向people表的外键,同时也是自己的主键,用以完成1:1的映射关系。

(py37) C:\tmp\mysite>c:\sqlite\sqlite3.exe db.sqlite3
SQLite version 3.27.1 2019-02-08 13:17:39
Enter ".help" for usage hints.
sqlite> .tables
...
mytest_people
mytest_student
mytest_teacher
...
sqlite> .schema mytest_people
CREATE TABLE IF NOT EXISTS "mytest_people" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(200) NOT NULL);
sqlite> .schema mytest_student
CREATE TABLE IF NOT EXISTS "mytest_student" ("people_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "mytest_people" ("id") DEFERRABLE INITIALLY DEFERRED, "student_info" varchar(200) NOT NULL);
sqlite> .schema mytest_teacher
CREATE TABLE IF NOT EXISTS "mytest_teacher" ("people_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "mytest_people" ("id") DEFERRABLE INITIALLY DEFERRED, "teacher_info" varchar(200) NOT NULL);
sqlite> .exit

使用python manage.py shell测试一下新建的model

(py37) C:\tmp\mysite>python manage.py shell
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from mytest.models import *
>>> p = People(name="people1")
>>> p.save() >>> t = Teacher(name="teacher1", teacher_info="t_info")
>>> t.save() >>> s = Student(name="student1", student_info="s_info")
>>> s.save()
>>> People.objects.all() <queryset>, <people>, <people>]>
>>> Teacher.objects.all() <queryset>]>
>>> Student.objects.all()
<queryset>]>
>>> exit()
/<queryset>/<queryset>/<people>/<people>/<queryset>

查看一下数据库里面的记录

(py37) C:\tmp\mysite>c:\sqlite\sqlite3.exe db.sqlite3
SQLite version 3.27.1 2019-02-08 13:17:39
Enter ".help" for usage hints.
sqlite> select * from mytest_people ...> ;
1|people1
2|teacher1
3|student1
sqlite> select * from mytest_student; 3|s_info
sqlite> select * from mytest_teacher;
2|t_info

sqlite> .exit

代理模型(Proxy models)

代理模型主要用在不想修改原有model,但是又想提供额外功能,并且不增加额外数据属性的情况下,通过把Model的Meta class下, proxy设置为True,用于表示这是一个代理模型。

比如下面的代码:

from django.db import models
# Create your models here.
class People(models.Model):
name = models.CharField(max_length=200)
class MyPeople(People):
class Meta:
proxy = True def say_hi(self):
print("hi " + self.name)

makemigrations可以看到model还是生成了两套model

(py37) C:\tmp\mysite>python manage.py makemigrations
Migrations for 'mytest':
mytest\migrations\0001_initial.py
- Create model People
- Create proxy model MyPeople
(py37) C:\tmp\mysite>more mytest\migrations\0001_initial.py
# Generated by Django 2.1.7 on 2019-02-12 15:49
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='People',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
],
),
migrations.CreateModel(

name='MyPeople',
fields=[
],
options={
'proxy': True,
'indexes': [],
},
bases=('mytest.people',),
),
]

migrate之后可以看到表格只有一个。

(py37) C:\tmp\mysite>c:\sqlite\sqlite3.exe db.sqlite3
SQLite version 3.27.1 2019-02-08 13:17:39
Enter ".help" for usage hints.
sqlite> .tables
...
mytest_people
...
sqlite> .schema mytest_people
CREATE TABLE IF NOT EXISTS "mytest_people" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(200) NOT NULL);
sqlite> .exit

测试一下新建的model, 可以看到People新建的实例,马上可以通过MyPeople获取,同时可以调用MyPeople的方法。

(py37) C:\tmp\mysite>python manage.py shell
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from mytest.models import *
>>> p = People(name="people1")
>>> p.save()
>>> my_p = MyPeople.objects.get(name="people1")
>>> my_p.name
'people1'
>>> my_p.say_hi()
hi people1

以上就是Django里面的三种model继承方式。


分享到:


相關文章: