Django的模型继承

        今天学习了一下Django的model 继承,特此总结一下。

        Django的继承主要分为三种:

  1. 抽象基类;
  2. 多表继承;
  3. 代理继承;

        不同的继承类型适用于特定的场景,没有谁是最好这一说。

        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.代理继承

        在多表继承中,子类模型会创建一个数据表来存储不在父类模型中的额外字段,但是,如果我们只想改变某个模型的行为方法,而不是添加额外的字段,我们就可以使用代理模型代理modelproxy 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其实用得不多。

--------EOF---------
微信分享/微信扫码阅读