Django模型

目录

数据库迁移命令

ORM模块

Model相关的概念与使用方法

Meta元数据类属性说明

Field的通用字段选项

基础字段类型

django.db.models.Field

常用的基础字段类型

三种关系字段类型

多对一关系类型(ForeignKey)

一对一关系类型(OneToOneField)

多对多关系类型(ManyToManyField)

Model的继承模型

抽象基类

多表继承

代理模型


数据库迁移命令

当模型类写好之后我们通常要进行数据库的迁移才能在数据库中创建表。

输入以下命令表示生成迁移文件。

python manage.py makemigrations 应用名

同步到数据库中:


python manage.py migrate

ORM模块

Django的ORM模块是框架特色功能之一,它把数据表与Python类对应、表字段与类属性对应、类实例与数据记录对应,并将对类实例的操作映射到数据库中。开发者不再需要写SQL代码,可以更加专注地完成业务逻辑,极大地提高了开发效率。

Django中的模型则就是使用到了ORM模块。

Model相关的概念与使用方法

Model是Django ORM的核心,它有许多特性(如继承模型、元数据),同时也要遵守一些规则,每个Model都是一个Python类,且通常会包含四个部分:继承自django.db.models.Model、Model元数据声明(Meta内部类)、若干个Field类型的字段以及__str__方法。

  • django.db.models.Model
    • 通过类之间的继承,Django主要对自定义的Model添加了两个属性。
      • id
        • Django规定,每一个Model必须有且仅有一个Field字段的primary_key属性设置为True,即必须要有主键。通常,在自定义Model的时候,不需要关注主键字段,基类会自动添加一个auto-incrementing id作为主键。
      • objects
        • 它是Manager(django.db.models.Manager)类的实例,被称为查询管理器,是数据库查询的入口。每个Django Model都至少有一个Manager实例,可以通过自定义创建Manager以实现对数据库的定制访问,但通常是没有必要的,后面的内容会看到默认Manager的强大功能。
  • Meta内部类声明元数据
    • Meta是一个类容器,Django会将容器中的元数据选项定义附加到Model中。常见的元数据定义有:数据表名称、是否是抽象类、权限定义、索引定义等。Meta定义的元数据相当于Model的配置信息,可以直接在shell环境中打印出来,Meta内部类是可选的,用户的Model如果没有需要完全可以不定义Meta,Django会自动应用默认的元数据到Model上。
  • 定义数据表项的Field实例
    • Model中的每个字段都是Field(django.db.models.fields.Field)类型的实例,Django会根据Field的实际类型确定以下几个信息。
      • 数据库中的列类型
      • 渲染表单时使用的默认HTML部件。
      • 管理后台与自动生成的表单中的数据验证。
    • 每个Field类型都定义了一些参数,这些参数大都会直接反映到数据表的Schema中,例如:unique设置为True反映这一列数据在表中是唯一的;primary_key设置为True会将这一列设置为表的主键等。需要注意的是,存在部分参数是通用的,即可以用在任何一种Field类型中。
  • __str__方法
    • __str__方法是Python中的“魔术”方法,它是为print这样的打印函数设计的。如果没有这个方法定义,打印对象会显示对象的内存地址,但是,这样的显示方式不够友好,且不利于调试。不仅如此,将来管理后台也可以看到__str__方法发挥的作用,它会将函数的返回值作为对象的显示值。

Meta元数据类属性说明

Meta类用于定义Model的元数据,即不属于Model的字段,但是可以用来标识它的一些属性。在BaseModel的定义中已经介绍了内部类Meta的使用方法,下面列举一些重要的元选项说明它们的含义以及使用方法。

  • abstract
    • 一个布尔类型的变量,如果设置为True,则标识当前的Model是抽象基类,这个元选项不具有传递性,只对当前声明的类有效。
  • proxy
    • 默认值是False,如果设置为True,则表示为基类的代理模型。
  • db_table
    • 这个字段用于指定数据表的名称。通常,如果没有特别的需要,默认会使用Django的表名生成规则
    • db_table元选项对抽象基类是无效的,也不应该在抽象基类中去声明它。因为抽象基类可以被多个子类继承,如果数据表名也可以继承,那么,在数据库创建表的时候就会抛出错误。
  • ordering
    • 用于指定获取对象列表时的排序规则,它是一个字符串的列表或元组对象,其中的每一个字符串都是Model中定义的字段名,字符串前面可以加上“-”代表逆序,默认按照正序排序。
    • 排序对于数据库查询是有代价的,所以,只有当所有的查询都需要按照特定的规则排序时才需要设定这个元选项,否则,可以在特定的查询中指定排序规则,不要做统一的定义。
  • managed
    • 它是一个布尔类型的变量,默认为True,代表Django会管理数据表的生命周期,即利用Django提供的工具可以完成创建和删除数据表。如果设置为False,唯一的不同之处就是Django不会管理这些Model所对应的数据表。这个元选项通常不需要考虑,除非用户在执行migrate命令之前已经在数据库中创建了数据表。此时就需要把它设置为False,由用户自己去做管理,但这样的声明需要注意以下问题。
      • 如果Model中没有声明主键,那么即使将managed设置为False的情况下,Django仍然会自动添加一个名称为id的自增主键。由于已经设置为自己去管理数据表了,所以,最好是手动指定数据表中的所有数据列,避免造成混乱。
      • 如果Model中包含ManyToManyField类型的字段,且指向的Model也是自管理的(managed=False),那么,Django不会给这种关系创建中间表,需要主动创建中间表,并使用ManyToManyField.through指定关联关系。
  • indexes
    • 它是一个列表类型的元选项,用来定义Model的索引,列表中的每一个元素都是Index(django.db.models.indexes.Index)类型的实例。
    • Index的类定义如下:
      • models.Index(fields=[],name=None,db_tablespace=None)
        • fields:一个列表对象,用于指定索引的字段,是必填项,且至少包含一个字段。
        • name:用于标识索引的名称,是可选的,如果不提供,Django会根据不同的数据库生成合适的索引名。
        • db_tablespace:表空间,也是可选的,常见于PostgreSQL、Oracle数据库,用于优化数据库性能。MySQL数据库是不支持表空间的,Django也不会主动创建表空间,如果设置了这个字段,但是存储后端并不支持,则Django会自动忽略这个选项。
  • unique_together
    • 这是一个很常见的元组类型元选项,包含多个元组对象,标识联合唯一约束,在数据库层面表现为联合唯一索引。它的优点是将对数据取值的限制抽离出业务逻辑,放在了框架和数据库层面去处理,使整体的逻辑显得更为清晰。
  • verbose_name和verbose_name_plural
    • 这两个元选项用于给Model类起一个方便阅读的名称,主要用在管理后台上的展示,其中verbose_name_plural是模型类的复数名。如果不设置的话,Django会使用小写的模型名作为默认值,且会将驼峰式的名称拆分为独立的单词,复数会参照verbose_name加上字母s。
  • default_permissions
    • Django默认会给每一个定义的Model设置三个权限(权限是用户的一种属性,用户拥有这种属性,就具备了一些能力。('add','change','delete')。如果不需要这些默认的权限,或者只需要其中的一部分,可以根据自己的需要重新定义这个元选项。
  • permissions
    • 除了Django默认给Model添加的三个权限之外,还可以通过这个元选项给Model添加额外的权限。permissions是一个包含二元组的元组或者列表,元素的格式为:(权限代码,权限名称)。

Field的通用字段选项

Model中添加的字段都是Field类型的实例,不同的Field类型可能会支持不同的字段选项,但是,还是有很多字段选项是通用的,即可以用在任何一种Field类型中。这里介绍一些常用且重要的通用字段选项,它们都有对应的默认值,这些字段选项都是可选的,

  • blank
    • 默认值是False,它是数据验证相关的字段,主要体现在管理后台录入数据的校验规则。对于任何一个属性,默认是不允许输入空值的,如果允许这种情况发生,需要设置blank=True。
  • unique
    • 默认值是False,它是一个数据库级别的选项,规定该字段在表中必须是唯一的,但是对ManyToManyField和OneToOneField关系类型是不起作用的。
    • 需要注意的是,数据库层面对待唯一性约束会创建唯一性索引,所以,如果一个字段设置了unique=True,就不需要对这个字段加上索引选项了。
  • null
    • 默认值是False,它是一个针对数据库的选项,影响表字段属性,规定这个字段的数据是否可以是空值。如果将其设置为True,则Django会在数据库中将空值存储为NULL。
    • 对于CharField和TextField这样的字符串类型,null字段应该总是设置为False,如果设置为True,对于“空数据”就会有两种概念:空字符串和NULL。有一个例外,是当CharField同时设置了unique=True和blank=True,那也需要设置null=True,这是为了防止在保存多个空白值时违反唯一性约束。
  • db_index
    • 默认值是False,如果设置为True,Django则会为该字段创建数据库索引。如果该字段经常作为查询的条件,那么就需要考虑设置db_index选项,以加快数据的检索速度。
  • db_column
    • 这个选项用于设置数据库表字段的名称。如果没有指定这个选项,Django会直接使用Model中字段的名称。
    • 需要注意的是,如果db_column设置的是数据库的保留字(如MySQL中的all)或者是包含了Python不能接受的变量名(如a-b),那么,Django会给数据列的名字加上引号。建议用户在设置列名之前,最好先查看自己所使用的数据库后端,不要与保留字发生冲突。
  • default
    • 用于给字段设置默认值。该选项可以设置为一个值或者是可调用对象,但是不能是可变对象,例如Model实例、列表、集合对象等。
    • 在使用default的时候,需要注意以下几点。
      • lambda表达式不可以作为default的参数值,因为它不能被migrations命令序列化。
      • 对于ForeignKey这样的字段,默认值设置的应该是主键而不应该是Model对象实例。
  • primary_ke
    • 默认值是False,如果某个字段设置该选项为True,则它会成为Model的主键字段,且不允许其他的字段再次将该选项设置为True。Model定义中如果没有设置该选项,那么Django会自动添加一个名称为id的AutoField类型的字段。通常情况下,Django的这种默认行为能够满足大部分的场景。
    • 在数据库层面,primary_key=True就意味着对应的字段唯一且不能是NULL。
    • 主键字段是只读的,所以,如果用户修改了主键字段的值,并执行了保存动作,结果是创建了一条新的数据记录。
  • choices
    • 这个选项用于给字段设置可以选择的值。它是一个可迭代的对象,即列表或者元组,其中每一个元素都是一个二元组:(A,B)。A是用来选择的对象,即作为字段值使用;B是对A的描述信息。
    • 设置了choices的字段在管理后台的显示上会由文本框变成选择框,选择框中的可选值就是choices中的元组。
    • 由于存储到数据表里的实际数据是用来选择的对象A,所以将这个字段的值输出后也是A,如果需要输出描述信息B的话,则需要使用到Model.get_FOO_display()方法了。对于每一个设置了的字段,该对象将有一个 方法,其中 是字段的名称。该方法返回字段的"人类可读"值。FOO表示的是字段名,使用时根据不同的字段进行修改即可输出对该字段的描写信息。
  • help_text
    • 这个选项用于在表单中显示字段的提示信息。例如在管理后台的编辑页面,对应在字段输入框的下方会显示该选项设定的值。由于表单通常提供给非技术人员,完善的提示信息将更加方便校验和录入字段数据,所以,对字段添加解释信息是很有必要的。
  • verbose_name
    • 这个选项用于给字段设置可读性更高的名称,通常也是用在表单的展示上。如果没有设置这个字段,Django将会直接展示字段名,且首字母大写。如果字段中存在下画线,Django会将它转换为空格。
    • 这个选项要与help_text区分开来,verbose_name可以认为是字段的别名,而help_text可以认为是对字段的描述。

基础字段类型

Django内置的字段类型,按照是否与其他的Model存在关联可以划分为两类:基础字段类型和关系字段类型。关系字段类型只有三个,基础字段类型较多,又可以细分为:字符串、数字、时间和二进制。

django.db.models.Field

Field是所有字段类型的基类,不管是Django内置的字段类型还是自定义的字段类型都需要继承自它。通常对于Field,只需要关注三个方面:映射到数据表的列类型(db_type)、将Python对象映射到数据库(get_prep_value)、从数据库返回Python对象(from_db_value)。

  • db_type(connection)
    • 它会根据所配置的数据库后端返回Field对应的数据库列类型。所以,如果对这个方法重载,那么应该需要考虑不同的数据库后端列类型兼容的问题。db_type方法只会在Django为Model生成创建表语句的时候调用一次,其他任何时候都不会被使用。所以,即使这个方法的实现较为复杂,也不会影响系统性能。
  • get_prep_value(value)
    • 参数value是Model属性的当前值,通过该方法对value的操作,最终返回用作数据库查询的参数。注意,Model对象保存到数据库中的时候,也会将该方法的返回值作为该列数据保存。
  • from_db_value(value,expression,connection)。
    • 与get_prep_value的作用正好相反,它会将从数据库中返回的数据转换为Python对象。因为数据库后端能够返回正确的Python类型,所以这个方法在大多数内置的字段中都没有用到。

常用的基础字段类型

  • IntegerField
    • 整型字段,取值为-2147483648~2147483647
  • SmallIntegerField
    • 小整数
  • BigIntegerField
    • 64位整数
  • PositiveIntegerField
    • 只允许存储大于等于0的整数
  • AutoField
    • 一个根据ID自增的IntegerField。如果Model中没有定义主键,那么,Django会自动添加一个名称为id的该类型字段作为主键。如果觉得AutoField的取值范围不够用,可以考虑使用BigAutoField,它继承自AutoField,但是它使用的是8个字节的存储空间。
  • CharField
    • 字符字段,是最常用的字段类型。它有一个必填的参数max_length,且取值只能是大于0的整数,
  • TextField
    • 与CharField类似,也是用于存储字符类型的字段,但是它用于存储大文本。
  • BooleanField
    • 布尔类型,在某个字段的取值只能是True或False的情况下选择使用该字段类型,
  • DateField和DateTimeField
    • 这两个字段类型是用来标识时间的,几乎在任何一个Model定义中都能看到会至少引用它们其中的一个。唯一的区别就是DateField表示的是年月日,而DateTimeField比它多一个时分秒。
    • 它们都有两个特殊的参数选项可以设置:
      • auto_now
        • 这个选项应用在对象保存的时候,会自动设置为当前时间。
      • auto_now_add
        • 当首次创建对象的时候,会自动将字段设置为当前时间。
      • 注意,auto_now和auto_now_add与default是互斥的,不应该将它们组合在一起使用。
  • EmailField
    • 它继承自CharField,也用来存储字符串类型的数据。但是,就像它的名字一样,它是专门用来存储电子邮件地址的。其内部使用EmailValidator验证器对输入的字符串进行校验。

三种关系字段类型

Django定义了三种关系类型用来描述数据库表的关联关系:多对一(ForeignKey)、一对一(OneToOneField)以及多对多(ManyToManyField)。

多对一关系类型(ForeignKey)

这种类型在数据库中的体现是外键关联关系,它可以和其他的Model建立关联,同时可以和自己建立关联,用来描述多对一的关系。

它有两个必填的参数。

  • to:指定所关联的Model,它的取值可以是直接引用其他的Model,也可以是Model对应的字符串名称。如果要创建递归的关联关系,即Model自身存在多对一的关系,可以设置为字符串self。
  • on_delete:当删除关联表的数据时,Django将根据这个参数设定的值确定应该执行什么样的SQL约束。在django.db.models中定义了on_delete的可选值如下。
    • CASCADE:级联删除,它是大部分ForeignKey的定义应该选择的约束。它的表现是删除了“一”,则“多”会被自动删除。
    • PROTECT:删除被引用对象时,将会抛出ProtectedError异常。
    • SET_NULL:设置删除对象所关联的外键字段为null,但前提是设置了选项null为True,否则会抛出异常。
    • SET_DEFAULT:将外键字段设置为默认值,但前提是设置了default选项,且指向的对象是存在的。
    • SET(value):删除被引用对象时,设置外键字段为value。value如果是一个可调用对象,那么就会被设置为调用后的结果。
    • DO_NOTHING:不做任何处理。但是,由于数据表之间存在引用关系,删除关联数据,会造成数据库抛出异常。

除了必填的参数之外,ForeignKey还有一些常用的可选参数需要关注。

  • to_field:关联对象的字段名称。默认情况下,Django使用关联对象的主键(大部分情况下是id),如果需要修改成其他字段,可以设置这个参数。但是,需要注意,能够关联的字段必须有unique=True的约束。
  • db_constraint:默认值是True,它会在数据库中创建外键约束,维护数据完整性。通常情况下,这符合大部分场景的需求。但是,如果数据库中存在一些历史遗留的无效数据,则可以将其设置为False,这时就需要自己去维护关联关系的正确性了。
  • related_name:这个字段设置的值用于反向查询,默认不需要设置,Django会设置其为“小写模型名_set”。如果不想创建反向关联关系,可以将它设置为“+”或者以“+”结尾。
  • related_query_name:这个名称用于反向过滤。如果设置了related_name,那么将用它作为默认值,否则Django会把模型的名称作为默认值。

一对一关系类型(OneToOneField)

OneToOneField继承自ForeignKey,在概念上,它类似unique=True的ForeignKey。它与ForeignKey最显著的差别体现在反向查询上:ForeignKey反向查询返回的是一个对象实例列表,而OneToOneField反向查询返回的是一个对象实例。

它与ForeignKey的参数几乎是一样的,只是多了一个可选参数

parent_link:当其设置为True并在继承自另一个非抽象的Model中使用时,那么该字段就会变成指向父类实例的应用,而不是用于扩展父类并继承父类的属性。

多对多关系类型(ManyToManyField)

Django通过中间表来维护Model与Model之间的多对多关系。这个中间表可以是自己提供的,也可以直接使用Django默认生成的。

它有一个必填的参数to,与ForeignKey和OneToOneField在概念上是一致的,都是用来指定与当前的Model关联的Model。

ManyToManyField还有一些重要的可选参数:

  • related_name:与ForeignKey中的related_name作用相同,都是用于反向查询。
  • db_table:用于指定中间表的名称。如果没有提供,Django会使用多对多字段的名称和包含这张表的Model的名称组合起来构成中间表的名称(仍然会有App的前缀)。
  • through:用于指定中间表。这个参数通常不需要设置,因为Django会默认生成隐式的throughModel。由于Django有自己的中间表生成策略,因此如果用户想自己去控制表之间的关联关系或者增加一些额外的信息,就可能会使用这个参数

Model的继承模型

Django Model的继承与Python类的继承是一样的,只是Django要求所有自定义的Model都必须继承自django.db.models.Model。在Django中Model之间有三种继承模型,它们分别是抽象基类、多表继承和代理模型

抽象基类

抽象Model专门设计为被其他的子类继承,它将子Model中通用的元素聚合到一起,以便子Model不用多次重复定义这些通用的部分,且对于修改也只需要操作基类,节省了很多工作量。

关于Model的元数据继承关系,遵循以下几个规则。

  • 抽象基类中定义的元数据,子类中没有定义,子类会继承基类中的元数据。
  • 抽象基类中定义的元数据,子类也定义了,子类优先级更高。
  • 子类可以定义自己的元数据,即不出现在抽象基类中的元数据。

在定义抽象基类时,需要注意,如果定义了ForeignKey或ManyToManyField类型的字段,并且设置了related_name或者related_query_name参数,由于继承关系,子类也会拥有同样的字段,所以,就需要想办法让子类中的反向名称和查询名称是唯一的。

Django提供了特殊的语法解决这个问题。

'%(class)s':会替换为子类的小写名称。

'%(app_label)s':会替换为子类所属App的小写名称。

由于Django中每个App的名称都应该是不同的,且每个App中所有Model的名称都应该是不同的,所以,组合在一起的名称就一定是唯一的。

多表继承

这种继承方式的效果是父Model和子Model都会有数据库表,且Django会自动给子Model添加一个OneToOneField类型的字段指向父Model,而这个字段会成为子Model数据表的主键。

多表继承与抽象基类有一个显著的不同点是Meta的继承:子类不会继承父类的Meta定义。但是,有两个Meta元选项比较特殊:ordering和get_latest_by,它们是会被子类继承的,所以,如果不想让它们影响子类的行为,应该覆盖这两个元选项。

代理模型

代理模型的使用场景是需要给原始的Model添加一些方法或者修改它的Meta选项,但是不需要修改原始Model的字段定义。为原始的Model创建一个代理,那么对代理模型的增删改的操作都会被保存到原始的Model中。

创建代理模型比较简单,只需要将Meta中的proxy选项设置为True即可。

需要注意,代理模型只能继承自一个非抽象的基类,并且不能同时继承多个非抽象基类。

你可能感兴趣的:(Django,django,python,后端)