美文网首页
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_模型类的继承以及用包来管理模型类

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

  • django数据库操作

    定义模型类模型类被定义在"应用/models.py"文件中。模型类必须继承自Model类,位于包django.db...

  • C++笔记五(面向对象编程下)

    十六 对象模型:关于vptr和vtbl 如图模型所示,B类继承A类,C类继承B类,子类有父类的成分,继承的包括数据...

  • 模型基类常用方法

    模型基类 模型继承基类 最后控制器调用

  • UML建模体系

    模型类型 模型图种类 建模机制 用例模型 用例模型图 静态建模 静态模型 类图、对象图、包图 静态建模 行为模型 ...

  • django 基础

    models.py文件,定义模型类 模型类继承自models.Model类 生成数据表 激活模型:编辑settin...

  • ThinkPHP笔记-模型

    模型定义 ThinkPHP中的模型类主要用于操作数据表。 模型类通常需要继承系统的\Think\Model类或其子...

  • 数据库获取数据第三种方法

    (3)使用模型以及关联模型操作数据库 模型:1.都需要继承think\model这个类 2.一个模型并不总是对应一...

  • Django模型层

    Django模型 一、基础 每个模型都是一个 Python 的类,这些类继承自 django.db.models....

  • block

    Block类是nn模块里提供的一个模型构造类 Sequential类继承自Block类。当模型的前向计算为简单串联...

网友评论

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

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