Django的模型继承
今天学习了一下Django的model 继承,特此总结一下。
Django的继承主要分为三种:
- 抽象基类;
- 多表继承;
- 代理继承;
不同的继承类型适用于特定的场景,没有谁是最好这一说。
1、抽象基类
如果想要创建多个model,每个model都有相同的字段,而我们不想重复再写它们,此时就可以创建一个抽象基类,该基类中含有一些公共字段。
抽象基类的最大特点是在Meta类中要设置abstract=True,基类不会创建表,含有的字段会自动写入到父类中。但这里要注意一点的是,基类和子类不能含有同名的字段,否则系统会抛出异常。
下面是我在自己项目中写的关于评论系统的model:
class BaseComment(models.Model):
create_time = models.DateTimeField(auto_now=True)
ip = models.GenericIPAddressField(protocol='IPv4')
def save(self,*args,**kwargs):
"""
try:
ip_address_validators('ipv4',self.ip)
except ValidationError:
return
"""
super(BaseComment,self).save(*args,**kwargs)
class Meta:
#the class is base class,include common info,do not create table
abstract = True
ordering = ['-create_time']
get_latest_by = 'create_time'
class ReplyComment(BaseComment):
content = models.TextField()
comment = models.ForeignKey(Comment,related_name='reply_comment')
author = models.ForeignKey(Author,related_name='reply_comment_author')
reply_to = models.ForeignKey(Author,related_name='reply_to_author')
def __unicode__(self):
return "{0}@{1}".format(self.author,self.reply_to)
class Meta(BaseComment.Meta):
verbose_name = 'replycomment'
上例中,虽然子类继承了抽象基类BaseComment的Meta,但是abstract会自动变成FALSE。
2.多表继承
多表继承和抽象继承的区别是基类不需要添加abstract=True一项。多表继承会在父类和子类分别创建一张数据表,只不过是每张表都只包含自己定义的字段,但是父类中的字段再子类中是有效的。
这个我自己写过model,当时是像实现第一种,但就是没加abstract=True,然后看子类创建的表中怎们没显示父类的字段,后来才明白是咋回事,算是阴差阳错吧。我就直接把官网中的拿过来吧。
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
我们可以这样访问:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
说子类可以访问父类的字段,那两者之间的关联是怎么创建的呢?
其实,当使用多重继承时,会在子类中自动创建一个OneToOneField,来实现两者的关联。
当执行Python manege.py migrate生成表格之后,你会在Restaurant中发现多一个字段“ place_ptr_id
”。
也就是说,原生的SQL类似于:
CREATE TABLE "blog_place" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(50) NOT NULL,
"address" varchar(80) NOT NULL
)
;
CREATE TABLE "blog_restaurant" (
"place_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_place" ("id"),
"serves_hot_dogs" bool NOT NULL,
"serves_pizza" bool NOT NULL
)
;
再说下多重继承的Meta。 多表继承中,父类的Meta属性一般不会继承到子类中,但是,ordering和 get_latest_by是继承的,如果子类想重构ordering,则可以手动显式的指定ordering=[]或者任何自己想要的值。
然后,在多重继承还有一点要注意,即当你在创建子类Model的时候,如果有多对一,或者多对多的字段一定要指定realated_name。因为多重继承自动创建的一对一字段的默认related_name和多对一以及多对多都是相同的,这样会引起冲突,因此要自定义一下。
3.代理继承
在多表继承中,子类模型会创建一个数据表来存储不在父类模型中的额外字段,但是,如果我们只想改变某个模型的行为方法,而不是添加额外的字段,我们就可以使用代理模型。代理model(proxy model)会继承父类的属性,并且只是添加一些对属性的操作的自定义方法而已。
还是拿官网的例子:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
这里MyPerson不会创建数据表,而是和Person操作同一张表。Person创建的任何实例MyPerson都可以访问。看到你就应该知道,父类不可以是抽象基类,因为抽象基类不会创建表,你没有表,还代理个毛线啊。看到了吧,就是这么强大。其实代理继承有点设计模式中的proxy pattern代理模式的idea,我代理你去做一些事情。
其实除了上面的三种继承方式,还有一种叫做多重继承,有点类似Python中的多重继承, 出现特定名称的第一个基类(比如Meta)是所使用的那个。例如,这意味着如果多个父类含有 Meta类,只有第一个会被使用,剩下的会忽略掉。
我今天试着创建多重继承,但是抛出异常了:
class Article(models.Model):
headline = models.CharField(max_length=50)
body = models.TextField()
class Book(models.Model):
title = models.CharField(max_length=50)
class BookReview(Book, Article):
pass
直接提示我blog.BookReview:the field 'id' from parent sArticle 'id' and 'id' from parent model 'Book',意思是有字段冲突。
多重继承model其实用得不多。
微信分享/微信扫码阅读