美文网首页
10、Django_模型类的继承以及用包来管理模型类

10、Django_模型类的继承以及用包来管理模型类

作者: 猪儿打滚 | 来源:发表于2019-10-12 14:13 被阅读0次

    面向对象语言的三大特性:多态、封装、继承。Django中模型类也拥有这些特性,本章说明下模型类的继承

    概述

    Django中,不管是直接继承也好,间接继承也罢,所有的模型类的基类是django.db.models.Model

    • 模型类的分类
      非抽象类:是一个独立自主的,会在数据库中创建数据表的模型类,在 Meta元数据中定义abstract = True
      抽象类:只是用于保存子类模型共有内容,并不会在数据库中创建数据表的模型类
    • 模型类的继承方式
      1.继承抽象基类:被用来继承的的抽象基类模型被称为Absstract base classes
      2.多表继承:Multi-table inheritance,子父类都是独立自主,在数据库中创建数据表,功能完整的模型类
      3.代理模型:当我们只想修改模型的Python层面的行为,而并不想改动模型的字段时使用
    一、继承抽象基类
    • 抽象模型类
      Meta元数据中定义abstract = True的模型类。Django不会为这种模型类创建数据库表,并且也不会有管理器,不能被实例化和直接保存。其用处就是用来保存子模型类共有的内容,被子模型类继承使用。当抽象模型类被子模型类继承时,它的字段会全部复制到子模型类中。

    • 注意点
      1.子模型会继承抽象基类的所有字段
      2.子模型可以额外添加元数据,如果子模型没有声明自己的Meta类,那么会直接继承抽象基类的Meta类
      3.抽象基类拥有的元数据,如果子模型没有,则子模型直接继承
      4.抽象基类拥有的元数据,如果子模型也有,则子模型的进行覆盖
      5.抽象基类元数据中的abstract = True不会被继承。如果子模型也想成为抽象类,则需要在子模型中的元数据定义该字段
      6.有一些元数据对于抽象基类来说是无效的,比如说db_table,因为抽象类不会生成实际数据表,并且它的子模型并不会按照这个元数据来设置表名

    • 下面例子中,Cat模型拥有named、age、skill这三个字段,Animal抽象基类不会生成数据表,不能当成一个正常的模型来使用。
    class Animal(models.Model):
        named = models.CharField(max_length=64)
        age = models.PositiveIntegerField()
    
        class Meta:
            abstract = True
            ordering = ['age']
    
    
    class Cat(Animal):
        skill = models.CharField(max_length=32)
        
        class Meta:
            db_table = 'cat'
    
    • related_namerelated_query_name参数
      如果抽象基类中,存在使用了related_namerelated_query_name参数的ForeignKeyManyToMany字段,那么要注意:
      默认情况下,每个子类都会继承抽象基类的所有字段,所以每个子类都会拥有同样的related_namerelated_query_name字段值,这样会导致错误。所以,在抽象基类中使用了related_namerelated_query_name参数时,它们的值应该包含%(app_label)s%(class)s部分
      1.%(app_label)s:使用子类的小写名字替换
      2.%(class)s:使用子类所属App的小写名替换
    • 例子:
      1.modelsinherit.Cat.key字段的reverse name/反向关系名是modelsinherit_cat_relatedreverse query name/反向查询名是modelsinherit_cat
      2.otherApp.Bird.key的反向关系名是otherapp_bird_related,反向查询名是otherapp_bird
    class Animal(models.Model):
        key = models.ManyToManyField(
            xxxModel,
            related_name='%(app_label)s_%(class)s_related',
            related_query_name='%(app_label)s_%(class)s'
        )
    
        class Meta:
            abstract = True
            ordering = ['age']
    ### 同个应用的模块类
    class Cat(Animal):
        skill = models.CharField(max_length=32)
    
    class Dog(Animal):
        skill = models.CharField(max_length=32)
    
    ### 其它应用的模块类(otherApp)
    from modelsinherit.models import Animal
    
    class Bird(Animal):
         skill = models.CharField(max_length=32)
    
    二、多表继承/继承正常模型类

    这种继承方式,父类和子类都是子父类都是独立自主,在数据库中创建数据表,功能完整的模型类。并且内部隐含了一对一的关系

    • 例子
      1.Cat包含Animal的所有字段,并且各自拥有自己的数据表
      2.如果一个Animal对象同时也是一个Cat对象,那么它可以从父类获取子类:a = Animal.object.get(id=1); a.cat #获取一个cat对象
    class Animal(models.Model):
        named = models.CharField(max_length=64)
        age = models.PositiveIntegerField()
    
    class Cat(Animal):
        skill = models.CharField(max_length=32)
    
    • 多表继承的Meta
      多表继承中,子类和父类都是一个功能完整的模型类,所以父类的Meta类会对子类造成影响。所以,Django在多表继承时,子类是不会继承父类的Meta类中大多数的元数据。
      这两个元数据会被继承:orderingget_latest_by,如果子类不想继承父类这两个元数据,可以在子类的Meta类中重写清空:
    class Cat(Animal):
        ordering = []
    
    • 多表继承的反向关联
      上面说过,当一个对象即是父类对象也是子类对象时,可以从父类访问子类。其原因是因为多表继承使用了一个隐含的OneToOneField链接了子类和父类。
      这里会有个隐患,因为这个OneToOneField字段默认的related_name的值与ForeignKeyManyToManyField默认的related_name的值相同。所以,如果一个子类和父类或其它子类是多对一、多对多关系时,Django会在运行或验证时抛出异常。
      解决方法:在每个多对多、多对一字段上,指定related_name的值
    class Cat(Animal):
        skill = models.ManyToManyField(
           Animal,
           related_name = 'related_skill'  # 指定related_name
    )
    
    三、代理模型

    有时候,我们只想更改模型类在python层面的行为,比如说:添加一个新的方法,更改默认的manager管理器。着时候就可以使用代理模型
    使用代理模型时,可以创建、更新、删除代理模型的实例,并且所有的数据都可以和使用原始模型(非代理模型)一样,可以被保存。不同的地方是,在代理模型中进行的操作不会对原始模型产生影响

    • 声明代理模型
      Meta类中,设置proxy = True

    • 例子
      1.Cat类和Animal类使用同一个数据表,并且新的Animal实例可以通过Cat类进行访问,反过来也可以
      2.并且Cat类通过代理进行排序,而父类Animal不进行排序

    from django.db import models
    
    calss Animal(models.Model):
        named = models.CharField(max_length=64)
        age = models.PositiveIntegerField()
    
    class Cat(Animal):
        skill = models.CharField(max_length=32)    
        class Meta:
            ordering = ['age']  # 普通的Animal类是无序的,而Cat类会按照age进行排序
            proxy = True # 代理模型
    
    • 使用代理模型的约束
      1.代理模型必须继承自一个非抽象的基类,并且不能同时继承多个非抽象基类
      2.代理模型可以同时继承任意多个抽象基类,前提是这些抽象基类没有定义任何模型字段。
      3.代理模型可以同时继承多个别的代理模型,前提是这些代理模型继承同一个非抽象基类。(早期Django版本不支持这一条)

    • 代理模型的管理器
      如果代理模型中不指定管理器,那么就会继承父类的管理器。而如果定义了管理器,那么它就会称为默认管理器,但是父类的管理器依旧有效,比如:

    from django.db import models
    
    calss Animal(models.Model):
        named = models.CharField(max_length=64)
        age = models.PositiveIntegerField()
        class Meta:
            abstract = True
    
    class Cat(Animal):
        object  = Animal()
        class Meta:
            proxy = True 
    

    如果想要在代理类中添加新的管理器,而不是替换现有的默认管理器。那么可以创建一个含有新的管理器的基类,然后继承时,把该基类放在主基类的后面:

    ## 创建一个含有新的管理器的基类
    class ExtraManagers(models.Model):
        secondary  = Animal()
        class Meta:
            abstract = True
    
    class Cat(Animal, ExtraManagers):
        class Meta:
            prox = True
    
    四、多重继承

    多重继承并非是多表继承
    Django中模型的多重继承和python的一样。如果模型类的多个父类都含有Meta类,则该子类只会继承第一个父类的Meta类
    不推荐多重继承,因为很容易混乱

    • 多重继承时,如果存在含有相同id主键字段的父类,则会抛出异常(常会出现在使用默认主键的情况)。所以,可以在这些父类中,声明AutoField去显式地去指定主键字段,比如:
    class Oldanimal(models.Model):
        old_id = models.AutoField(primary_key=True)  # 指定主键
    
    class Newanimal(models.Model):
        new_id = models.AutoField(primary_key=True)  # 指定主键
    
    class Cat(Newanimal, Oldanimal):
        pass
    
    • Python中当子类拥有和父类相同的属性名时,子类会覆盖父类的。但是在Django中,如果继承的父类不是一个抽象基类,那么子类不允许拥有和父类相同的字段。在执行python manage.py makemigrations时会报错:
    django.core.exceptions.FieldError: Local field 'name' in class 'yy' clashes with field of the same name from base class 'xx'.
    

    当然,如果父类是一个抽象模型类就没问题了

    五、用包来管理模型类

    当我们生成一个app时,该app的目录下会自动生成一个models.py文件,我们就会在这个文件中创建这个app所需要的各种模型类。
    但是,当模型类很多的时候,管理起来就不大方便,所以这个时候,我们可以把这些模型类分成各个py文件,使用包来管理这些模型类。

    • 步骤:
      1.删除自动生成的models.py文件
      2.在该app目录下创建一个models文件夹
      3.创建一个__init__.py文件
      4.和正常py文件一样,创建模型类的py文件并和之前编写model类一样进行编写,比如animal.pycat.py
      5.在__init__.py文件中导入这些模型类(显示明确地导入每个模型,而不是使用from .model import *的方式,这样命名空间容易混淆,不容易被分析工具所分析使用)
    from .animal import Animal
    from .cat import Cat
    

    相关文章

      网友评论

          本文标题:10、Django_模型类的继承以及用包来管理模型类

          本文链接:https://www.haomeiwen.com/subject/nttqmctx.html