AutoField
class AutoField(**options)
一个根据实际ID自动增长的IntegerField . 你通常不需要直接使用;如果不指定,一个主键字段将自动添加到你创建的模型中.详细查看 主键字段.
BigIntegerField
class BigIntegerField([**options])
一个64位整数, 类似于一个 IntegerField ,它的值的范围是 -9223372036854775808 到9223372036854775807之间. 这个字段默认的表单组件是一个TextInput.
BinaryField
class BinaryField([**options])
这是一个用来存储原始二进制码的Field. 只支持bytes 赋值,注意这个Field只有很有限的功能。例如,不大可能在一个BinaryField 值的数据上进行查询
Abusing BinaryField
尽管你可能想使用数据库来存储你的文件,但是99%的情况下这都是不好的设计。这个字段不是替代static files 的合理操作.
BooleanField
class BooleanField(**options)
true/false 字段。
此字段的默认表单挂件是一个CheckboxInput.
如果你需要设置null 值,则使用NullBooleanField 来代替BooleanField。
如果Field.default没有指定的话, BooleanField 的默认值是 None。
CharField
class CharField(max_length=None[,** options])
一个用来存储从小到很大各种长度的字符串的地方
如果是巨大的文本类型, 可以用 TextField.
这个字段默认的表单样式是 TextInput.
CharField必须接收一个额外的参数:
CharField.max_length
字段的最大字符长度.max_length将在数据库层和Django表单验证中起作用, 用来限定字段的长度.
Note:
如果你在写一个需要导出到多个不同数据库后端的应用,你需要注意某些后端对max_length有一些限制,查看数据库后端注意事项来获取更多的细节
MySQL的使用者们:
如果你在使用该字段的同时使用MySQLdb 1.2.2以及 utf8_bin 校对 其一般不是 默认情况), 你必须了解一些问题. 详细请参考 MySQL database notes .
CommaSeparatedIntegerField
class CommaSeparatedIntegerField(max_length=None[, **options])
一个逗号分隔的整数字段。像 CharField一样, 需要一个max_length 参数, 同时数据库移植时也需要注意。
DateField
lass DateField([auto_now=False, auto_now_add=False, **options])
这是一个使用Python的datetime.date实例表示的日期. 有几个额外的设置参数:
DateField.auto_now
每次保存对象时,自动设置该字段为当前时间。用于"最后一次修改"的时间戳。注意,它总是使用当前日期;和你可以覆盖的那种默认值不一样。
DateField.auto_now_add
当对象第一次被创建时自动设置当前时间。用于创建时间的时间戳. 它总是使用当前日期;和你可以覆盖的那种默认值不一样。
该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮.包含了一个额外的invalid_date错误消息键.
auto_now_add, auto_now, and default 这些设置是相互排斥的. 他们之间的任何组合将会发生错误的结果.
Note:
在目前的实现中,设置auto_now或者auto_now_add为True将为让这个字段同时得到editable=False和blank=True这两个设置.
Note:
auto_now and auto_now_add这两个设置会在对象创建或更新的时刻,总是使用default timezone(默认时区)的日期. 如果你不想这样,你可以考虑一下简单地使用你自己的默认调用或者重写save()(在save()函数里自己添加保存时间的机制.译者注)而不是使用auto_now or auto_now_add; 或者使用DateTimeField字段类来替换DateField 并且在给用户呈现时间的时候,决定如何处理从datetime到date的转换.
DateTimeField
class DateTimeField([auto_now=False, auto_now_add=False, **options])
它是通过Python datetime.datetime实例表示的日期和时间. 携带了跟DateField一样的额外参数.
该字段默认对应的表单控件是一个单个的TextInput(单文本输入框). 管理界面是使用两个带有 JavaScript控件的 TextInput 文本框.
DecimalField
class DecimalField(max_digits=None, decimal_places=None[, **options])
用python中 Decimal 的一个实例来表示十进制浮点数. 有两个 必须的参数:
DecimalField.max_digits
位数总数,包括小数点后的位数。该值必须大于等于decimal_places.
DecimalField.decimal_places
小数点后的数字数量
例如,要保存最大为 999 并有两位小数的数字,你应该使用:
models.DecimalField(..., max_digits=5, decimal_places=2)
而要存储那些将近10亿,并且要求达到小数点后十位精度的数字:
models.DecimalField(..., max_digits=19, decimal_places=10)
该字段默认的窗体组件是 TextInput.
Note:
想获取更多关于 FloatField 和 DecimalField 差异, 请参照 FloatField vs. DecimalField.
DurationField
New in Django 1.8.
class DurationField([**options])
用作存储一段时间的字段类型 - 类似Python中的timedelta. 当数据库使用的是PostgreSQL, 该数据类型使用的是一个 interval 而在Oracle上,则使用的是 INTERVAL DAY(9) TO SECOND(6). Otherwise a bigint of microseconds is used.
Note:
DurationField 的算数运算在多数情况下可以很好的工作。 然而,在除了PostgreSQL之外的其他数据库中, 将 DurationField 与 DateTimeField 的实例比较则不会得到正确的结果。
EmailField
class EmailField([max_length=254, **options])
一个 CharField 用来检查输入的email地址是否合法。它使用 EmailValidator 来验证输入合法性。
Changed in Django 1.8:
默认最大长度 max_length 从75增加到254以符合RFC3696/5321标准。
FileField
class FileField([upload_to=None, max_length=100, **options])
一个上传文件的字段。
Note:
FileField字段不支持primary_key 和unique参数,如果使用会生成 TypeError错误
有两个可选参数:
FileField.upload_to
Changed in Django 1.7:
在旧版本Django中,upload_to 属性是必须要有的;
一个本地文件系统的路径,它将附加到MEDIA_ROOT 设置的后面来确定url 属性的值。
这个路径可能会包含一个 strftime() 格式串,并且会在文件上传时被替换为 实际的date/time作为文件路径 (这样上传的文件就不会塞满你指定的文件夹了).
它还可以是一个可调用对象如函数,将调用它来获取上传路径,包括文件名。这个可调用对象必须接受两个参数,并且返回一个Unix 风格的路径(带有前向/)给存储系统。将传递的两个参数为:
Argument Description
instance
FileField 定义所在的模型的实例。更准确地说,就是当前文件的所在的那个实例。
大部分情况下,这个实例将还没有保存到数据库中,若它用到默认的AutoField 字段,它的主键字段还可能没有值。
FileField.storage
一个Storage 对象,用于你的文件的存取。参见管理文件 获取如何提供这个对象的细节。
这个字段的默认表单Widget 是ClearableFileInput。
在模型中调用FileField 或 ImageField (见下方) 需如下几步:
在你的settings文件中, 你必须要定义 MEDIA_ROOT 作为Django存储上传文件的路径(从性能上考虑,这些文件不能存在数据库中。) 定义一个 MEDIA_URL 作为基础的URL或者目录。确保这个目录可以被web server使用的账户写入。
在模型中添加FileField 或 ImageField 字段, 定义 upload_to参数,内容是 MEDIA_ROOT 的子目录,用来存放上传的文件。
数据库中存放的仅是这个文件的路径 (相对于MEDIA_ROOT). 你很可能会想用由Django提供的便利的url 属性。比如说, 如果你的ImageField 命名为 mug_shot, 你可以在template中用 {{ object.mug_shot.url }}获得你照片的绝对路径。
例如,如果你的 MEDIA_ROOT设定为 '/home/media',并且 upload_to设定为 'photos/%Y/%m/%d'。 upload_to的'%Y/%m/%d'被strftime()所格式化;'%Y' 将会被格式化为一个四位数的年份, '%m' 被格式化为一个两位数的月份'%d'是两位数日份。如果你在Jan.15.2007上传了一个文件,它将被保存在/home/media/photos/2007/01/15目录下.
如果你想获得上传文件的存盘文件名,或者是文件大小,你可以分别使用 name 和 size 属性; 更多可用属性及方法信息,请参见 File 类索引 和 Managing files 主题指导.
Note:
保存的文件作为模型存储在数据库中的一部分,所以在磁盘上使用的实际的文件名在模型保存完毕之前是不可靠的。
上传的文件对应的URL可以通过使用 url 属性获得. 在内部,它会调用 Storage 类下的url()方法.
值得注意的是,无论你在任何时候处理上传文件的需求,你都应该密切关注你的文件将被上传到哪里,上传的文件类型,以避免安全漏洞。认证所有上传文件 以确保那些上传的文件是你所认为的文件。例如,如果你盲目的允许其他人在无需认证的情况下上传文件至你的web服务器的root目录中,那么别人可以上传一个CGI或者PHP脚本然后通过访问一个你网站的URL来执行这个脚本。所以,不要允许这种事情发生。
甚至是上传HTML文件也值得注意,它可以通过浏览器(虽然不是服务器)执行,也可以引发相当于是XSS或者CSRF攻击的安全威胁。
FileField 实例将会在你的数据库中创建一个默认最大长度为100字符的varchar 列。就像其他的fields一样, 你可以用 max_length 参数改变最大长度的值.
ForeignKey [ManyToManyField / OneToOneField]
表名=modles.ForeignKey('表名', related_name='+')
表名=models.ManyToManyField('表名')
models中的外键表达方法,定义多对一、多对多、一对一关系; models 多对一可以关联自身,多对多关系也可以关联自身,如下:
leader=models.ForeignKey('self',blank=True,null=True)
friends = models.ManyToManyField("self")
related_name 是反向名称,用来从被关联字段指向关联字段。如果不想设置反向关系,设置related_name为'+'或者以'+'结束。
user= models.ForeignKey(User, related_name='+')
如果有多个ManyToManyField指向同一个Model,这样反向查询FOO_set的时候就无法弄清是哪个ManyToManyField字段了,可以禁止反向关系:
users = models.ManyToManyField(User, related_name='u+')
referents = models.ManyToManyField(User, related_name='ref+')
详情参照文档 : 点击这里
FileField 和FieldFile
class FieldFile[source]
当你添加 FileField 到你的模型中时, 你实际上会获得一个 FieldFile的实例来替代将要访问的文件。 除了继承至 django.core.files.File的功能外, 这个类还有其他属性和方法可以用于访问文件:
FieldFile.url
通过潜在Storage 类的url()方法可以只读地访问文件的URL。
FieldFile.open(mode='rb')[source]
该方法像标准的Python open() 方法,并可通过 mode参数设置打开模式.
FieldFile.close()[source]
该方法像标准的Pythonfile.close() 方法,并关闭相关文件.
FieldFile.save(name, content, save=True)[source]
这个方法会将文件名以及文件内容传递到字段的storage类中,并将模型字段与保存好的文件关联. 如果想要手动关联文件数据到你的模型中的 FileField实例, 则save() 方法总是用来保存该数据.
方法接受两个必选参数: name 文件名, 和 content 文件内容.可选参数save 控制模型实例在关联的文件被修改时是否保存.默认为 True.
注意参数 content 应该是 django.core.files.File的一个实例, 而不是Python内建的File对象.你可以用如下方法从一个已经存在的Python文件对象来构建 File :
from django.core.files import File
# Open an existing file using Python's built-in open()
f = open('/tmp/hello.world')
myfile = File(f)
或者,你可以像下面的一样从一个python字符串中构建
from django.core.files.base import ContentFile
myfile = ContentFile("hello world")
FieldFile.delete(save=True)[source]
删除与此实例关联的文件,并清除该字段的所有属性。注意︰ 如果它碰巧是开放的调用 delete() 方法 时,此方法将关闭该文件。
模型实例save的文件与此字段关联的可选 保存 参数控件已被删除。默认值为 True。
注意,model删除的时候,与之关联的文件并不会被删除。如果你要把文件也清理掉,你需要自己处理。
FilePathField
class FilePathField(path=None[, match=None, recursive=False, max_length=100, **options])
一个 CharField ,内容只限于文件系统内特定目录下的文件名。有三个参数, 其中第一个是 必需的:
FilePathField.path
必填。这个FilePathField 应该得到其选择的目录的绝对文件系统路径。例如: "/home/images".
FilePathField.match
可选的.A regular expression, as a string, that FilePathField will use to filter filenames. Note that the regex will be applied to the base filename, not the full path. Example: "foo.*.txt$", which will match a file called foo23.txt but not bar.txt or foo23.png.
FilePathField.recursive
可选的.Either True or False. Default is False. Specifies whether all subdirectories of path should be included
FilePathField.allow_files
可选的.Either True or False. Default is True. Specifies whether files in the specified location should be included. Either this or allow_folders must be True.
FilePathField.allow_folders
是可选的.输入 True 或者 False.默认值为 False.Specifies whether folders in the specified location should be included. Either this or allow_files must be True.
Of course, these arguments can be used together.
有一点需要提醒的是 match只匹配基本文件名(base filename), 而不是整个文件路径(full path):
FilePathField(path="/home/images", match="foo.*", recursive=True)
...will match /home/images/foo.png but not /home/images/foo/bar.png because the match applies to the base filename (foo.png and bar.png).
FilePathField实例被创建在您的数据库为varchar列默认最大长度为 100 个字符。作为与其他字段,您可以更改使用的max_length最大长度。
FloatField
class FloatField([**options])
用Python的一个float 实例来表示一个浮点数.
该字段的默认组件是一个 TextInput.
FloatField vs. DecimalField
有时候FloatField 类会和DecimalField 类发生混淆. 虽然它们表示都表示实数,但是二者表示数字的方式不一样。FloatField 使用的是Python内部的 float 类型, 而DecimalField 使用的是Python的 Decimal 类型. 想要了解更多二者的差别, 可以查看Python文档中的 decimal 模块.
ImageField
class ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, **options])
继承了 FileField的所有属性和方法, 但还对上传的对象进行校验,确保它是个有效的image.
除了从FileField继承来的属性外,ImageField 还有宽和 高属性。
为了更便捷的去用那些属性值, ImageField 有两个额外的可选参数
ImageField.height_field
该属性的设定会在模型实例保存时,自动填充图片的高度.
ImageField.width_field
该属性的设定会在模型实例保存时,自动填充图片的宽度.
ImageField字段需要调用Pillow 库.
ImageField会创建在你的数据库中 和 varchar 一样,默认最大长度为100和其他字段一样, 你可以使用max_length 参数来设置默认文件最大值.
The default form widget for this field is a ClearableFileInput.
IntegerField
class IntegerField([**options])
一个整数。在Django所支持的所有数据库中,从 -2147483648 到 2147483647 范围内的值是合法的。默认的表单输入工具是TextInput.
IPAddressField
class IPAddressField([**options])
Deprecated since version 1.7:
该字段已废弃,从1.7开始支持GenericIPAddressField.
IP地址,会自动格式化(例如:“192.0.2.30”)。默认表单控件为 TextInput.
GenericIPAddressField
class GenericIPAddressField([protocol=both, unpack_ipv4=False, **options])
一个 IPv4 或 IPv6 地址, 字符串格式 (例如 192.0.2.30 或 2a02:42fe::4). 这个字段的默认表单小部件是一个TextInput.
IPv6 地址会根据 RFC 4291 章节 2.2所规范, 包括该章节中第三段的的IPv4格式建议, 就像 ::ffff:192.0.2.0这样. 例如, 2001:0::0:01 将会被规范成 2001::1, ::ffff:0a0a:0a0a 被规范成 ::ffff:10.10.10.10. 所有字符都会被转换成小写.
GenericIPAddressField.protocol
限制有效输入的协议类型. 允许的值是 'both' (默认值), 'IPv4' 或 'IPv6'. 匹配不区分大小写.
GenericIPAddressField.unpack_ipv4
Unpacks IPv4 mapped addresses like ::ffff:192.0.2.1. If this option is enabled that address would be unpacked to 192.0.2.1. Default is disabled. Can only be used when protocol is set to 'both'.
If you allow for blank values, you have to allow for null values since blank values are stored as null.
NullBooleanField
class NullBooleanField([**options])
Like a BooleanField, but allows NULL as one of the options. Use this instead of a BooleanField with null=True. The default form widget for this field is a NullBooleanSelect.
PositiveIntegerField(正整数字段)
类似 IntegerField, 但值必须是正数或者零(0). Values from 0 to 2147483647 are safe in all databases supported by Django. The value 0 is accepted for backward compatibility reasons.
PositiveSmallIntegerField
class PositiveSmallIntegerField([**options])
该模型字段类似 PositiveIntegerField, 但是只允许小于某一特定值(依据数据库类型而定)。从0 到 32767 这个区间,对于Django所支持的所有数据库而言都是安全的。
SlugField
class SlugField([max_length=50, **options])
Slug 是一个新闻术语(通常叫做短标题)。一个slug只能包含字母、数字、下划线或者是连字符,通常用来作为短标签。通常它们是用来放在URL里的。
与CharField类似, 你可以指定 max_length 的值(read the note about database portability and max_length in that section, too). 如果没有指定 max_length, Django将会默认长度为50。
Implies setting Field.db_index to True.
It is often useful to automatically prepopulate a SlugField based on the value of some other value. You can do this automatically in the admin using prepopulated_fields.
SmallIntegerField
class SmallIntegerField([**options])
与 IntegerField这个字段类型很类似,不同的是SmallInteger类型只能在一个确定的范围内(数据库依赖)。对于django来讲,该字段值在 -32768 至 32767这个范围内对所有可支持的数据库都是安全的。
TextField
class TextField([**options])
大文本字段。该模型默认的表单组件是Textarea。
Changed in Django 1.7:
如果你在这个字段类型中使用了max_length属性,它将会在渲染页面表单元素Textarea 时候体现出来。但是并不会在model或者数据库级别强制性的限定字段长度。Use a CharField for that。
TimeField
class TimeField([auto_now=False, auto_now_add=False, **options])
时间字段,和Python中 datetime.time 一样。Accepts the same auto-population options as DateField.
表单默认为 TextInput.输入框。The admin adds some JavaScript shortcuts.
URLField
class URLField([max_length=200, **options])
一个CharField 类型的URL
The default form widget for this field is a TextInput.
Like all CharField subclasses, URLField takes the optional max_length argument. If you don’t specify max_length, a default of 200 is used.
UUIDField
New in Django 1.8.
class UUIDField([**options])
一个用来存储UUID的字段。使用Python的UUID类。 当使用PostgreSQL数据库时,该字段类型对应的数据库中的数据类型是uuid,使用其他数据库时,数据库对应的是char(32)类型。
使用UUID类型相对于使用具有primary_key参数的AutoField类型是一个更好的解决方案。 数据库不会自动生成UUID,所以推荐使用default参数:
import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# other fields
注意:这里传递给default是一个可调用的对象(即一个省略了括号的方法),而不是传递一个UUID实例给default
Field Options 选项
null
如果为 True , Django 在数据库中会将空值(empty)存储为 NULL 。默认为 False 。
blank
设置字段是否可以为空,默认为False(不允许为空)
和null的区别在于:null是数据库的范围,而blank是用于验证。如果一个字段的 blank=True ,Django 在进行表单数据验证时,会允许该字段是空值。如果字段的 blank=False ,该字段就是必填的。
choices
它是一个可迭代的二元组(例如,列表或是元组),用来给字段提供选择项。如果设置了 choices, Django会显示选择框,而不是标准的文本框,而且这个选择框的选项就是 choices 中的元组。
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。在一个给定的 model 类的实例中,想得到某个 choices 字 段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
u'L'
>>> p.get_shirt_size_display()
u'Large'
default
默认值,可以是一个具体的值也可以是一个对象,每次调用次会创建一个新的对象
在DateTimeField内, 想默认一个当前时间:
import django.utils.timezone as timezone
创建时间 = models.DateTimeField(default=timezone.now)
当参数choices下需要一个默认选项时候, default=ID (列表中的数字编号)
help_text
附加的帮助信息。在使用表单时会显示在字段下面。即使不使用表单也可以起来帮助文档的作用。
primary_key
如果为True,则表示这个字段是主键。
如果你没有设置主键,Django会自动创建一个自增的IntergerField类型主键,可以通过自定义主键来覆盖默认的行为。
unique
如果为 True ,那么字段值就必须是全表唯一的。
unique_for_date [unique_for_month / unique_for_year]
字符串类型,值指向一个DateTimeField 或者 一个 DateField的列名称。日期唯一,如下例中系统将不允许title和pub_date两个都相同的数据重复出现
db_column
string 值,指定当前列在数据库中的名字,不设置,将自动采用model字段名;
db_index
boolean 值,如果为True将为此字段创建索引;
editable
boolean 值,如果为假,admin模式下将不能改写。缺省为(True);
error_messages
字典,设置默认的出错信息,可覆盖的key 有 null, blank, invalid, invalid_choice, 和 unique。
radio_admin
用于 admin 模式下将 select 转换为 radio 显示。只用于 ForeignKey 或者设置了choices
max_length
CharField类型中必须含有 max_length限定一个字段长度
max_digits / decimal_places
类型为 Decimal 的时候,必须指定整数位max_digits和小数位decimal_places
verbose_name
string类型。更人性化的列名。
upload-to
上传到哪个位置,更多与image,filepath配合使用
validators
有效性检查。无效则抛出 django.core.validators.ValidationError 异常。
如何实现检查器 见:https://docs.djangoproject.com/en/dev/ref/validators/
Automatic primary key fields
默认情况下,Django 会给每个 model 添加下面这个字段:
id = models.AutoField(primary_key=True)
这是一个自增主键字段。
如果你想指定一个自定义主键字段,只要在某个字段上指定 primary_key=True 即可。如果 Django 看到你显式地设置了 Field.primary_key,就不会自动添加 id 列。
每个 model 只要有一个字段指定 primary_key=True 就可以了。(可以自定义也可以保持默认自动增加的主键)
Verbose field names(详细名称)
每个字段的类型,除了ForeignKey, ManyToManyField 和 OneToOneField外,还有一个可选的第一位置参数,这个参数用于标记字段的详细名称。如果Verbose field names没有显示的给出,Django会自动创建这一字段属性,将字段名中的"_"转换成空格。
例如:设置详细名称为 "person's first name":
first_name = models.CharField("person's first name", max_length=30)
如果没有设置详细名称,则详细名称为: "first name":
first_name = models.CharField(max_length=30)
由于ForeignKey, ManyToManyField和 OneToOneField
需要使用第一参数,所以可以显示的使用 verbose_name来设置详细名称:
poll = models.ForeignKey(Poll, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(Place, verbose_name="related place")
仅在以上特列中使用verbose_name,Django会自动利用第一个参数。
Relationships(关系)
Django支持关系数据库中常用的many-to-one,many-tomany,one-to-one
Many-to-one(多对一关系)
定义Many-to-one关系,使用django.db.models.ForeignKey。使用方法和其他字段类型一样:在model中定义时包含ForeignKey属性。
ForeignKey必须要一个参数:需要链接到哪个model.
例如:一辆汽车(car)和汽车制造商(manufacturer)的关系,那么一个汽车制造商会有制造多辆车,但每辆车却只能有一个汽车制造商:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
你也可以定义一个递归的关系(在对象内容实部Many-to-one的定义)和relationships to models not yet defined(没看明白,看完模型关系后再修改);
建议但不强制要求ForeignKey字段的名字是模型的小写字母的名字(例如在上例中使用的manufacturer)。当然你可以使用任何你想要的名字,例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
Many-to-many(多对多关系)
定义Many-to-many关系,使用django.db.models.ManyToManyField.使用方法和其他字段类型一样:在model中定义包含ManyToManyField属性。
ManyToManyField必须要一个参数:需要链接到那个model.
例如,一个Pizza有多个Topping对象——也就是一个Topping可以在多个Pizza上,每个Pizza有多个Toppings——这种情况我们可以这样定义:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
和ForeignKey一样,可以创建递归关系(在对象内部实现Many-to-many的定义)和relationships to models not yet defined
建议但不强制要求ManyToManyField的名字(上面的例子中的toppings)是复数形式,复数形式是为了描述相关模型对象的集合。
哪个模型带有ManyToManyField都没关系,但你只能在其中一个模型中使用它,而不能在两个模型中都使用它。
一般来说,ManyToManyField实例应该包含在使用表单编辑的对象中,在上面的例子中,toppings在Pizza中(而不是Topping有pizzas ManyToManyField),因为一个pizzas有多个Topping,比一个Topping在多个pizzas上更容易让人理解。这就是上面我们使用的方式,Pizza管理表单将让用户选择那种Topping。
还有一些可选参数。
Many-to-many关系的额外字段
如果只需要处理简单的多对多关系,就像上面pizzas和topping的关系,那么ManyToManyField字段就可以满足需要,然而,有些时候你需要让数据在两个模型之间产生联系。
例如,考虑一下跟踪乐队和乐队拥有的音乐家的应用程序的例子。这是一个人和这个人所在团队之间的多对多关系,因此你可以使用ManyToManyField来描述这个关系。然而,这种成员关系有很多你想要搜集的细节,比如这个人加入团队的时间。
对于这种情况,Django让你可以指定用来管理多对多关系的模型。然后你可以在中间模型中放入额外的字段。中间模型使用through参数指向像中间人一样工作的模型,来和ManyToManyField发生关系。对于四个音乐家的例子,代码可能像这样子:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
当你建立中间模型时,你需要为模型明确地指定外键,以建立多对多关系。这个明确的声明定义了两个模型是如何产生联系的。
对于中间模型,有一些限制:
中间模型必须包含并且只包含一个到目标模型的外键(在上面的例子中的Group)。或者使用ManyToManyField.through_fields来明确指定外键关系。如果你有多个外键,但没有指定through_fields,会产生校验错误。类似的限制适用于外键的目标model(例如Person)
对于一个model通过中间model实现多对多关系,两个到同一模型的外键是允许的,但会被认为是多对多关系的两个不同侧面。如果有两个或以上的外键定义,你必须要定义through_fields,否则会产生校验错误。
当使用中间模型来定义一个到自己的多对多关系的模型时,你必须使用symmetrical=False(参阅“模型字段参考”)。
现在你已经建立了ManyToManyField来使用中间模型(在这个例子中是MemberShip),你可以开始创建一些多对多的对应关系了。具体来说是创建中间模型的一些实例:
现在已经在中间model中设置了ManyToManyField(例子中的Membership),你可以通过中间model创建关系实例:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]
不同于一般的many-to-many关系字段,你不能通过直接通过关系对象进行增加、创建或赋值(即:beatles.members = [...])
# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name="George Harrison")
# AND NEITHER WILL THIS
>>> beatles.members = [john, paul, ringo, george]
这是因为你需要知道一些Person和Group关系之外的一些细节,这些细节在中间model--Membership中定义,而不仅仅只是简单创建了Person和Group之间的关系。类似关系的唯一解决办法是创建中间model
基于同样的原因 remove() 也是被禁用的,但可以通过 clear() 清除所有多对多关系实例:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
[]
一旦创建了中间model实例,并建立了一个多对多关系实例,就可以和正常的多对多关系一样进行查询:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]
因为你正在使用中间模型,你也可以使用它的属性来进行查询:
# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
... group__name='The Beatles',
... membership__date_joined__gt=date(1961,1,1))
[<Person: Ringo Starr]
如果你需要访问membership’s 信息,你可以这样做直接查询Membership:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'
当然你也可以通过多对多的反向关系从Person 实例进行查询:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'
One-to-one关系
要定义一对一关系,请使用OneToOneField。它的使用方法和其它字段一样:把它包含在模型的类属性中。
当对象以某种方式扩展了另一个对象的主键时,对象的主键是最重要的。
OneToOneField要求一个位置参数:该模型相关的类。
例如,如果你将创建一个数据表places,你可能会在数据表中建立一些相当标准的东西,就像地址、电话号码等等。然后,如果你想在places上建立一个饭馆,你可以不必重复劳动,在Restaurant模型中复制这些字段,你可以建立一个带有到Place的OneToOneField(因为饭馆就是一个place;实际上,对于这种情况典型的做法是使用继承,这实际上是一种隐式的一对一关系)。
和外键一样,你可以定义循环关系和到未定义模型的关系;
OneToOneField也接受一个可选的参数parent_link 这个参数在“模型字段参考”有介绍。
OneToOneField类曾经会自动成为模型的主键。现在情况不再如此了(如果你愿意你可以手动传递一个primary_key参数)。因此,现在一个模型可以拥有多个OneToOneField类型的字段。
Models across files(跨文件的model)
在当前model和另一个应用程序中的model建立关系是没有问题的,只需要引入相关的model,然后在需要的时候使用就可以了:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
Field name restrictions(字段名限制 )
Django对字段名只有两个限制:
1) 字段名不能是Python的保留关键字,不然会导致Python语法错误。例如:
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
2) 字段名在一行中不能包含一个以上的下划线,这和Django的搜索查询语法的工作方式有关。例如:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
这些限制是可以绕过的,因为你的字段名并不需要和数据表的列名匹配。
SQL保留字,比如join、where或select,可以用作模型字段名,因为Django在进行底层的SQL查询前会对所有数据表名和列名进行转义。
Custom field types(自定义字段类型)
如果现有的字段类型不能满足你的需要,或者你使用的数据库具有一些特殊的类型,你可以创建自己字段类型。
Meta options
定义model的metadata(元数据)是通过使用一个内部类Meta,例:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
model元数据不是一个字段,例如ordering,db_table,verbose_name 和 verbose_name_plural这些附加选项,都可以放到class Meta中,这些选项都不是必需的。
Model attributes objects
这是model最重要的Manager属性,通过它查询数据库并于model之间形成映射。如果没有自定义manager,默认名称为objects。managers只能通过model类,而不能通过model类实例来访问。
Model methods
通过model自定义方法添加自定义"行级"功能,而managers方法是为“表级”添加自定义功能,model自定义方法可以通过model实例来使用。
这样就可以把业务逻辑都放在一个model里。例如:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
上个实例的最后一个方法是属性。
这个model实例继承自models.Model,会自动具备大量的方法,可以覆盖大部分的方法,但有几个却是必须的:
str() (Python 3)
在Python3中相当于unicode()
unicode() (Python 2)
这是一个Python的"魔术方法",它以unicode方式返回任何对象的陈述。Python和Django需要输出字符串陈述时使用。例如在交互式控制台或管理后台显示的输出陈述。
默认的实现并不能很好的满足需要,所以最好自定义这个方法,
get_absolute_url()
定义对象的URL.在管理界面中,任何时候都可以通过URL找到一个对象。
任何通过URL访问的对象,都应该有唯一标识。
Overriding predefined model methods
还有一些model方法封装了一些行为。当你想自定义数据库行为,尤其是想改变save() 和 delete()方式的时候。
你可以自由地重写这些方法来改变行为。
如果你想在保存对象的时候,覆盖内置save() 行为:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
你也可以阻止保存:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
需要特别注意的要记得调用父类的方法--super(Blog, self).save(args, kwargs),以确保对象仍然被保存到数据库中,如果你忘记调用父类的方法,默认的行为不会发生,数据库也不会发生改变。
随着时间的推移,django会增加或扩展一些新的方法,如果你使用args, kwargs作为你的方法参数,就必须要保证能正确处理这些参数的增加。
覆盖方法大多数不会用于批量操作
delete()并不一定是在调用一个QuerySet批量删除时被触发。为了确保自定义的删除逻辑被执行,则需要使用 pre_delete and/or post_delete 信号。
不幸的是,还没有一个好的方法用于批量的创建和更新,因为没有save()、pre_save、post_save会被调用。
Executing custom SQL
另外一种常见的模式是在model方法和module-level方法中执行自定义SQL。
Model inheritance(Model继承)
Model的继承和普通的Python类继承几乎相同,但基类的继承必须是django.db.models.Model.
在Django中有三种继承风格:
1、如果你想用父类来保存每个子类共有的信息,并且这个类是不会被独立使用的,那么应该使用抽象基类。
2、如果你继承现有model(甚至可能这个类是在另一个应用程序中),并希望每个model都拥有自己对应的数据库,那就应该使用多表继承。
3、如果你只是想修改model的Python-level行为,而不改变models fields,则使用代理模式。
Abstract base classes
当你想集中一些公共信息,可以使用虚类。你需要在model的元数据中设置 abstract=True ,这个model将不会被用于创建任何数据表。相反,当他被作为其他子类的基类时,他的字段将被添加到这些子类中。如果抽象类和他的子类有相同名称的字段会产生一个异常。
例如:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Student会拥有三个字段:name\age\home_group。CommonInfo将不能像普通的Django model一样使用,因为他是一个抽象基类。他不会产生一个数据表或者拥有一个管理器,也不能被实例化或直接调用。
在许多情况下这种类型的继承正是你想要的,它提供了一种用来分解公共信息的方法,虽然只能实现数据表级别创建子模型。
Meta inheritance
当创建一个抽象基类的时候,Django允许在基类中声明各种Meta属性,如果子类没有声明自己的Meta元数据,他将继承父类的。如果子类想扩展父类的Meta元数据,则可以继承父类的Meta。例如:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
Django对于抽象基类的元数据调整只应该在安装之前设置abstract=false。这意味着从抽象基类派生的子类不会自动转型成抽象类本身。当然你也可以继承来自别一个抽象基类。只需要记住abstract=True每次都应该明确设置。(这段没怎么看明白)
在抽象基类中某些属性几乎是没有任何意义的,包括父类的元数据。例如使用db_table将意味着所有的子类(那些没有指定自己的Meta)将使用同一数据库,这肯定不会是你想要的。
Be careful with related_name(注意抽象基类中的反向关系名称定义)
如果你在ForeignKey和ManyToManyField的属性中使用related_name,你必须为字段指定一个唯一反向关系名称。在抽象基类中会有一些问题,因为抽象基类的每个字段都会被包括在他的每一个子类中,包括related_name.
要解决这个问题,应该在抽象基类中使用related_name时,名称中应包含'%(app_label)s' 和 '%(class)s'.
'%(class)s':小写的子类名称
'%(app_label)s':应用的小写名称(app)。因为每个已安装的应用名称都是唯一的,所以产生的名称最终也会不同。
例如:首先定义common/models.py:
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
class Meta:
abstract = True
class ChildA(Base):
pass
class ChildB(Base):
pass
然后另一个APP rare/models.py:
from common.models import Base
class ChildB(Base):
pass
在这个示例中 common.ChildA.m2m 字段的反向关系名称是common_childa_related,而common.ChildB.m2m 的关系名称ommon_childb_related。这是因为使用了'%(app_label)s' 和 '%(class)s'产生了不同的反向关系名称。如果你定义了related_name,但忘记了使用'%(app_label)s' 和 '%(class)s' Django会系统检查或运行migrate时引发错误。
如果没有在抽象基类的字段中定义related_name属性,默认关系名称将是子类名称+"_set"。通常related_name会被直接在子类的字段属性中被定义。例如上例中,如果related_name属性被省略。common.ChildA.m2m 的反向关系名称应该是childa_set,common.ChildB.m2m的反向关系名称应该是childb_set。
Multi-table inheritance(多表继承)
Django支持的第二种model继承是多表继承,在继承结构中每个model都是独立的。都对应着自己的数据库表,可以进行独立的查询等操作。继承关系实际是子model和每个父model之间的关系(通过自动创建OneToOneField)。例如:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
所有Place的字段都可以在Restaurant中使用,虽然数据存放在不同的数据表中。所以可以如下使用:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
如果一个Place对象存在相应的Restaurant对象,那么就可以使用Place对象通过关系获得Restaurant对象:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
但如果place对象所对应的Restaurant对象不存在,则会引发 Restaurant.DoesNotExist 异常。
Meta and multi-table inheritance
在多表继承的情况下继承父类的Meta是没有意义的。所有的Meta都已经被应用到父类,再应用这些Meta只会导致矛盾。
所以子model不能访问到父model的Meta,然而也有少数的情况下,子model会从父model中继承一些行为,例如子model没有指定 ordering或 get_latest_by属性,那么就会从父model中继承。
如果父model中有一个排序,但你不希望子model有任何的排序规划,你可以明确的禁用:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
Inheritance and reverse relations(继承与反向关系)
因为多表继承实际是隐式的使用OneToOneField来键接父Model和子model,在这种关系有可能会使用父model来调用子model,比如上面的例子。但是如果你把ForeignKey和ManyToManyField关系应用到这样一个继承关系中,Django会返回一个验证错误,必须要指定一个related_name字段属性。
例如上面的例子,我们再创建一个子类,其中包含一个到父model的ManyToManyField关系字段:
class Supplier(Place):
customers = models.ManyToManyField(Place)
这时会产生一个错误:
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.
HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
解决这个问题只需要在customers字段属性中增加related_name属性:models.ManyToManyField(Place, related_name='provider').
Specifying the parent link field
如上所述,Django会自动创建一个OneToOneField链接你的子model和任何非抽象父model。如果你想自定义子model键接回父model的属性名称,你可以创建自己的OneToOneField并设置parent_link=True,表示这个字段是对父model的回链。
Proxy models
当使用多表继承时一个新的数据表model会在每一个子类中创建,这是因为子model需要存储父mdoel不存在的一些数据字段。但有时只需要改变model的操作行为,可能是为了改变默认的管理行为或添加新的方法。
这时就应该使用代理模式的继承:创建原始model的代理。你可以创建一个用于 create, delete 和 update的代理model,使用代理model的时候数据将会真实保存。这和使用原始model是一样的,所不同的是当你改变model操作时,不需要去更改原始的model。
代理模式的声明和正常的继承声明方式一样。你只需要在Meta class 中定义proxy为True就可以了。
例如,你想为Person model添加一个方法:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
MyPerson这个类将作用于父类Person所对应的真实数据表。可通过MyPerson进行所有相应的操作:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
你也可以使用代理模式来定义model的不同默认排序,例如:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
这样当使用原始model查询时结果是无序的,而使用OrderedPerson进行查询时将按last_name进行排序。
QuerySets still return the model that was requested(QuerySets的类型依然会是原始model类型)
当你通过MyPerson来查询Person对象时,返回的QuerySet依然会是Person对象类型的集合。使用代理模式的model是依靠原始model的,是原始model的扩展。而不是用来替代父model。
Base class restrictions(基类限制)
代理model必须继承一个非抽像model。
不能从多个非抽像model继承,代理模式不能为不同model之间创建链接。
代理模式可以从任意没有定义字段的抽象model继承。
Proxy model managers
如果没有指定代理model的管理器(managers),它将继承父类的管理行为。如果你定义了代理model的管理器,它将会成为默认的,当然父类中定义的定义的任何管理器仍然是可以使用的。
继续上面的例了,增加一个默认的管理器:
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
可以通过创建一个含有新的管理器并进行继承,来增加一个新的管理器,而不需要去改变更有的默认管理器。
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
Differences between proxy inheritance and unmanaged models
代理model看起来很像一个在Meta class中设置了manged的非托管模式model。但实际上这两种方案是不太一样,应该考虑在不同的情况下使用那一个:
两者区别在于:你可以设置model的Meta.managed=False以及通过Meta.db_table指定数据表有创建非托管模式model,并对其添加各种方法,但如果你可保持非托管模式和真实数据表之间的同步,做任何更改都将是很麻烦的事。
而代理model主要用于管理model的各种行为或方法,他们将继承父model的管理器等。
曾经尝试将两种模式合并,但由于API会变得非常复杂,并且难以理解,所以现在是分离成两种模式:
一般的使用规划是:
1、如果正使用现有的数据表,但不想在Django中镜像所有的列,所以应该使用Meta.managed=False,通过这个选项使不在django控制下的数据表或视图是可用的。
2、如果你想改变一个model的操作行为,但希望保持原始model不被改变,就应该使用Meta.proxy=True.
Multiple inheritance(多重继承)
和Python的继承方式一样,django中的model也可以从多个父model继承,当然也和Python的继承方式 一样,如果出现相同名字的时候只有第一个将被使用。例如:如果多个父model中都包含Meta类,将只有第一个将被使用,其他会被忽略。
通常情况下是不会用到多重继承的。主是用于“混合式”model:增加一个特殊的额外字段或方式是由多个父model组合而来。应该尽量保持继承层次的简单,不然会很难排查某个信息是从那里来的。
在django1.7以前,多个父model中有id主键字段时虽然不会引发错误,但有可能导致数据的丢失。例如像下面的model:
class Article(models.Model):
headline = models.CharField(max_length=50)
body = models.TextField()
class Book(models.Model):
title = models.CharField(max_length=50)
class BookReview(Book, Article):
pass
下面的使用方法演示了怎么用一个子对象来覆盖父对象的值:
>>> article = Article.objects.create(headline='Some piece of news.')
>>> review = BookReview.objects.create(
... headline='Review of Little Red Riding Hood.',
... title='Little Red Riding Hood')
>>>
>>> assert Article.objects.get(pk=article.pk).headline == article.headline
Traceback (most recent call last):
File "<console>", line 1, in <module>
AssertionError
>>> # the "Some piece of news." headline has been overwritten.
>>> Article.objects.get(pk=article.pk).headline
'Review of Little Red Riding Hood.'
要正确的使用多重继承,你应该使用一个明确的 AutoField 在父model中:
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
...
class BookReview(Book, Article):
pass
或者使用一个共同的父类来定义AutoField:
class Piece(models.Model):
pass
class Article(Piece):
...
class Book(Piece):
...
class BookReview(Book, Article):
pass
Field name “hiding” is not permitted
正常的Python类继承,允许一个子类覆盖父类的任何属性。在Django中是不允许覆盖父类的属性字段的。如果一个父类中定义了一个叫author的字段,你就不能在子model中创建别一个叫author的字段。
这种限制仅适用于字段(field),普通的python属性是可以的。也有一种情况是可以覆盖的:多表继承的时候进行手动指定数据库列名,可以出现子model和父model有同名的字段名称,因为他们属于不同的数据表。
如果你覆盖了父model的字段属性,django会抛出FieldError异常。
表操作的扩展阅读