Django 提供了自动管理界面。 这个特性是这样起作用的: 它读取你模式中的元数据,然后提供给你一个强大而且可以使用的界面,网站管理者可以用它立即工作。
建议你读这章,即使你不打算用admin。因为我们将介绍一些概念,这些概念可以应用到Django的所有方面,而不仅仅是admin。
将你的 Models 加入到 Admin 管理中
将自己的模块加入管理工具中,这样我们就能够通过这个漂亮的界面添加、修改和删除数据库中的对象了。 下面给出一个示例:
打开 app 目录下的 admin.py,注册 Models,示例代码如下:
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
admin.site.register(Publisher)
admin.site.register(Author)
admin.site.register(Book)
完成后,打开页面 http://127.0.0.1:8000/admin/,你会看到一个 “app名称” 区域,其中包含Authors、Books和Publishers。 (你可能需要重新启动服务(先关掉服务,然后再 runserver
),才能使其生效。)
Admin是如何工作的
当服务启动时,Django从 url.py 引导URLconf,然后执行 admin.autodiscover()
语句。 这个函数遍历INSTALLED_APPS配置,并且寻找相关的 admin.py文件。
如果在指定的app目录下找到admin.py,它就执行其中的代码。在 books 应用程序目录下的 admin.py 文件中,每次调用 admin.site.register() 都将那个模块注册到管理工具中。 管理工具只为那些明确注册了的模块显示一个编辑/修改的界面。
应用程序 django.contrib.auth 包含自身的 admin.py,所以Users和Groups能在管理工具中自动显示。
其它的django.contrib应用程序,如django.contrib.redirects,其它从网上下在的第三方Django应用程序一样,都会自行添加到管理工具。
综上所述,管理工具其实就是一个Django应用程序,包含自己的模块、模板、视图和URLpatterns。你要像添加自己的视图一样,把它添加到 URLconf 里面。 你可以在 Django 基本代码中的 django/contrib/admin 目录下,检查它的模板、视图和 URLpatterns,但你不要尝试直接修改其中的任何代码,因为里面有很多地方可以让你自定义管理工具的工作方式。(如果你确实想浏览Django管理工具的代码,请谨记它在读取关于模块的元数据过程中做了些不简单的工作,因此最好花些时间阅读和理解那些代码。)
设置字段可选
在摆弄了一会之后,你或许会发现管理工具有个限制:编辑表单需要你填写每一个字段,然而在有些情况下,你想要某些字段是可选的。
举个例子,我们想要Author模块中的email字段成为可选,即允许不填。 在现实世界中,你可能没有为每个作者登记邮箱地址。为了指定email字段为可选,你只要编辑Book模块,在 email 字段上加上 blank=True。代码如下:
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField( blank=True )
这些代码告诉 Django,作者的邮箱地址允许输入一个空值。 所有字段都默认 blank=False,这使得它们不允许输入空值。
设置日期型和数字型字段可选
虽然 blank=True 同样适用于日期型和数字型字段,但是这里需要详细讲解一些背景知识。
SQL 有指定空值的独特方式,它把空值叫做 NULL。NULL 可以表示为未知的、非法的、或其它程序指定的含义。
在SQL中,NULL 的值不同于空字符串,就像 Python 中 None 不同于空字符串(“”
)一样。这意味着某个字符型字段(如VARCHAR)的值不可能同时包含NULL和空字符串。
这会引起不必要的歧义或疑惑。 为什么这条记录有个NULL,而那条记录却有个空字符串? 它们之间有区别,还是数据输入不一致? 还有:我怎样才能得到全部拥有空值的记录,应该按NULL和空字符串查找么?还是仅按字符串查找?
为了消除歧义,Django 生成 CREATE TABLE 语句自动为每个字段显式加上 NOT NULL。
在大多数情况下,这种默认的行为对你的应用程序来说是最佳的,因为它可以使你不再因数据一致性而头痛。
而且它可以和 Django 的其它部分工作得很好。如在管理工具中,如果你留空一个字符型字段,它会为此插入一个空字符串(而不是NULL)。但是,其它数据类型有例外:日期型、时间型和数字型字段不接受空字符串。
如果你尝试将一个空字符串插入日期型或整数型字段,你可能会得到数据库返回的错误,这取决于那个数据库的类型。
在这种情况下,NULL 是唯一指定空值的方法。 在 Django 模块中,你可以通过添加 null=True 来指定一个字段允许为NULL。因此,这说起来有点复杂:如果你想允许一个日期型(DateField、TimeField、DateTimeField)或数字型(IntegerField、DecimalField、FloatField)字段为空,你需要使用 null=True 和 blank=True。
class Book(models.Model):
title = models.CharField( max_length=100 )
authors = models.ManyToManyField( Author )
publisher = models.ForeignKey( Publisher )
publication_date = models.DateField( blank=True, null=True )
添加 null=True 比添加 blank=True 复杂。因为 null=True 改变了数据的语义,即改变了 CREATE TABLE 语句,把
publication_date 字段上的 NOT NULL 删除了。 要完成这些改动,我们还需要更新数据库。
自定义字段标签
在编辑页面中,每个字段的标签都是从模块的字段名称生成的。规则很简单:
用空格替换下划线;首字母大写。例如:Book模块中publication_date的标签是Publication date。
然而,字段名称并不总是贴切的。有些情况下,你可能想自定义一个标签。 你只需在模块中指定verbose_name。
举个例子,说明如何将 Author.email 的标签改为 e-mail,中间有个横线。
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField(blank=True, verbose_name='e-mail' )
修改后重启服务器,你会在author编辑页面中看到这个新标签。
值得注意的是,为了使语法简洁,你可以把它当作固定位置的参数传递。 这个例子与上面那个的效果相同。
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField('e-mail', blank=True)
但这不适用于 ManyToManyField 和 ForeignKey 字段,因为它们第一个参数必须是模块类。那种情形,必须显式使用 verbose_name 这个参数名称。
自定义 ModelAdmi 类
迄今为止,我们做的 blank=True、null=True 和 verbose_name 修改其实是模块级别,而不是管理级别的。也就是说,这些修改实质上是构成模块的一部分,并且正好被管理工具使用,而不是专门针对管理工具的。除了这些,Django 还提供了大量选项让你针对特别的模块自定义管理工具。 这些选项都在 ModelAdmin classes 里面,这些类包含了管理工具中针对特别模块的配置。
自定义列表
我们为 Author 模块定义一个 ModelAdmin 类。 这个类是自定义管理工具的关键,其中最基本的一件事情是允许你指定列表中的字段。打开admin.py并修改:
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
admin.site.register(Publisher)
admin.site.register(Author, AuthorAdmin)
admin.site.register(Book)
解释一下代码:
- 我们新建了一个类 AuthorAdmin,它是从 django.contrib.admin.ModelAdmin 派生出来的子类,保存着一个类的自定义配置,以供管理工具使用。 我们只自定义了一项:list_display, 它是一个字段名称的元组,用于列表显示。 当然,这些字段名称必须是模块中有的。
- 我们修改了 admin.site.register() 调用,在 Author 后面添加了 AuthorAdmin。你可以这样理解: 用 AuthorAdmin 选项注册 Author 模块。
- admin.site.register() 函数接受一个 ModelAdmin 子类作为第二个参数。 如果你忽略第二个参数,Django 将使用默认的选项。Publisher 和 Book 的注册就属于这种情况。
弄好了这个东东,再刷新 author 列表页面,你会看到列表中有三列:姓氏、名字和邮箱地址。 另外,点击每个列的列头可以对那列进行排序。
接下来,让我们添加一个快速查询栏。 向 AuthorAdmin 追加 search_fields,如:
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
search_fields = ('first_name', 'last_name')
接下来,让我们为 Book 列表页添加一些过滤器。
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
search_fields = ('first_name', 'last_name')
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
admin.site.register(Publisher)
admin.site.register(Author, AuthorAdmin)
admin.site.register(Book, BookAdmin)
由于我们要处理一系列选项,因此我们创建了一个单独的 ModelAdmin 类:BookAdmin。首先,我们定义一个 list_display,以使得页面好看些。然后,我们用 list_filter 这个字段元组创建过滤器,它位于列表页面的右边。Django 为日期型字段提供了快捷过滤方式,它包含:今天、过往七天、当月和今年。这些是开发人员经常用到的。
另外一种过滤日期的方式是使用 date_hierarchy 选项,如:
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
请注意,date_hierarchy接受的是字符串 ,而不是元组。因为只能对一个日期型字段进行层次划分。
最后,让我们改变默认的排序方式,按publication date降序排列。 列表页面默认按照模块class
Meta(详见第五章)中的ordering所指的列排序。但目前没有指定ordering值,所以当前排序是没有定义的。
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
自定义编辑表单
正如自定义列表那样,编辑表单多方面也能自定义。
首先,我们先自定义字段顺序。 默认地,表单中的字段顺序是与模块中定义是一致的。 我们可以通过使用ModelAdmin子类中的fields选项来改变它:
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publisher', 'publication_date')
完成之后,编辑表单将按照指定的顺序显示各字段。 它看起来自然多了——作者排在书名之后。 字段顺序当然是与数据条目录入顺序有关, 每个表单都不一样。
另一个常用的编辑页面自定义是针对多对多字段的。 真如我们在book编辑页面看到的那样,多对多字段被展现成多选框。虽然多选框在逻辑上是最适合的HTML控件,但它却不那么好用。 如果你想选择多项,你必须还要按下 Ctrl 键(苹果机是 command 键)。
虽然管理工具因此添加了注释(help_text),但是当它有几百个选项时,它依然显得笨拙。更好的办法是使用 filter_horizontal。让我们把它添加到 BookAdmin 中,然后看看它的效果。
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
filter_horizontal = ('authors',)
用户、用户组和权限
因为你是用超级用户登录的,你可以创建,编辑和删除任何对像。 然而,不同的环境要求有不同的权限,系统不允许所有人都是超级用户。管理工具有一个用户权限系统,通过它你可以根据用户的需要来指定他们的权限,从而达到部分访问系统的目的。
用户帐号应该是通用的、独立于管理界面以外仍可以使用。但我们现在把它看作是管理界面的一部分。在第十四章,我们将讲述如何把用户帐号与你的网站(不仅仅是管理工具)集成在一起。
你通过管理界面编辑用户及其许可就像你编辑别的对象一样。 我们在本章的前面,浏览用户和用户组区域的时候已经见过这些了。如你所想,用户对象有标准的用户名、密码、邮箱地址和真实姓名,同时它还有关于使用管理界面的权限定义。 首先,这有一组三个布尔型标记:
- 活动标志,它用来控制用户是否已经激活。 如果一个用户帐号的这个标记是关闭状态,而用户又尝试用它登录时,即使密码正确,他也无法登录系统。
- 成员标志,它用来控制这个用户是否可以登录管理界面(即:这个用户是不是你们组织里的成员) 由于用户系统可以被用于控制公众页面(即:非管理页面)的访问权限(详见第十四章),这个标志可用来区分公众用户和管理用户。
- 超级用户标志,它赋予用户在管理界面中添加、修改和删除任何项目的权限。 如果一个用户帐号有这个标志,那么所有权限设置(即使没有)都会被忽略。
普通的活跃,非超级用户的管理用户可以根据一套设定好的许可进入。管理界面中每种可编辑的对象(如:books、authors、publishers)都有三种权限: 创建 许可, 编辑 许可和 删除 许可。给一个用户授权许可也就表明该用户可以进行许可描述的操作。
当你创建一个用户时,它没有任何权限,该有什么权限是由你决定的。 例如,你可以给一个用户添加和修改publishers的权限,而不给他删除的权限。请注意,这些权限是定义在模块级别上,而不是对象级别上的。据个例子,你可以让小强修改任何图书,但是不能让他仅修改由机械工业出版社出版的图书。后面这种基于对象级别的权限设置比较复杂,并且超出了本书的覆盖范围,但你可以在 Django documentation 中寻找答案。
何时、为什么使用管理界面?何时又不使用呢?
经过这一章的学习,你应该对 Django 管理工具有所认识。 但是我们需要表明一个观点:什么时候为什么用,以及什么时候又不用。
Django 的管理界面对非技术用户要输入他们的数据时特别有用;事实上这个特性就是专门为这个 实现的。
在 Django 最开始开发的新闻报道的行业应用中,有一个典型的在线自来水的水质专题报道 应用,它的实现流程是这样的:
- 负责这个报道的记者和要处理数据的开发者碰头,提供一些数据给开发者。
- 开发者围绕这些数据设计模型然后配置一个管理界面给记者。
- 记者检查管理界面,尽早指出缺少或多余的字段。 开发者来回地修改模块。
- 当模块认可后,记者就开始用管理界面输入数据。 同时,程序员可以专注于开发公众访问视图和模板(有趣的部分)。
换句话说,Django 的管理界面为内容输入人员和编程人员都提供了便利的工具。
当然,除了数据输入方面,我们发现管理界面在下面这些情景中也是很有用的:
- 检查模块* :当你定义好了若干个模块,在管理页面中把他们调出来然后输入一些虚假的数据,这是相当有用的。 有时候,它能显示数据建模的错误或者模块中其它问题。
- 管理既得数据* :如果你的应用程序依赖外部数据(来自用户输入或网络爬虫),管理界面提供了一个便捷的途径,让你检查和编辑那些数据。 你可以把它看作是一个功能不那么强大,但是很方便的数据库命令行工具。
- 临时的数据管理程序 :你可以用管理工具建立自己的轻量级数据管理程序,比如说开销记录。 如果你正在根据自己的,而不是公众的需要开发些什么,那么管理界面可以带给你很大的帮助。 从这个意义上讲,你可以把它看作是一个增强的关系型电子表格。