Model声明
在app的model.py文件里 声明 person是model名称
将每一个字段都声明出来 如果字段很多 可以用
1 | $ django-admin startapp 名称 |
1 | $ python manage.py inspectdb persons |
将字段跑出来 直接粘到model文件里 但是需要调整
比如: max_length 或者 jsonb格式的数据
1 | from django.db import models |
字段命名约束:
Django不允许下面两种字段名:
与Python关键字冲突。这会导致语法错误。例如:
class Example(models.Model): pass = models.IntegerField() # ‘pass’是Python保留字!
字段名中不能有两个以上下划线在一起,因为两个下划线是Django的查询语法。
ps: 声明之后要在base.py中PROJECT_APPS 中注册
参考:https://www.liujiangblog.com/course/django/95
Model关联
1对多
多对一的关系,通常被称为外键。外键字段类的定义如下:
1 | class ForeignKey(to, on_delete, **options)[source] |
外键要定义在‘多’的一方!
1 | cv_account = models.ForeignKey(CvAccount, on_delete=models.SET_NULL, null=True) |
如果要关联的对象在另外一个app中,可以显式的指出。下例假设Manufacturer模型存在于production这个app中,则Car模型的定义如下
1 | class Car(models.Model): |
如果要创建一个递归的外键,也就是自己关联自己的的外键,使用下面的方法:
1 | models.ForeignKey('self', on_delete=models.CASCADE) |
on_delete参数
该参数可选的值都内置在django.db.models
中,包括:
- CASCADE:模拟SQL语言中的
ON DELETE CASCADE
约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!) - PROTECT:阻止上面的删除操作,但是弹出
ProtectedError
异常 - SET_NULL:将外键字段设为null,只有当字段设置了
null=True
时,方可使用该值。 - SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用。
- DO_NOTHING:什么也不做。
- SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
db_column参数
设置了外键的字段在下面表字段声明里就不要声明了
1 | cv_account = models.ForeignKey('cv_accounts.CvAccount', on_delete=models.SET_NULL, null=True, db_column='user_id') |
多对多(ManyToManyField)
多对多关系在数据库中也是非常常见的关系类型。比如一本书可以有好几个作者,一个作者也可以写好几本书。多对多的字段可以定义在任何的一方,请尽量定义在符合人们思维习惯的一方,但不要同时都定义。
1 | class ManyToManyField(to, **options)[source] |
多对多关系需要一个位置参数:关联的对象模型。它的用法和外键多对一基本类似。(通过两个一对多的关系 做多对多的关联)
可以通过db_table
选项,自定义表名。
through(定义中间表)
through_fields
接着上面的例子。Membership模型中包含两个关联Person的外键,Django无法确定到底使用哪个作为和Group关联的对象。所以,在这个例子中,必须显式的指定through_fields
参数,用于定义关系。
through_fields
参数接收一个二元元组(‘field1’, ‘field2’),field1是指向定义有多对多关系的模型的外键字段的名称,这里是Membership中的‘group’字段(注意大小写),另外一个则是指向目标模型的外键字段的名称,这里是Membership中的‘person’,而不是‘inviter’。
再通俗的说,就是through_fields
参数指定从中间表模型Membership中选择哪两个字段,作为关系连接字段。
db_table
设置中间表的名称。不指定的话,则使用默认值。
一对一(OneToOneField)
一对一关系类型的定义如下:
1 | class OneToOneField(to, on_delete, parent_link=False, **options)[source] |
用法和前面的多对一外键一样
Meta
每个模型都可以有自己的元数据类,每个元数据类也只对自己所在模型起作用。
abstract
如果abstract=True
,那么模型会被认为是一个抽象模型。抽象模型本身不实际生成数据库表,而是作为其它模型的父类,被继承使用。
db_table
指定在数据库中,当前模型生成的数据表的表名。比如:
1 | db_table = 'my_freinds' |
managed
该元数据默认值为True,表示Django将按照既定的规则,管理数据库表的生命周期。如果设置为False,将不会针对当前模型创建和删除数据库表。
一般需要migration的时候才会设置成true
ordering
最常用的元数据之一了!
用于指定该模型生成的所有对象的排序方式,接收一个字段名组成的元组或列表。默认按升序排列,如果在字段名前加上字符“-”则表示按降序排列,如果使用字符问号“?”表示随机排列。请看下面的例子:
1 | ordering = ['pub_date'] # 表示按'pub_date'字段进行升序排列 |
unique_together
这个元数据是非常重要的一个!它等同于数据库的联合约束!
举个例子,假设有一张用户表,保存有用户的姓名、出生日期、性别和籍贯等等信息。要求是所有的用户唯一不重复,可现在有好几个叫“张伟”的,如何区别它们呢?(不要和我说主键唯一,这里讨论的不是这个问题)
我们可以设置不能有两个用户在同一个地方同一时刻出生并且都叫“张伟”,使用这种联合约束,保证数据库能不能重复添加用户(也不要和我谈小概率问题)。在Django的模型中,如何实现这种约束呢?
使用unique_together
,也就是联合唯一!
比如:
1 | unique_together = (('name', 'birth_day', 'address'),) |
这样,哪怕有两个在同一天出生的张伟,但他们的籍贯不同,也就是两个不同的用户。一旦三者都相同,则会被Django拒绝创建。这一元数据经常被用在admin后台,并且强制应用于数据库层面。
unique_together接收一个二维的元组((xx,xx,xx,…),(),(),()…),每一个元素都是一个元组,表示一组联合唯一约束,可以同时设置多组约束。为了方便,对于只有一组约束的情况下,可以简单地使用一维元素,例如:
1 | unique_together = ('name', 'birth_day', 'address') |
indexes
Django1.11新增的选项。
接收一个应用在当前模型上的索引列表,如下例所示:
1 | from django.db import models |
模型继承
Django有三种继承的方式:
- 抽象基类:被用来继承的模型被称为
Abstract base classes
,将子类共同的数据抽离出来,供子类继承重用,它不会创建实际的数据表; - 多表继承:
Multi-table inheritance
,每一个模型都有自己的数据库表; - 代理模型:如果你只想修改模型的Python层面的行为,并不想改动模型的字段,可以使用代理模型。
注意!同Python的继承一样,Django也是可以同时继承两个以上父类的!
抽象基类:
只需要在模型的Meta类里添加abstract=True
元数据项,就可以将一个模型转换为抽象基类。Django不会为这种类创建实际的数据库表,它们也没有管理器,不能被实例化也无法直接保存,它们就是用来被继承的。抽象基类完全就是用来保存子模型们共有的内容部分,达到重用的目的。当它们被继承时,它们的字段会全部复制到子模型中。看下面的例子:
1 | from django.db import models |
Student模型将拥有name,age,home_group三个字段,并且CommonInfo模型不能当做一个正常的模型使用。
如果子类没有声明自己的Meta类,那么它将继承抽象基类的Meta类
ps:
- 抽象基类中有的元数据,子模型没有的话,直接继承;
- 抽象基类中有的元数据,子模型也有的话,直接覆盖;
- 子模型可以额外添加元数据;
- 抽象基类中的
abstract=True
这个元数据不会被继承。也就是说如果想让一个抽象基类的子模型,同样成为一个抽象基类,那你必须显式的在该子模型的Meta中同样声明一个abstract = True
; - 有一些元数据对抽象基类无效,比如
db_table
,首先是抽象基类本身不会创建数据表,其次它的所有子类也不会按照这个元数据来设置表名。
多表继承
这种继承方式下,父类和子类都是独立自主、功能完整、可正常使用的模型,都有自己的数据库表,内部隐含了一个一对一的关系。
在多表继承的情况下,由于父类和子类都在数据库内有物理存在的表,父类的Meta类会对子类造成不确定的影响,子类除了ordering
和get_latest_by
都不会继承父类的Meta功能。这一点和抽象基类的继承方式有所不同。
如果在多表继承中,你不想让你的子类继承父类的上面两种参数,就必须在子类中显示的指出或重写。如下:
1 | class ChildModel(ParentModel): |
代理模型
只想更改模型在Python层面的行为,比如更改默认的manager管理器,或者添加一个新方法。你可以创建、删除、更新代理模型的实例,并且所有的数据都可以像使用原始模型(非代理类模型)一样被保存。不同之处在于你可以在代理模型中改变默认的排序方式和默认的manager管理器等等,而不会对原始模型产生影响。
声明一个代理模型只需要将Meta中proxy的值设为True。
- 代理模型必须继承自一个非抽象的基类,并且不能同时继承多个非抽象基类;
- 代理模型可以同时继承任意多个抽象基类,前提是这些抽象基类没有定义任何模型字段。
- 代理模型可以同时继承多个别的代理模型,前提是这些代理模型继承同一个非抽象基类。
多重继承
注意,多重继承和多表继承是两码事,两个概念。
Django的模型体系支持多重继承,就像Python一样。如果多个父类都含有Meta类,则只有第一个父类的会被使用,剩下的会忽略掉。
一般情况,能不要多重继承就不要,尽量让继承关系简单和直接,避免不必要的混乱和复杂。