字段
一个模型最重要也是唯一必需的部分,是它定义的数据库字段。
字段名称限制
Django对模型的字段名做了两个限制
一个字段名不能是一个Python保留字,因为那样会导致一个Python语法错误,例如:
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
一个字段名不能包含连续的一个以上的下划线,因为那是Django查询语句的语法。例如:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
不过这些限制可以被绕过,因为字段名不一定要和数据库列名称完全相同。参见下面的 db_column 。
SQL保留字,像 join 、 where 或 select , 可以 用在模型字段名中,因为Django在每个SQL查询中,会对所有的数据库表名称和列名称进行转义。它会根据不同的数据库引擎的引用语法来进行相应的转义。
你的模型的每个字段应该是一个适当的 Field 类的实例,Django使用这个字段类的类型去确定如下内容:
下面是一个完整的按照字母排序的字段列表。注意关系字段( ForeignKey 等)会在下一节里说明。
AutoField
指一个能够根据可用ID自增的 IntegerField 。通常你不用直接使用它,如果你没有指定主键的话,系统会自动在你的模型中加入这样的主键。
BooleanField
一个真/假(true/false)字段。
CharField
一个字符串字段,适用于中小长度的字符串。对于长段的文字,请使用 TextField 。
CharField 有一个额外的必需参数: maxlength ,它是字段的最大长度(字符数)。这个最大长度在数据库层面和Django验证中是被强制要求的。
CommaSeparatedIntegerField
一个用逗号分隔开的整数字段。和 CharField 中一样, maxlength 参数是必需的。
DateField
日期字段。 DateField 有一些额外的可选参数,如表B-1所示。
表B-1. 额外的 DateField 选项
| Argument |
Description |
| auto_now |
每次对象保存时,自动设置为当前日期。一般用来产生最后一次修改时间。
注意:使用此选项的字段的值总是在保存时被设置为保存时的日期,这是无法改变的。
|
| auto_now_add |
当对象第一次产生时字段设置为当前日期。一般用来产生对象的建立时间。
注意:使用此选项的字段值总是在对象建立时被设置为建立时的日期,这是无法改变的。
|
DateTimeField
时间日期字段。接受跟 DateField 一样的额外选项。
EmailField
一个能检查值是否是有效的电子邮件地址的 CharField 。不接受 maxlength 参数,它的 maxlength 被自动设置为75。
FileField
一个文件上传字段。它有一个 必需的 参数,如表B-2所示。
表B-2. 额外的FileField选项
| 参数 |
描述 |
| upload_to |
一个本地的文件系统路径,被附加到你的 MEDIA_ROOT 设置后面,这决定了
get_<fieldname>_url() 辅助函数的输出 |
这个路径可以包含 strftime 格式串(参见 http://www.djangoproject.com/r/python/strftime/ ),文件上传时就会用当时的具体日期/时间替换(这样给定的目录就不会被上传的文件塞满了)。
在模型中使用 FileField 或者 ImageField 时,要有以下的步骤:
在settings文件中你需要定义 MEDIA_ROOT ,它就是你要保存上传文件的目录的全路径。(出于性能考虑,这些文件不会保存到数据库中。)还要定义 MEDIA_URL ,刚才那个目录的对外URL。你要确保网络服务器使用的用户对这个目录是可写入的。
在模型中添加 FileField 或者 ImageField ,务必要定义 upload_to 选项,这样Django才知道把上传的文件写到 MEDIA_ROOT 的哪个子目录中。
保存到数据库中的只有文件(相对于 MEDIA_ROOT )的路径。你很可能会使用Django提供的 get_<fieldname>_url 函数。例如,如果你的 ImageField 叫做 mug_shot 的话,你在模板中使用 {{ object.get_mug_shot_url }} 就会得到图片的绝对URL了。
例如,你的 MEDIA_ROOT 设置为 '/home/media' , upload_to 设置为
'photos/%Y/%m/%d' 。其中 '%Y/%m/%d' 部分是日期格式化串: '%Y' 为4位的年份, '%m' 是两位月份, '%d' 是两位的日期。如果你在2007年1月15日上传文件,这个文件就会被保存在 /home/media/photos/2007/01/15 目录下。
如果你想得到上传文件在磁盘上的文件名,或者指向该文件的URL,或者文件大小,你可以分别使用这些方法: get_FIELD_filename() 、 get_FIELD_url() 和 get_FIELD_size() 。附录C中有这些方法的详细解释。
备注
处理上传的文件时,为了避免安全漏洞,你应该总是密切注意上传文件的位置,以及文件的类型。 验证所有上传文件 ,确保文件内容是你所期望的。
例如,你未加验证地盲目的让某人上传文件,恰恰又上传到了网页服务器的根目录下的某个目录中,这个人就可以通过上传一个CGI或者PHP脚本并访问对应链接来执行那个脚本。不要让这种情况发生。
FilePathField
一个拥有若干可选项的字段,选项被限定为文件系统中某个目录下的文件名。它有3个特殊的参数,如表B-3所示。
表B-3. FilePathField的额外选项
| 参数 |
描述 |
| path |
必需 ;文件系统中一个目录的绝对路径, FilePathField 将从那个目录得到选项列表(比如:
"/home/images" )。 |
| match |
可选;一个正则表达式字符串, FilePathField 用它来过滤文件名。注意,这个正则表达式只作用
于基文件名,而不是全路径(例如: "foo.*\.txt^" 会匹配 foo23.txt ,但是不会匹配
bar.txt 或者 foo23.gif )。 |
| recursive |
可选; True 或者 False 。默认值为 False 。它指定是否把 path 的所有子目录都包
含进来。 |
当然,这些参数可以同时使用。
一个潜在的意料之外的东西就是 match 只作用于基文件名,而不是全路径。所以,看看这个例子:
FilePathField(path="/home/images", match="foo.*", recursive=True)
会匹配 /home/images/foo.gif ,但是不会匹配 /home/images/foo/bar.gif ,因为 match 只作用于基文件名( foo.gif 和 bar.gif )。
FloatField
一个浮点数,对应Python中的 float 实例。它有两个 必需 的参数,如表B-4所示。
表B-4. FloatField的额外选项
| 参数 |
描述 |
| max_digits |
数字中允许的最大的数字位数 |
| decimal_places |
数字的小数位数 |
例如,要保存最大值为999并且有两位小数的数字,应该这样写:
models.FloatField(..., max_digits=5, decimal_places=2)
要保存最大值为10亿并且带10个小数位的数字,要这样写:
models.FloatField(..., max_digits=19, decimal_places=10)
ImageField
像 FileField 一样,只不过要验证上传的对象是一个有效的图片。它有两个额外的可选参数: height_field 和 width_field ,如果设置了的话,每当模型实例被保存的时候,这两个值就会被设置成图片的高度和宽度。
FileField 中有一系列的 get_FIELD_* 方法,作为一种补充, ImageField 提供了 get_FIELD_height() 和 get_FIELD_width() 方法。附录C中有相关文档。
ImageField 依赖Python图片库( http://www.pythonware.com/products/pil/ )。
IPAddressField
一个IP地址,以字符串格式表示(例如: "24.124.1.30" )。
NullBooleanField
就像一个 BooleanField ,但它支持 None /Null 。尽量使用这个,而不要使用设置了 null=True 的 BooleanField 。
PhoneNumberField
它是一个 CharField ,并且会检查值是否是一个合法的美式电话格式,如(XXX-XXX-XXXX)。
备注
如果你需要表示一个其他国家的电话号码,检查 django.contrib.localflavor
包,看看是否包括对应你的国家的字段定义。
PositiveIntegerField
和 IntegerField 类似,但必须是正值。
PositiveSmallIntegerField
与 PositiveIntegerField 类似,但只允许小于一定值的值。最大值取决于数据库,但因为数据库有一个2-byte的小整数字段,最大的小整数正值一般都是65,535。
SlugField
嵌条是报纸业的术语。 嵌条 就是一段内容的简短标签,这段内容只能包含字母、数字、下划线或连字符。通常用于URL中。
像 CharField 一样,你可以指定 maxlength 。如果没有指定 maxlength ,Django将使用默认值50。
由于嵌条主要用于数据库查找,所以 SlugField 默认的就有 db_index=True 。
SlugField 接受一个额外的选项: prepopulate_from ,它是一些字段的列表,而这些字段将在对象管理表单中通过JavaScript生成嵌条。
models.SlugField(prepopulate_fpom=("pre_name", "name"))
prepopulate_from 不接受 DateTimeField 字段的名字作为参数。
SmallIntegerField
和 IntegerField 类似,但是只允许在一个数据库相关的范围内的数值(通常是-32,768到+32,767)。
TimeField
时分秒的时间显示。它接受的可指定参数与 DateField 和 DateTimeField 相同。
URLField
用来存储URL的字段。如果 verify_exists 选项被设置为 True (默认),给出的URL就会被检测是否存在(例如:这个URL的确被加载并且没有给出一个404响应)。
和其他字符字段一样, URLField 接受 maxlength 参数。如果你没有指定 maxlength ,则使用默认值200。
USStateField
美国州名称缩写,两个字母。
备注
如果你需要表示其他的国家或地区,查看一下 django.contrib.localflavor 包,看看Django是否已经包含了对应你本地的字段。
通用字段选项
所有的字段类型都可以使用下面的参数。所有的都是可选的。
null
如果设置为 True 的话,Django将在数据库中存储空值为 NULL 。默认为 False 。
记住,空字符串值保存时总是以空字符串的形式存在,而不是 NULL 。一般只对非字符串字段使用 null=True ,比如整型、布尔型和日期型。对于这两种字段,如果你允许表单中的对应值为空的话,你还需要设定 blank=True ,因为 null 参数只影响数据库存储(参见下面题为blank的一节)。
如果没有充分理由的话,应该尽量避免对诸如 CharField 和 TextField 这样字符串字段使用 null 参数。如果对字符串字段指定了 null=True 的话,这意味着空数据有两种可能的值: NULL 和空字符串。而大多数情况下,空数据没必要对应两种可能的值,所以Django中习惯使用空字符串,而不是 NULL 。
blank
如果是 True ,该字段允许留空,默认为 False 。
注意这与 null 不同, null 完全是数据相关的,而 blank 是用来做验证的。如果一个字段设置了 blank=True ,在Django的管理界面会允许该字段留空,如果设置了 blank=False ,那么这就是一个必填字段。
choices
一个包含双元素元组的可迭代的对象,用于给字段提供选项。
如果指定了这个选项,Django管理界面不会使用标准的文本框了,而是取而代之,使用列表选择框限定选择范围。
下面就是一个选项列表:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素是实际存储的值,第二个元素是用于显示给用户的选项。
选项列表既可以作为模型类的一部分来定义:
class Foo(models.Model):
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
也可以定义到模型类的外面:
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
class Foo(models.Model):
gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
对于设定了 choices 选项的模型字段,Django会添加一个方法,来获取字段当前值对应的用户可读文本。详见附录C。
db_column
当前字段在数据库中对应的列的名字。如果没有指定的话,Django会使用这个字段的名字。当你要定义一个数据库中存在命名冲突的模型时,这个选项非常有用。
如果你指定的数据库列名称是SQL的保留字,或者名称中包含Python变量名不允许的字符(就是连字符),没问题,Django会悄悄地把列名或者表名用引号引起来。
db_index
如果为 True ,Django会在创建表格(比如运行 manage.py syncdb )时对这一列创建数据库索引。
editable
如果为 False ,这个字段在管理界面或表单里将不能编辑。默认为 True 。
help_text
在管理界面表单对象里显示在字段下面的额外帮助文本。即使你没有管理表单这个属性对文档也是有用的。
primary_key
如果为 True ,这个字段就会成为模型的主键。
如果你没有对模型中的任何字段指定 primary_key=True 的话,Django会自动添加这个字段:
id = models.AutoField('ID', primary_key=True)
所以,如果你不想覆盖默认的主键行为的话,你就不必对任何字段设定 primary_key=True 。
primary_key=True 就意味着 blank=False 、 null=False 和 unique=True 。一个对象只能有一个主键。
radio_admin
默认地,对于 ForeignKey 或者拥有 choices 设置的字段,Django管理界面会使用列表选择框(<select>)。如果 radio_admin 设置为 True 的话,Django就会使用单选按钮界面。
如果字段不是 ForeignKey 或者没有 choices 设置的话,就不要对字段只用这个选项。
unique
如果是 True ,这个字段的值在整个表中必须是唯一的。
unique_for_date
把它的值设成一个 DateField 或者 DateTimeField 的字段的名称,可以确保字段在这个日期内不会出现重复值,例如:
class Story(models.Model):
pub_date = models.DateTimeField()
slug = models.SlugField(unique_for_date="pub_date")
...
在上面的代码中,Django不会允许在同一个日期发表两个嵌条相同的故事。和使用 unique_together 不同的是,它只考虑 pub_date 字段的日期,而忽略掉时间差异。
unique_for_month
和 unique_for_date 类似,只是要求字段在指定字段的月份内唯一。
unique_for_year
和 unique_for_date 及 unique_for_month 类似,只是时间范围变成了一年。
verbose_name
除 ForeignKey 、 ManyToManyField 和 OneToOneField 之外的字段都接受一个详细名称作为第一个位置参数。如果详细名称没有给定的话,Django会把字段的属性名中的下划线转化成空格后的字符串当作详细名称。
下面的例子中,详细名称是 "Person's first name" :
first_name = models.CharField("Person's first name", maxlength=30)
下面的例子中,详细名称是 "first name" :
first_name = models.CharField(maxlength=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会根据需求自动大写首字母。
关系
很明显,关系数据库的强大在于表与表之间的相互关联关系,Django提供定义了三种最为通用的数库库关系类型:many-to-one(多对一关系),many-to-many(多对多关系)和one-to-one(一对一关系)
对于一对一关系,在本书出版时正在被重新审阅,因此本章没有涉及这一点,你可以从在线文档中获取最新信息。
多对一关系
用 ForeignKey 来定义多对一的关系。用法和其他的 Field 是一样的,把它放到模型中类的属性定义中就行了。
ForeignKey 需要一个与之相关联的类作为位置参数。
例如,一个 Car 模型中有个 Manufacturer ,就是说一个 Manufacturer 可以生产很多汽车,但是每个 Car 只能有一个 Manufacturer ,可以这样定义:
class Manufacturer(models.Model):
...
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
...
要建立一个 递归 的关系——就是一个对象和自身有多对一的关系——可以这样写: models.ForeignKey('self') :
class Employee(models.Model):
manager = models.ForeignKey('self')
如果你创建关系时,所需的模型还没有被定义,你可以不使用模型对象本身,而是使用那个模型的名字。
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
...
class Manufacturer(models.Model):
...
但是,你要记住,只能对在同一个 models.py 文件中的模型使用字符串引用,对于其他应用程序中的模型或者从其他地方导入的模型是不能使用名字对其做引用的。
Django在数据库中使用的列名称是对应的字段的名称后追加 _id 得到的字符串。再前面的那个例子中, Car 模型对应的数据库表中会有一个名字是 manufacturer_id 的列,(你可以通过指定 db_column 来显式改变这个名字,参见前面的db_column一节)但是,如果你不需要写定制的SQL语句的话,你永远不要去处理数据库列名,只需要处理你的模型对象中的字段名称。
建议你使用模型的名字的小写形式作为 ForeignKey 字段的名字(上个例子中的 manufacturer ),但这不是必须的,你当然可以任意命名,例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
# ...
为了定义关系的细节, ForeignKey 字段接受很多额外的参数(参见表B-5)。所有的参数都是可选的。
表B-5. ForeignKey选项
| 选项 |
描述 |
| edit_inline |
如果不设为 False 的话,它对应的对象就可以在页面上内联编辑,就是说这个对象有自
己独立的管理界面。如果设为 models.TABULAR 或者 models.STACKED 的话,这个内
联编辑对象分别显示成一个表格或者一些字段的集合。 |
| limit_choices_to |
可以限定对象的值的范围的一个参数和值的字典(参见附录C)。结合Python的 datetime
模块的函数可以根据日期来限定对象。例如,下面的代码:
limit_choices_to = {'pub_date__lte': datetime.now}
把可选对象限定到 pub_date 早于当前时间的对象中。
除字典外,这里也可以是一个可以执行更复杂的查询的 Q 对象(参见附录C)。
这个选项和 edit_inline 是不兼容的。
|
| max_num_in_admin |
对于内联编辑对象,这个是要在管理界面里显示的相关对象的最多个数。所以,如果披萨最多
只会有10种配料, max_num_in_admin=10 会保证用户最多输入10种配料。
记住,本项并不保证不会创建10种以上的配料,他只是控制管理界面,而不是在Python的API
层和数据库层做什么限制。
|
| min_num_in_admin |
在管理界面中要显示的相关的对象的最少个数。通常,在创建的时候,显示的内联对象的个数
为 num_in_admin 个,在编辑的时候,在当前的基础上又会多显示
num_extra_on_change 个空对象,但是显示的对象个数不会少于 min_num_in_admin
个。 |
| num_extra_on_change |
修改对象时要额外显示的对象数目。 |
| num_in_admin |
添加对象时要显示的内联对象的默认个数。 |
| raw_id_admin |
为要键入的整数显示一个文本框,而不是一个下拉列表。在关联对象有很多行时,这个比显示
一个列表选择框更实用。
使用 edit_inline 时,本项无效。
|
| related_name |
关联对象反向引用描述符。更多信息参见附录C。 |
| to_field |
关联对象的用于关联的字段,Django默认使用关联对象的主键。 |
多对多关系
用 ManyToManyField 来定义多对多的关系。像 ForeignKey 一样, ManyToManyField 需要一个与之相关联的类作为位置参数。
例如,一个 Pizza 可以有多种 Topping 对象——就是说一种 Topping 可以用在多个披萨上面,同时一个 Pizza 可以有多种配料——你可以这样写:
class Topping(models.Model):
...
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
...
像 ForeignKey 一样,和自身的关系可以通过字符串 'self' 来定义,而不用模型名。对于那些尚未定义的模型,你也可以通过模型名字来引用。但是,你只能对在同一个 models.py 文件中的模型使用字符串引用,对于其他应用程序中的模型或者从其他地方导入的模型是不能使用名字对其做引用的。
建议你描述想过模型对象集时,用复数名词作为 ManyToManyField 的名字,但这并不是必须的。
Django会在后台建立一个起桥梁作用的表来描述多对多关系。
对于有多对多关系的两个模型, ManyToManyField 存在于哪个模型中并不重要,但只能存在于一个模型中。
如果你是用管理界面的话, ManyToManyField 实例应该放到要在管理界面编辑的对象里。在前面的例子里,是 toppings 位于 Pizza 中的(而不是 Topping 中有一个名为 pizzas 的 ManyToManyField ),因为这样比一种配料放到多个披萨中更符合人们的思维习惯。在这个例子中,用户可以在 Pizza 的管理界面中选择配料。
为了定义关系细节, ManyToManyField 对象接受几个额外的参数(参见表B-6),这些参数都是可选的。
表B-6. ManyToManyField选项
| 参数 |
描述 |
| related_name |
关联对象反向引用描述符。更多信息参见附录C。 |
| filter_interface |
在这个对象的管理界面里面,使用简单易用的JavaScript过滤界面,而不是使用可用性较差的
<select multiple> 。它的值应该是 models.HORIZONTAL 或者 models.VERTICAL
(就是说界面应该横放还是竖放)。 |
| limit_choices_to |
参见 ForeignKey 中对本项的描述。 |
| symmetrical |
仅用于模型定义指向自身的 ManyToManyField 的情况。看下面这个模型:
class Person(models.Model):
friends = models.ManyToManyField("self")
当Django处理这个模型时,会发现它有一个指向自身的 ManyToManyField ,它因此就不会在
Person 类中添加 person_set 属性。而对于 ManyToManyField ,我们会假定这种
关系是对称的,就是说,如果我是你的朋友,你也是我的朋友。
在对 self 的 ManyToMany 关系中,如果你不需要这种对称性,你可以把
symmetrical 的值设为 False 。这样就会强制Django给关系的另外一方添加描述符,
从而使这种关系不是对称的。
|
| db_table |
用来保存多对多数据的表的名字。如果没有提供本项的话,Django会把两个表的名字连接起来当
做多对多数据表的默认名字。 |
模型的Metadata选项
在模型类中定义一个 class Meta ,然后可以在其中指定本模型特有的metadata:
class Book(models.Model):
title = models.CharField(maxlength=100)
class Meta:
# model metadata options go here
...
Model metadata是任何非字段的属性,比如排序选项等等
下面的章节列出了所有可能的 Meta 选项,它们都是可选的,连 class Meta 都不是模型必需的。
db_table
模型对应的数据库表的名字。
为了节省时间,Django通过你定义的模型的类名和所在的应用程序的名称自动得到数据库的表名,它是由模型的应用程序名称——就是你执行 manage.py startapp 命令所指定的应用程序的名称——和模型的类名组成的,它们之间通过下划线进行连接。
例如,假设你有一个应用程序: books (通过执行 manage.py startapp books 命令创建的),又定义了一个模型: class Book ,那么这个模型对应的默认的数据库表名应该为 books_book 。
通过复写 class Meta 中的 db_table 参数可以改变模型映射的数据库表名:
class Book(models.Model):
...
class Meta:
db_table = 'things_to_read'
如果没有指定该选项的话,Django会使用: app_label + '_' + model_class_name 。参见“表名”一节。(译注:这一节不知何故,并未在本章中出现,英文原文可参见: http://docs.djangoproject.com/en/dev/ref/models/options/#table-names )
如果你指定的数据库表名是SQL的保留字,或者名称中包含Python变量名不允许的字符(就是连字符),没问题,Django会悄悄地把列名或者表名用引号引起来。
get_latest_by
模型中的一个 DateField 或者 DateTimeField 字段的名字,它指明模型 Manager 的 latest() 方法使用的默认字段。
示例如下:
class CustomerOrder(models.Model):
order_date = models.DateTimeField()
...
class Meta:
get_latest_by = "order_date"
关于 latest() 方法的更多信息可参考附录C。
order_with_respect_to
标识这个对象可以根据指定字段排序,这个主要用于相互关联的对象,让他们可以按照和父对象相关的方式排序。例如,如果 Answer 和 Question 对象相关,同一个问题可能对应多个答案,如果答案顺序很重要的话,你应该这样做:
class Answer(models.Model):
question = models.ForeignKey(Question)
# ...
class Meta:
order_with_respect_to = 'question'
ordering
对象默认的排序方法,获取对象列表时会用到。
class Book(models.Model):
title = models.CharField(maxlength=100)
class Meta:
ordering = ['title']
它就是一个字符串的元组或列表。字符串是一个有可选前缀 - 的字段名,这个前缀表示降序排列。没有前缀 - 则表示升序排列。使用字符串 "?" 可进行随机排序。
例如,要以 title 字段做升序排列(也就是A-Z),要这样写:
ordering = ['title']
要以 title 做降序排列(也就是Z-A),这样写:
ordering = ['-title']
要先以 title 做降序排列,再以 author 做升序排列,就这样写:
ordering = ['-title', 'author']
注意,无论 ordering 中有多少字段,admin界面只使用第一个字段。
permissions
创建对象时,需要额外加入权限表的权限。对于设置了 admin 选项的对象,添加、删除和修改的权限在创建对象时会自动创建。下面的例子指定了一个附加的权限: can_deliver_pizzas :
class Employee(models.Model):
...
class Meta:
permissions = (
("can_deliver_pizzas", "Can deliver pizzas"),
)
它是一个形如 (permission_code, human_readable_permission_name) 的元组的列表。
有关权限的更多信息参见第12章。
unique_together
组合在一起的一些字段的名字,这些字段的组合值必须是唯一的:
class Employee(models.Model):
department = models.ForeignKey(Department)
extension = models.CharField(maxlength=10)
...
class Meta:
unique_together = [("department", "extension")]
这是一个由一些字段列表组成的列表,每个列表里的字段的组合值必须是唯一的。它用于Django管理界面,而且在数据库层是强制要求的(就是说在 CREATE TABLE 语句中会包含一些 UNIQUE 语句)。
verbose_name
对象的友好可读名称(单数形式):
class CustomerOrder(models.Model):
order_date = models.DateTimeField()
...
class Meta:
verbose_name = "order"
如果没有给出此选项,那么Django将会根据类名来得到一个名称,例如 CamelCase 就会变成 camel case 。
verbose_name_plural
对象复数形式的名字:
class Sphynx(models.Model):
...
class Meta:
verbose_name_plural = "sphynges"
如果此选项没有指定,则Django会在 verbose_name 后面加上个s来作为此选项的默认值。
模型方法
自定义model的方法可以为你的model对象提供行级的操作功能。Whereas Manager
methods are intended to do tablewide things, model methods should act on a particular model instance.
这是一个很有价值的技术,它有利于你把业务逻辑统一放到一个地方,这个地方就是:model. 例如:model中有一些自定义的方法。
class Person(models.Model):
first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
birth_date = models.DateField()
address = models.CharField(maxlength=100)
city = models.CharField(maxlength=50)
state = models.USStateField() # Yes, this is America-centric...
def baby_boomer_status(self):
"""Returns the person's baby-boomer status."""
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
return "Post-boomer"
def is_midwestern(self):
"""Returns True if this person is from the Midwest."""
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
@property
def full_name(self):
"""Returns the person's full name."""
return '%s %s' % (self.first_name, self.last_name)
最後一個方法,在這個例子,是屬性,一個特性由客製的gtter/setter使用者的源碼來實作。
屬性是一個重要的技巧,被加入在Python 2.2;你可以讀到更多關於這個屬性,在此http://www.python.org/download/releases/2.2/descrintro/#property`_.
還有一堆手動的模型方法,對Python or Django有特別的意義。
這些方法在下面的段落裏會描述。
__str__
__str__()是一個Python 魔術的方法,它定義了,什麼東名必須被回傳假如你呼叫str()在這個物件。Django 使用str(obj)(或是相對應的函數,unicode(obj),簡短地描述),在很多地方,最有名,是這秀出來的值被這個物件重繪在Django 管理站,而且被寫入樣板的值,當它展示一個物件。因此,你必須總是返回一個好的,人類可讀的字串,對這個物件__str__。即使這不是必須的,它還是強列地被鼓勵 。
這裏是一個範例:
class Person(models.Model):
first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
get_absolute_url
可以通过定义 get_absolute_url() 方法来告诉Django怎样得到一个对象的URL,例如:
def get_absolute_url(self):
return "/people/%i/" % self.id
Django 使用這個在它的管理介面。假如一個物件定義了get_absolute_url(),這物件編輯的頁面將有一個視圖在站上的聯結獎會帶領你直接地往這個物件的公開的視圖,根據get_absolute_url()
另外,一堆Django其他的小地方,如syndication-feed框架,使用get_absolute_url(),當做一個方便的提醒人們已定義過這個方法。
相对于写死了你的对象所处于的URL来讲,在模板中使用 get_absolute_url() 是一个很好的习惯。例如,下面这个例子的写法是很不好:
<a href="/people/{{ object.id }}/">{{ object.name }}</a>
但这段模板代码就很优雅:
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
當我們這樣用,只寫get_absolute_url(),問題是,它輕微的破壞DRY原理:這物件的URL被同時定義在 URLconf 檔和模型裏。
你可以進一步的解構你的模型,從URLconf ,使用permalink裝飾詞。這decorator 被傳入一個view 函數,一系列的定位參數,而且(可選擇的)一個字典叫做命名的參數。Django 可以正確的走完整的URL 路徑,藉著URLconf,替代你已經給URL的參數。例如,假如你的URLconf 擁有一行,如下:
(r'^people/(\d+)/$', 'people.views.details'),
你的模型里的 get_absolute_url 方法可以像下面这样写:
@models.permalink
def get_absolute_url(self):
return ('people.views.details', [str(self.id)])
与此类似,如果有这样的一个URLconf:
(r'/archive/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', archive_view)
你可以像下面那样用permalink()来引用这个链接:
@models.permalink
def get_absolute_url(self):
return ('archive_view', (), {
'year': self.created.year,
'month': self.created.month,
'day': self.created.day})
注意,我們指出一個空的序列,在這種案例的第二個參數,因為我們想要傳入只有關鍵字的參數,不是已命名的參數。
用這個方法,你可以試著用模型的絕對URL ,指向view ,被用來展示它,
而不要在任何地方重覆URL 訊息。你仍然可以使用get_absolute_url方法,在樣板裏,就像之前一樣
执行定制的SQL
請自在的寫客製的SQL 句子在客製的模型方法,和模組層級的方法。
這個物件,django.db.connection展示了現行的資料庫連接。要使用它,直接呼叫connection.cursor()來獲得一個cursor 物件。然後,呼叫cursor.execute(sql, [params])來執行一段SQL,和cursor.fetchone()或是cursor.fetchall()來傳回結果資料列:
def my_custom_sql(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
connection and cursor最常被實作,在標準的 Python DB-API
(http://www.python.org/peps/pep-0249.html).假如你還不夠熟悉Python DB-API,注意在cursor.execute()SQL 句子。使用placeholders,%s,甚於直接在SQL裏加參數。假如你使用這個技巧,底下的資料庫函式庫將自動的加上引號給你的參數,視需要而定。(也注意Django 期待%splaceholder而不是?placeholder,後者用於SQLite Python連接。這是為了穩定性的考量。
最後的註記:假如你只是想要使用一個客製的WHERE語句,你可以只用where,tables,params參數給這個標準的lookup API.請看附錄C.
覆盖默认的Model方法
如附錄C所解釋的,每一個模型自動獲得少數的方法,有名的,如save()和delete()。你可以覆寫這些方法來改變行為。
一個古老的使用者案例:覆寫內建的方法,假如你想要一些事情發生,當你要存入一個物件,例如:
class Blog(models.Model):
name = models.CharField(maxlength=100)
tagline = models.TextField()
def save(self):
do_something()
super(Blog, self).save() # Call the "real" save() method.
do_something_else()
你也可以阻止保存:
class Blog(models.Model):
name = models.CharField(maxlength=100)
tagline = models.TextField()
def save(self):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super(Blog, self).save() # Call the "real" save() method
Admin选项
Admin 类告诉Django如何在管理站点中显示模块。
以下的段落展示了一系列的所有可能的Admin選項。沒有一個選項是必選的。為了使用一個管理者介面,而不要指定任何選項,請用pass,像這樣:
class Admin:
pass
在模型中是否需要加入Admin类完全是可选的
date_hierarchy
設定date_hierarchy到你模型裏的DateField或是DateTimeField,然後修改清單的頁面將會包含一個使用這個欄位的date-based的瀏覽
這裏有個例子:
class CustomerOrder(models.Model):
order_date = models.DateTimeField()
...
class Admin:
date_hierarchy = "order_date"
fields
設定fields來控制管理畫面的輸出,加上,修改頁面
欄位,是一個非常複雜巢狀的資料結構 ,可以很好的被一個範例展示。以下是擷取自FlatPage模型,這是django.contrib.flatpages的一部分
class FlatPage(models.Model):
...
class Admin:
fields = (
(None, {
'fields': ('url', 'title', 'content', 'sites')
}),
('Advanced options', {
'classes': 'collapse',
'fields' : ('enable_comments', 'registration_required', 'template_name')
}),
)
型式上,fields是一個串列:由兩個tuples組成,在其中,每一組two-tuple呈現一個<fieldset>在admin 表單頁。一個<fieldset>是表單的一個段落。
這個two-tuples都用這種型式,(name, field_options),在這裏,name是一個字串表示是fieldset 的名稱,而field_options是資訊字典,fieldset,包含一系列要展示的欄位。
If fields isnt given, Django will default to displaying each field that isnt an AutoField and has
editable=True , in a single fieldset, in the same order as the fields are defined in the model.
“field_options”字典拥有将在下面的章节中描述的关键字。
fields
给字段的元组命名,以方便显示在这个字段集中。这个关键字是必需的。
在一行里面显示多个字段,并且将这些字段装在它们自己的元组里。在这个例子里,“first_name”和“last_name”字段将显示在同一行中。
'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
classes
A string containing extra CSS classes to apply to the fieldset.
通过空格分开已使用多个类:
'classes': 'wide extrapretty',
Two useful classes defined by the default admin site stylesheet are collapse and wide . Fieldsets
with the collapse style will be initially collapsed in the admin site and replaced with a small click to
expand link. Fieldsets with the wide style will be given extra horizontal space.
description
A string of optional extra text to be displayed at the top of each fieldset, under the heading of the
fieldset. Its used verbatim, so you can use any HTML and you must escape any special HTML characters (such
as ampersands) yourself.
js
A list of strings representing URLs of JavaScript files to link into the admin screen via <script
src=""> tags. This can be used to tweak a given type of admin page in JavaScript or to provide quick links
to fill in default values for certain fields.
If you use relative URLs that is, URLs that dont start with http:// or / then the admin site will
automatically prefix these links with settings.ADMIN_MEDIA_PREFIX .
list_display
Set list_display to control which fields are displayed on the change list page of the admin.
If you dont set list_display , the admin site will display a single column that displays the
__str__() representation of each object.
这里是一些专门关于``list_display``的例子:
If the field is a ForeignKey , Django will display the __str__() of the related object.
ManyToManyField fields arent supported, because that would entail executing a separate SQL statement
for each row in the table. If you want to do this nonetheless, give your model a custom method, and add
that methods name to list_display . (More information on custom methods in list_display
shortly.)
If the field is a BooleanField or NullBooleanField , Django will display a pretty on or off icon
instead of True or False .
If the string given is a method of the model, Django will call it and display the output. This method
should have a short_description function attribute, for use as the header for the field.
以下是一个完整的例子模型:
class Person(models.Model):
name = models.CharField(maxlength=50)
birthday = models.DateField()
class Admin:
list_display = ('name', 'decade_born_in')
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
If the string given is a method of the model, Django will HTML-escape the output by default. If youd
rather not escape the output of the method, give the method an allow_tags attribute whose value is
True .
以下是一个完整的例子模型:
class Person(models.Model):
first_name = models.CharField(maxlength=50)
last_name = models.CharField(maxlength=50)
color_code = models.CharField(maxlength=6)
class Admin:
list_display = ('first_name', 'last_name', 'colored_name')
def colored_name(self):
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
colored_name.allow_tags = True
If the string given is a method of the model that returns True or False , Django will display a
pretty on or off icon if you give the method a boolean attribute whose value is True .
以下是一个完整的例子模型:
class Person(models.Model):
first_name = models.CharField(maxlength=50)
birthday = models.DateField()
class Admin:
list_display = ('name', 'born_in_fifties')
def born_in_fifties(self):
return self.birthday.strftime('%Y')[:3] == 5
born_in_fifties.boolean = True
The __str__() methods are just as valid in list_display as any other model method, so its
perfectly OK to do this:
list_display = ('__str__', 'some_other_field')
Usually, elements of list_display that arent actual database fields cant be used in sorting (because
Django does all the sorting at the database level).
However, if an element of list_display represents a certain database field, you can indicate this
fact by setting the admin_order_field attribute of the item, for example:
class Person(models.Model):
first_name = models.CharField(maxlength=50)
color_code = models.CharField(maxlength=6)
class Admin:
list_display = ('first_name', 'colored_first_name')
def colored_first_name(self):
return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
colored_first_name.allow_tags = True
colored_first_name.admin_order_field = 'first_name'
The preceding code will tell Django to order by the first_name field when trying to sort by
colored_first_name in the admin site.
list_display_links
Set list_display_links to control which fields in list_display should be linked to the change page
for an object.
By default, the change list page will link the first column the first field specified in list_display to
the change page for each item. But list_display_links lets you change which columns are linked. Set
list_display_links to a list or tuple of field names (in the same format as list_display ) to link.
list_display_links can specify one or many field names. As long as the field names appear in
list_display , Django doesnt care how many (or how few) fields are linked. The only requirement is that
if you want to use list_display_links , you must define list_display .
在这个例子中,“first_name”和“last_name”将被链接到更改列表页
class Person(models.Model):
...
class Admin:
list_display = ('first_name', 'last_name', 'birthday')
list_display_links = ('first_name', 'last_name')
Finally, note that in order to use list_display_links , you must define list_display , too.
list_filter
Set list_filter to activate filters in the right sidebar of the change list page of the admin interface.
This should be a list of field names, and each specified field should be either a BooleanField ,
DateField , DateTimeField , or ForeignKey .
This example, taken from the django.contrib.auth.models.User model, shows how both list_display and
list_filter work:
class User(models.Model):
...
class Admin:
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_filter = ('is_staff', 'is_superuser')
list_per_page
Set list_per_page to control how many items appear on each paginated admin change list page. By default,
this is set to 100 .
ordering
Set ordering to specify how objects on the admin change list page should be ordered. This should be a
list or tuple in the same format as a models ordering parameter.
If this isnt provided, the Django admin interface will use the models default ordering.
save_as
Set save_as to True to enable a save as feature on admin change forms.
Normally, objects have three save options: Save, Save and continue editing, and Save and add another. If
save_as is True , Save and add another will be replaced by a Save as button.
Save as means the object will be saved as a new object (with a new ID), rather than the old object.
默认情况下,“save_as”设置成“False”
save_on_top
Set save_on_top to add save buttons across the top of your admin change forms.
Normally, the save buttons appear only at the bottom of the forms. If you set save_on_top , the buttons
will appear both on the top and the bottom.
默认情况下,“save_on_top”设置成“False”
search_fields
Set search_fields to enable a search box on the admin change list page. This should be set to a list of
field names that will be searched whenever somebody submits a search query in that text box.
These fields should be some kind of text field, such as CharField or TextField . You can also
perform a related lookup on a ForeignKey with the lookup API follow notation:
class Employee(models.Model):
department = models.ForeignKey(Department)
...
class Admin:
search_fields = ['department__name']
When somebody does a search in the admin search box, Django splits the search query into words and returns
all objects that contain each of the words, case insensitive, where each word must be in at least one of
search_fields . For example, if search_fields is set to ['first_name', 'last_name'] and a user
searches for john lennon , Django will do the equivalent of this SQL WHERE clause:
WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')
For faster and/or more restrictive searches, prefix the field name with an operator, as shown in Table B-7.
表B-7. search_fields中允许使用的操作符
| 操作符 |
含义 |
| ^ |
匹配字段的开头。例如,把 search_fields 设置成 ['^first_name', '^last_name'] ,当用户搜索
john lennon 时,Django相当于执行了这样的SQL WHERE 语句:
WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%')
AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')
这个查询要比执行普通的 '%john%' 查询效率高,因为数据库只需要检查每一列数据的开头,而不用把整
列数据都扫一遍。此外,如果有针对这一列的索引的话,某些数据库可能会在查询中使用索引,即使它是一个
LIKE 查询。
|
| = |
精确匹配,不区分大小写。例如,把 search_fields 设置成 ['=first_name', '=last_name'] ,当
用户搜索 john lennon 时,Django相当于执行了这样的SQL WHERE 语句:
WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john')
AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')
记住,搜索输入是靠空格来分隔的,所以,在这个例子中还不可能找出 first_name 恰恰是
'john winston' (中间有空格)的所有记录。
|
| @ |
执行全文匹配。这个和默认的搜索方法类似,但是它使用索引。目前只在MySQL中可用。 |
关于本评注系统
本站使用上下文关联的评注系统来收集反馈信息。不同于一般对整章做评注的做法, 我们允许你对每一个独立的“文本块”做评注。一个“文本块”看起来是这样的:
一个“文本块”是一个段落,一个列表项,一段代码,或者其他一小段内容。 你选中它会高亮度显示:
要对文本块做评注,你只需要点击它旁边的标识块:
我们会仔细阅读每个评论,如果可能的话我们也会把评注考虑到未来的版本中去:
如果你愿意你的评注被采用,请确保留下你的全名 (注意不是昵称或简称)
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.extlibrary. Thanks also to Yahoo for YUI itself.