python做为一门面向对象语音,其继承特性是做的非常好的。如果能在models定义中将一些通用字段放在父类中,子类直接继承,然后添加一些自己独有的字段,岂不是可以省很多创建模型的时间。这一篇我们就来看看这个问题。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
纯语法定义继承
在models.py
中创建如下People
类做为一会要用的父类
class People(models.Model):
p_name = models.CharField(max_length=16)
p_sex = models.BooleanField(default=True)
然后创建两个子类American
和Chinese
继承自People
class American(People):
a_state = models.CharField(max_length=16)
class Chinese(People):
c_province = models.CharField(max_length=16)
按照正常逻辑,我们只想要American
和Chinese
这两张表,但是完成makemigrations
和migrate
以后和我们设想的不一样,一共出现了三张表,也就是说People
表也被创建了。
而且在American
和Chinese
中都多了一个叫people_ptr_id
的字段,查看DDL如下
create table Four_american
(
people_ptr_id int not null
primary key,
a_state varchar(16) not null,
constraint Four_american_people_ptr_id_733979de_fk_Four_people_id
foreign key (people_ptr_id) references Four_people (id)
);
create table Four_chinese
(
people_ptr_id int not null
primary key,
c_province varchar(16) not null,
constraint Four_chinese_people_ptr_id_15a467cd_fk_Four_people_id
foreign key (people_ptr_id) references Four_people (id)
);
发现这个people_ptr_id
既是每个表的主键,又是指向父类表主键的一个外键。按照前面的学习,这是一对一的SQL逻辑。下面我们就往两个子类的表中添加数据来验证一下。
创建如下的路由和view函数来添加数据
path('add_american/', views.add_american, name='add_american'),
path('add_chinese/', views.add_chinese, name='add_chinese'),
def add_american(request):
state = request.GET.get('state')
name = request.GET.get('name')
american = American()
american.p_name = name
american.a_state = state
american.save()
return HttpResponse('Add american successfully')
def add_chinese(request):
province = request.GET.get('province')
name = request.GET.get('name')
chinese = Chinese()
chinese.p_name = name
chinese.c_province = province
chinese.save()
return HttpResponse('Add chinese successfully')
然后就可以通过类似http://127.0.0.1:8000/four/add_chinese/?province=hubei&name=hanmeimei
和http://127.0.0.1:8000/four/add_american/?state=californiai&name=james
来添加数据了。结果生成的数据如下
American:
Chinese:
People:
添加的数据在添加到American
或者Chinese
的同时也会被添加到People
中,并且通过id
或者people_ptr_id
来唯一确定。
这显然不是我们想要的现象,理想的情况是子类只继承父类的字段,而每个子类是彼此独立的。
直接执行python manage.py migrate Four 0003_auto_20200401_2321
撤销这一次的迁移
关于迁移的原理和撤销回退操作可以参考我的另一篇博客《Django2.2中迁移(makemigrations和migrate)的原理和撤销回退操作图文详解》
加上Abstract定义继承
撤销了上面的迁移以后,我们修改下models.py
中的People
的定义
class People(models.Model):
p_name = models.CharField(max_length=16)
p_sex = models.BooleanField(default=True)
class Meta:
abstract = True
这里在Meta
中添加了abstract=True
的说明,将People
变为一个抽象类,也就意味着不会在数据库中显示了。
然后再进行迁移,之后发现数据库中只有子类的两个表了
点开两个表看一下,发现成功继承了People
的p_name
和p_sex
字段
American:
Chinese:
这表结构和我们之前设想的一样,DDL也证明没有和别的表关联
create table Four_chinese
(
id int auto_increment
primary key,
p_name varchar(16) not null,
p_sex tinyint(1) not null,
c_province varchar(16) not null
);
create table Four_american
(
id int auto_increment
primary key,
p_name varchar(16) not null,
p_sex tinyint(1) not null,
a_state varchar(16) not null
);
下面来添加数据验证下,同样使用上面的路由和view函数,通过类似http://127.0.0.1:8000/four/add_american/?state=maryland&name=james
和http://127.0.0.1:8000/four/add_chinese/?province=taiwan&name=amei
来添加数据。
发现两个表的数据是完全独立的,id彼此没有顺序
American:
Chinese:
这样就达到了只继承字段,不添加额外表关联的目的。
总结
企业开发中,合理利用继承可以使得模型的创建更高效。不过还有另一种情况,就是和你合作的那个数据开发不会写python所以只能写好SQL,给了你一堆现成的表,又该如何反向转为Django中的模型供我们使用呢?我们下一篇一起来看看这个问题。