4.2 Model相关的概念和使用方法
2025-02-17
- 4.2 Model相关的概念和使用方法
- Model是Django ORM的核心,有许多特性(继承模型,元数据)
- 需要遵守一些规则:字段类型必须是Field类型
4.2.1 Model的组成部分
通常包含四个部分
- 继承自:
django.db.models.Model - Model元数据声明(Meta内部类)
- 若干个Field类型的字段
__str__方法
django.db.models.Model
通过类之间的继承,Django主要对自定义的Model添加了两个属性
- id:每一个Model必须有且仅有一个Field字段的primary_key属性设置为True,即必须要有主键,通常在定义Model的时候不需要关注主键字段,基类会自动添加一个auto-incrementing id 作为主键
- objects:是Manager(django.db.models.Manager)类的实例,称为查询管理器,是数据库查询的入口
Meta内部类声明元数据
Meta是一个类容器,Django会将容器中的元数据选项定义附加到Model中
常见的元数据定义有:数据表名称,是否是抽象类,权限定义,索引定义等
Meta定义的元数据相当于Model的配置信息,可以直接在shell环境中打印
from post.models imoprt
Topic
Topic.Meta
Topic.Meta.__dict__- Meta内部类可选,用户的Model没有需要完全可以不定义Meta,Django会自动应用默认的元数据到Model上
定义数据表项的Field实例
- Model中的每个字段都是Field(
django.db.models.fields.Field)类型的实例,根据Field的实际类型确认下面的信息- 数据库中的列类型
- 渲染表单时使用的默认HTML部件
- 管理后台与自动生成的表单中的数据验证
__str__方法
- __str__方法是为print这样的打印函数设计的,此方法不定义,打印对象会显示对象的内存地址
4.2.2 Meta元数据类属性说明
- Meta类不数据Model的字段,但可以用来标识一些属性
- 一些重要的元数据和含义以及使用方法
abstract:布尔类型的变量
- True:标识当前的Model是抽象基类,这个元选项不具备传递性,只对当前声明的类有小
proxy:布尔类型的变量
- False:默认值
- True;表示为基类的代理模型
db_table:用于指定数据表的名称
- 默认会使用Django的表名生成规则
- 例子:
db_table='topic' - 对抽象基类是无效的,不应该在抽象基类中去声明它,因为抽象基类可以被多个子类继承,如果数据表名也可以继承,会在数据库创建数据表的时候抛出异常错误
ordering:指定获取对象列表时的排序规则,是一个字符串的列表或元组对象
- 每一个字符串都是Model中定义的字段名,前面加上
-,代表逆序,默认正序 - 例子
ordering = ['created_time']: 正序ordering = ['-created_time']: 逆序ordering = ['created_time','-last_modified']: 先按照created_time正序,然后按last_modified逆序- 建议当所有的查询都需要按照特定的规则排序时,才设置这个元选项,否则,可以在特定的查询中指定排序规则
- 每一个字符串都是Model中定义的字段名,前面加上
managed:布尔类型的变量
- True:默认值。代表Django会管理数据库的生命周期,即可利用Django提供的工具完成创建和删除数据表
- False:Django不会管理这些Model所对应的数据表
当在执行migrate命令之前那已经在数据库中创建了数据表,此时就需要把managed设置为False,让用户自己去管理 需要注意下面的问题
- 当Model中没有声明主键,即使managed设置为False,Django也会自定添加列为id的自增主键
- 如果Model中包含ManyToManyField类型的字段,且指向的Model也是自管理的(managed=False),那Django不会给这种关系创建中间表,需要主动创建中间表,并使用
ManyToManyField.through指定关联关系
indexes:列表类型的元数据
定义Model的索引
列表中的每个元素都是index(django.db.models.indexes.Index)类型的实例
Index类的定义
class Index(field()=[], name=None, db_tablespace=None)- field: 列表对象,指定索引的字段,必填项,至少包含一个字段
- name:索引的名称
- db_tablespace:表空间,用于优化数据库性能(用于PostgreSQL,Oracle)
例子
from django.db import models indexs = { models.Index(fields=['title']), models.Index(fields=['title','is_online'], name='title_is_online_idx') }
unique_together:常见的元组类型元选项
- 包含多个元组对象,标识联合唯一约束,在数据库表现为联合唯一索引
- 优点:将数据取值的限制抽离出业务逻辑,放在框架和数据库层面处理
- 例子
unique_together = (('title','is_online'),('title','content'))unique_together = ('title','is_online')
ManyToManyField类型的字段不能包含在
unique_together中verbose_name 和 verbose_name_plural
- 两个元选项用于给Model类起一个方便阅读的名称,主要用在管理后台上的展示
default_permissions
- 默认会给每一个定义的Model设置三个权限:add,change,delete
permissions
此元选项给Model添加额外的权限。permissions是一个包含二元组的元组或者列表
- 格式:
(权限代码,权限名称) - 例子:
permissions = ( ("can_read_topic", "可以阅读话题"), ("can_discuss_topic", "可以评论话题") )- 格式:
4.2.3 Field的通用字段选项
- blank:数据验证相关字段,体现在管理后台录入数据的校验规则
- 默认值:False,不允许输入空值
- True:允许输入空值
- unique:数据库级别的选项,表明必须是唯一的;对ManyToManyField和OneToOneField关系类型不起作用
- 默认值:False
- 设置为True,就不需要对这个字段加上索引选项了
- null:影响表字段属性,规定字段的数据是否可以是空值
- 默认值:False
- True:会在数据库中将空值存储为NULL
- 对于CharField和TextField的字符串类型,null字段应用总是设置为False,
- 如果设置为True,对于“空数据”会有两个概念:空字符串和NULL
- 例外:当CharField同时设置了unique=True和blank=True,则需要设置null=True,防止保存多个空白值时违反唯一性约束
- db_index:索引属性
- 默认值:False
- True:会为字段创建数据库索引
- db_column:设置数据库表字段的名称
- default:给字段设置默认值
- lambda表达式不可以作为default的参数值,因为不能被migrations命令序列化
- 对于ForeignKey的字段,默认值设置的应该是主键而不应该是Model对象实例
- primary_key:主键属性
- 默认值:False
- True:成为Model的主键字段,且不允许其他的字段再次将该选项设置为True
- 特性:
- 在数据库层面:primary_key=True 意味着对应的字段唯一且不能是NULL
- 主键字段是只读的
- choices:
给字段设置可以选择的值,是一个可迭代的对象(列表或者元组),每一个元素都是一个二元组(A,B),A是选择的对象(作为字段值使用),B是A的描述信息
choices字段在管理后台的显示上会有文本框变成选择框,框中的可选值就是choices中的元组
Django建议将choices定义在Model的内部,是代码更加规整
例如
from django.db import models class People(models.Model): MALE = 'm' FEMALE = 'f' GENDER_CHOICES = ( (MALE,"男性"), (FEMALE,"女性") )但在数据库中还是会遵循Field类型对应的数据表字段类型
- help_text:显示字段的提示信息
- verbose_name:给字段设置可读性更高的名称
4.2.4 基础字段类型
Django内置的字段类型,按照与其他的Model存在关联分为两类
- 基础字段类型:字符串,数字,时间,二进制等
- 关系字段类型:只有三个
django.db.models.Field
对于Field,只需要关注三个方面:
- 映射到数据表的列类型(db_type)
- 将Python对象映射到数据表(get_prep_value)
- 从数据库返回python对象(from_db_value)
db_type(connection)
- 根据所配置的数据库后端返回Field对应的数据库列类型
- 对这个方法重载,需要考虑不同的数据库后端列类型兼容的问题
- 此方法只会在Django为Model生成创建表语句的时候调用一次,其他任何时候都不会使用。
- 此方法实现较为复杂,但不会影响系统性能
get_prep_value(value)
- 参数value是Model属性的当前值
- 该方法对value操作,最终返回用作数据库查询的参数
- Model对象保存到数据库中的时候,也会将该方法的返回值作为该列数据保存
from_db_value(value,expression,connection)
- 与get_prep_value的作用相反,将从数据库中返回的数据转换为Python对象
常用的基础字段类型
- IntegerField
- SmallIntegerField: 小整数
- BigIntegerField: 64位整数
- PositiveIntegerField: 只允许存储大于等于0的整数
- AutoField:根据ID自增的IntegerField
- BigAutoField:使用的是8个字节的存储空间
- CharField:
- 有必填参数:max_length,且取值只能是大于0的整数
- TextField
- BooleanField:布尔类型
- 某个字段的取值只能是True或False的情况
- DateField和DateTimeField
- DateField是日期,以Python的datetime.date实例表示
- DateTime是日期时间,以Python的达特time.datetime实例表示
- 都有两个特殊的参数选项可以设置
- auto_now:此选项应用在对象保存的时候,会自动设置为当前时间
- auto_now_add:此选项应用在首次创建对象的时候,会自动将字段设置为当前时间
- auto_now,auto_now_add,default是互斥的,不能组合在一起使用
- EmailField
- 存储字符串类型的数据,是专门用来存储邮箱地址
- 内部有使用EmailValidator验证器对输入的字符串进行校验
自定义一个字段类型
- GenericIPAddressField:存储IP地址
- URLField:存储URL
- FloatField:存储浮点数的字段类型
4.2.5 三种关系字段类型
- 多对一(ForeignKey)
- 一对一(OneToOneField)
- 多对多(ManyToManyField)
多对一关系类型
- 在数据库中体现为:外键关联关系
- 可以和其他Model建立关联,也可和自己建立关联,描述多对一的关系
- ForeignKey的定义方法:
class django.db.models.ForeignKey(to, on_delete, **options) - 两个必填的参数
- to:指定所关联的Model,取值可以是直接引用其他的Model,也可以是Model对应的字符串名称,要创建递归的关联关系,即Model自身存在多对一的关系,可以设置为字符串self
- on_delete:删除关联表的数据时,此参数决定Django会执行什么样的SQL约束,可选值
- CASCADE:级联删除,大部分应该选择的约束
- PROTECT:删除被引用对象时,会抛出ProtectedError异常
- SET_NULL:设置删除对象所关联的外键字段为null,前提是设置了选项null为True
- SET_DEFAULT:将外键字段设置为默认值,但前提是设置了default选项,且指向的对象是存在的
- SET(value):删除被引用对象时,设置外键字段为value。value如果是一个可调用对象,那么就会被设置为调用后的结果
- DO_NOTHING:不做任何处理。但是,由于数据表之间存在引用关系,删除关联数据,会造成数据库抛出异常
- 可选参数
- to_field:关联对象的字段名称。默认情况下,Django使用关联对象的主键(大部分情况下是id),如果需要修改成其他字段,可以设置这个参数。但是,需要注意,能够关联的字段必须有unique=True的约束
- db_constraint:默认值是True,它会在数据库中创建外键约束,维护数据完整性
- related_name:这个字段设置的值用于反向查询,默认不需要设置,Django会设置其为“小写模型名_set”。如果不想创建反向关联关系,可以将它设置为“+”或者以“+”结尾
- related_query_name:这个名称用于反向过滤。如果设置了related_name,那么将用它作为默认值,否则Django会把模型的名称作为默认值
一对一关系类型
类似于unique=True的ForeignKey
与ForeignKey的差别在于:反向查询上
- ForeignKey反向查询返回的是一个对象实例列表
- OneToOneField:返回的是一个对象实例
OneToOneField的定义:
class models.OneToOneField(to, on_delete, parent_link=False, **options)parent_link参数:
- True:表示在继承另一个非抽象的Model中使用时,该字段会变成指向父类实例的应用
例子:关联对象的字段名称。默认情况下,Django使用关联对象的主键(大部分情况下是id),如果需要修改成其他字段,可以设置这个参数。但是,需要注意,能够关联的字段必须有unique=True的约束
from django.db import models from django.contrib.auth.models import User class CusTomUser(models.Model): user = models.OneToOneField(to=User, on_delete=models.CASCADE) sign = models.CharField(max_length=255, help_text=u'用户签名')
多对多关系类型
此关系类型Django会通过中间表来进行维护
定义格式:
class django.db.models.ManyToManyField(to, **option)重要的可选参数:
- related_name:用于反向查询
- db_table:用于指定中间表的名称
- through:用于指定中间表
例子:针对Author和Book之间的多对多关系
from django.db import models class Book(models.Model): title = models.CharField(max_length=64, help_text=u'书名') class Author(models.Model): name = models.CharField(max_length=32, help_text=u'作者') books = models.ManyToManyField(to=Book)
4.2.6 Model的继承模型
- 三种继承模型
- 抽象基类
- 多表继承
- 代理模型
抽象基类
抽象Model专门设计为被其他的子类继承,它将子Model中通用的元素聚合到一起,以便子Model不用多次重复定义这些通用的部分,且对于修改也只需要操作基类
- 定义Meta内部类,并将
abstract设置为True,就可把当前的Model定义为抽象基类了 - 抽象基类,不能被实例化,所以Django不会为它创建数据表和查询管理器
Model的元数据继承关系的几个原则
- 抽象基类中定义的元数据,子类中没有定义,子类会继承基类中的元数据。
- 抽象基类中定义的元数据,子类也定义了,子类优先级更高。
- 子类可以定义自己的元数据,即不出现在抽象基类中的元数据。
多表继承
继承方式的效果是父Model和子Model都会有数据库表,且Django会自动给子Model添加一个OneToOneField类型的字段指向父Model,而这个字段会成为子Model数据表的主键
例子
from django.db import models class CategortTopic(Topic): """ 分类 Topic """ category = models.CharField(max_length=32, help_text='类别')与抽象基类的显著不同点是Meta的继承
- 子类不会继承父类的Meta定义
- 但是有两个Meta元选项会被子类继承:
ordering,get_latest_by
代理模型
使用场景:需要给原始的Model添加一些方法或者修改它的Meta选项,但是不需要修改原始Model的字段定义
创建方法:将Meta中的proxy选项设置为
True例如:需要给Topic添加title的校验方法且查询记录按照id的顺序返回
class ProxyTopic(Topic): """ 代理 Topic """ class Meta: ordering = ['id'] proxy = True def id_topic_valid(self): return 'django' in self.title代理模型只能继承自一个非抽象的基类,并且不能同时继承多个非抽象基类