Django模型中最重要并且也是唯一必须执行的就是字段定义。字段在类中进行定义,对应于实体数据库的字段。另外,定义模型字段名时为了避免冲突,不建议使用模型API中已经定义的关键字。
字段类型用以指定数据库的数据类型,例如Integer、VARCHAR和TEXT这几种比较常用的数据类型。在Django模型定义中,字段类型均派生自Field类的实例。Django框架中的Field类是一个抽象类,专门用于定义数据库表的列表项。
Django模型一共内置了多种字段类型,基本能够满足一般的设计需求。Django模型的主要字段类型说明如下:
· AutoField:一个自动增加的Integer类型。一般情况下,AutoField类型是不需要直接使用的,域的主键会自动被添加到模型中。
· BigAutoField:类似AutoField类型,一个自动增加的长Integer(64-bit)类型(1~9223372036854775807)。
· IntegerField:一个Integer类型(−2147483648~2147483647)。
· BigIntegerField:一个长Integer类型(−9223372036854775808~9223372036854775807)。
· SmallIntegerField:一个Small Integer类型(−32768~32767)。
· BinaryField:一个用来存储二进制数据的类型。
· BooleanField:一个用来存储布尔值(True / False)的类型。
· NullBooleanField:类似BooleanField(null=True)类型。
· FloatField:一个用来存储浮点型数据的类型,表示Python语言中的float实例。
· CharField:一个用来存储字符串的类型。CharField类型必须额外定义一个表示最大长度的参数——CharField.max_length。
· DateField:一个用来存储日期的类型,表示Python语言中的datetime.date实例。CharField类型可以额外定义两个可选参数——DateField.auto_now和DateField.auto_now_add。其中,DateField.auto_now参数用于自动获取当前时间,DateField.auto_now_add参数用于在对象第一次创建时自动获取当前时间。
· DateTimeField:一个用来存储日期和时间的类型,表示Python语言中的datetime.datetime实例。
· TimeField:一个用来存储时间的类型,表示Python语言中的datetime.time实例。
· DecimalField:一个用来存储十进制数据的类型,表示Python语言中的Decimal实例。DecimalField类型需要定义两个必选参数——DecimalField.max_digits和DecimalField.decimal_places。其中,DecimalField.max_digits参数表示最大数值,DecimalField.decimal_places参数表示存储位置。
· DurationField:一个用来存储时间间隔的类型,表示Python语言中的timedelta。
· EmailField:一个CharField类型的域,用于表示电子邮件类型。
· FileField:一个用于文件上传的类型。FileField类型需要定义两个必选参数——FileField.upload_to和FileField.storage。其中,FileField.upload_to参数表示存储路径,FileField.storage参数表示存储对象。
· TextField:一个用于存储文本的类型,在表单域中默认使用TextArea小部件(Widget)。
· ImageField:一个用来存储Image文件的类型,继承自FileField类型。ImageField类型需要定义两个必选参数——ImageField.height_field和ImageField.width_field。其中,ImageField.height_field参数表示Image文件的高度,ImageField.width_field参数表示Image文件的宽度。
· GenericIPAddressField:一个用来存储原生IP(IPv4或IPv6)地址的类型,在表单域中默认使用TextInput小部件(Widget)。
· URLField:一个用来存储URL的类型,继承自CharField类型,在表单域中默认使用TextInput小部件(Widget)。
每一种字段类型都需要指定一些特定的参数。例如,CharField(及其子类)需要接收一个max_length参数,用以指定数据库存储VARCHAR数据时的字节数。
一些可选的参数是通用的,可以用于任何字段类型,下面具体介绍一些会经常用到的通用参数。
1.null(类型:Field.null)
null的默认值为False,如果设置为True,则当该字段为空时,Django模型会将数据库中的该字段设置为NULL。
注意 避免在基于字符串的字段(如CharField和TextField)上使用null类型,Django模型在使用惯例上是使用空字符串而不是NULL。
2.blank(类型:Field.blank)
blank的默认值为False,如果设置为True,则该字段允许为空。
blank类型与null类型是有区别的。blank类型主要用于表单验证,如果某个表单域设为“blank=True”,则验证时会允许该域为空值;如果某个表单域设为“blank=False”,则验证时该域是必须填写的。
3.default(类型:Field.default)
表示字段的默认值。该字段值可以是一个值或者是一个可调用的对象,如果是一个可调用的对象,则每次实例化模型时都会调用该对象。
default类型的代码示例如下:
【代码3-4】
【代码分析】
在第01、02行代码中定义了一个contact_default()方法,返回了一个email对象。
在第04~06行代码中定义了一个JSON域变量contact_info,其default值引用了contact_default()方法返回的email对象。
4.choices(类型:Field.choices)
choices是一个用来选择值的二维元组。其中,第一个值是实际存储的值,第二个值用来进行选择。
choices类型最好是在Django模型中使用。下面是官方文档给出的代码示例,这个代码示例实现了一个大学生年级类。
【代码3-5】
【代码分析】
在第03行代码中定义了一个大学生类CollegeStudent。
在第04~07行代码中定义了一组字符串变量,即FRESHMAN、SOPHOMORE、JUNIOR和SENIOR,分别用于表示大学生四个年级的名称。
在第08~13行代码中定义了一个choices类型的变量YEAR_IN_COLLEGE_CHOICES,其中包含了4个元组,使用了第04~07行代码中定义的变量。
在第14~18行代码中定义了一个字符型域变量year_in_college,将choices值定义为变量YEAR_IN_COLLEGE_CHOICES,默认值为FRESHMAN。
在第20~21行代码中定义了一个方法is_upperclass(),用于返回大学生中的高年级元组。
5.unique(类型:Field.unique)
如果unique的值设置为True,则这个字段必须在整个表中保持值唯一。unique类型还定义了一组关于日期和时间的子类型,例如unique_for_date(唯一日期)、unique_for_month(唯一月份)、unique_for_year(唯一年份)。
6.editable(类型:Field.editable)
editable默认值为True(真),如果值为False(假),则在Admin模式下字段将不能编辑。
7.primary_key(类型:Field.primary_key)
primary_key用于设置主键,一个字段只能设置一个主键。如果没有设置主键,则Django框架在创建表时会自动加上。如果primary_key值设置为True,则将该字段设置为该模型的主键,示例如下:
id = meta.AutoField('ID', primary_key=True)
8.help_text(类型:Field.help_text)
help_text是额外的“帮助”文本,随表单控件一同显示。示例如下:
help_text="Please use the following format: <em>YYYY-MM-DD</em>."
即便某个字段未用于表单,该类型对于生成文档也是很有用的。
9.verbose_name(类型:Field.verbose_name)
verbose_name用于设置admin模式中字段的显示名称。
10.validators(类型:Field.validators)
validators用于设置某个域的有效性检查列表。
11.db_column(类型:Field.db_column)
db_column用于为某个域指定数据库列表的名称;如果未指定,则使用该域的名称。
12.db_index(类型:Field.db_index)
如果db_index值为True,则创建数据库索引。
13.db_tablespace(类型:Field.db_tablespace)
db_tablespace用于为某个域的索引指定数据库表空间的名称。
Django模型中同样也定义了一组代表关系的字段——外键(Foreign Key),这一点与传统关系数据库的设计是一致的。
在Django模型中,外键是通过一个名称为ForeignKey的类实现的,具体声明如下:
class ForeignKey(to, on_delete, **options)
其中,参数to(必需的)表示要关联的类,on_delete表示删除操作时的级联关系,**options是一些可选参数。在创建“多对一”的关系时,必须设置参数to和on_delete。
如果要创建一个递归关系,即一个与其自身有“多对一”关系的对象,则写法如下:
models.ForeignKey('self', on_delete=models.CASCADE)
其中,使用models对象上的CASCADE参数表示在删除某一关联数据时,与之关联的全部数据也会删除。
参数on_delete的各个选项值说明如下:
· models.CASCADE:表示在删除某一关联数据时,与之关联的全部数据也会删除。
· models.DO_NOTHING:表示在删除关联数据时将会引发IntegrityError错误。
· models.PROTECT:表示在删除关联数据时将会引发ProtectedError错误。
· models.SET_NULL:表示在删除关联数据时,与之关联的值设置为null(前提是FK字段设置为可空)。
· models.SET_DEFAULT:表示在删除关联数据时,与之关联的值设置为默认值(前提FK字段设置默认值)。
· models.SET:表示在删除关联数据时,如果与之关联的值设置为指定值,则设置models.SET值;如果与之关联的值设置为可执行对象的返回值,则设置models.SET可执行对象。
可选参数**options的各个选项的说明如下:
· related_name=None:表示反向操作时使用的字段名。
· related_query_name=None:表示反向操作时使用的连接前缀。
· limit_choices_to=None:表示在Admin或ModelForm中显示关联数据时提供的条件。
· db_constraint=True:表示是否在数据库中创建外键约束。
· parent_link=False:表示在Admin中是否显示关联数据。
关于在Django模型中使用外键的方法,可参看下面官方文档给出的代码示例。
【代码3-6】
【代码分析】
在第10行代码中定义了一个制造商类Manufacturer。
在第03~08行代码中定义了一个汽车类Car。
在第04~07行代码中,通过models对象的ForeignKey()方法创建了汽车类Car的外键manufacturer。具体内容如下:
· 第05行代码中,参数to引用了制造商类Manufacturer。
· 第06行代码中,参数on_delete设置为models.CASCADE选项值。
在Django模型中,“一对一”关系是通过一个名称为OneToOneField的类实现的,具体声明如下:
class OneToOneField(to, on_delete, **options)
其中,参数to(必需的)表示要关联的类,on_delete表示删除操作时的级联关系,**options是一些可选参数。在创建一对一的关系时,必须设置参数to和on_delete。
对于一对一关系,生活中比较典型的例子就是银行“账户”和“联系人”之间的关系,示例代码如下:
【代码3-7】
【代码分析】
在第03~06行代码中定义了一个账户类Account。
在第07~16行代码中定义了一个联系人类Contact。具体内容如下:
· 在第12~15行代码中,通过models对象的OneToOneField()方法创建了联系人类Contact的外键account。
· 在第13行代码中,参数to引用了账户类Account。
· 在第14行代码中,参数on_delete设置为models.CASCADE选项值。
这样,在删除某个账户时,基于联系人类Contact中外键account的设置,相关联的联系人也会一同被删除。
在关联关系字段的外键使用过程中,除了“多对一”和“一对一”关系之外,最常用的就是“多对多”关系了。
在Django模型中,多对多关系是通过一个名称为ManyToManyField的类实现的,具体声明如下:
class ManyToManyField(to, **options)
其中,参数to(必需的)表示要关联的类,**options是一些可选参数。在创建多对多的关系时,必须设置参数to。
对于多对多关系,生活中比较典型的例子就是“作者”和“图书”之间的关系。简单来讲,就是一个作者可以编写多本书,而一本书也可以有多个作者,这个就是典型的“多对多”关系。代码示例如下:
【代码3-8】
【代码分析】
在第03~07行代码中,定义了一个作者类Author。
在第08~16行代码中,定义了一个图书类Book。具体说明如下:
在第13~15行代码中,通过models对象的ManyToManyField()方法创建了作者类Author的外键author,实现了多对多关联关系。其中在第14行代码中,参数to引用了作者类Author。
上面的代码实现了“作者”表与“图书”表之间多对多的关联关系,但是如果还想要关联某个作者写作的某一本图书的出版时间,因为表已经存在了,所以增加一个字段的操作就会比较麻烦。
对于这样的情形,Django模型允许指定一个用于管理多对多关联关系的中间模型,然后就可以把这些额外的字段添加到这个中间模型中。具体的方法就是在ManyToManyField()方法中,指定参数through作为中间模型。
下面,我们在【代码3-8】的基础上略作修改,实现添加一个“版本”字段的功能,代码如下:
【代码3-9】
【代码分析】
在第03~07行代码中,定义了一个作者类Author。
在第08~17行代码中,定义了一个图书类Book。具体说明如下:
在第13~16行代码中,通过models对象的ManyToManyField()方法创建了作者类Author的外键author,实现了多对多关联关系。
在第15行代码中,参数through引用了第三个类BookVersion。
在第18~29行代码中,定义了图书版本类(BookVersion)。具体说明如下:
· 在第19~22行代码中,通过models对象的ForeignKey()方法创建了作者类Author的外键author。
· 在第23~26行代码中,通过models对象的ForeignKey()方法创建了图书类Book的外键book。
· 在第27行代码中,通过models对象的CharField()方法新增了一个图书“版本”变量,该变量就是新增的字段。
如果已经存在的模型字段不能满足最初的需求,或者希望支持一些不太常见的数据库字段,Django模型支持自定义模型字段,并提供创建自定义字段的各方面内容。
Django模型内置的字段类型并未覆盖所有可能的数据库字段类型,一般只有类似VARCHAR和Integer这样的常见类型。对于更多的字段类型,就需要用户自己创建自定义类型了。自定义类型是一个相对复杂的Python对象,该对象可以采用某种形式进行序列化,适应标准的数据库字段类型。
这里,我们举一个创建“桥牌”自定义模型字段的例子。对于这个“桥牌”自定义模型字段,读者不需要知道“桥牌”具体的游戏规则,只需要知道一副“桥牌”共计52张牌,会平均分配给4个玩家。通常这4个玩家被称为“北”“东”“南”和“西”。
那么,自定义模型“桥牌”这个Python类就可以定义如下:
【代码3-10】
【代码分析】
在第01行代码中,定义了这个桥牌类的名称为Hand。
在第04~09行代码,在定义的初始化方法__init__()中,依次将north(北),east(东),south(南)和west(西)4个玩家设置为Hand类的self内置属性。
在Django模型中使用自定义模型字段时,是不需要修改这个字段的,对象属性的赋值与取值操作与其他Python类是一样的,关键技巧是告诉Django如何保存和加载对象。