对于某一类网站, 管理界面 是基础设施中非常重要的一部分。 这是以网页和有限的可信任管理者为基础的界面,它可以让你添加,编辑和删除网站内容。 一些常见的例子: 你可以用这个界面发布博客,后台的网站管理者用它来润色读者提交的内容,你的客户用你给他们建立的界面工具更新新闻并发布在网站上,这些都是使用管理界面的例子。
但是管理界面有一问题: 创建它太繁琐。 当你开发对公众的功能时,网页开发是有趣的,但是创建管理界面通常是千篇一律的。 你必须认证用户,显示并管理表格,验证输入的有效性诸如此类。 这很繁琐而且是重复劳动。
Django 在对这些繁琐和重复的工作进行了哪些改进? 它用不能再少的代码为你做了所有的一切。 Django 中创建管理界面已经不是问题。
这一章是关于 Django 的自动管理界面。 这个特性是这样起作用的: 它读取你模式中的元数据,然后提供给你一个强大而且可以使用的界面,网站管理者可以用它立即工作。
请注意我们建议你读这章,即使你不打算用admin。因为我们将介绍一些概念,这些概念可以应用到Django的所有方面,而不仅仅是admin
Django自动管理工具是django.contrib的一部分。django.contrib是一套庞大的功能集,它是Django基本代码的组成部分,Django框架就是由众多包含附加组件(add-on)的基本代码构成的。 你可以把django.contrib看作是可选的Python标准库或普遍模式的实际实现。 它们与Django捆绑在一起,这样你在开发中就不用“重复发明轮子”了。
管理工具是本书讲述django.contrib的第一个部分。从技术层面上讲,它被称作django.contrib.admin。django.contrib中其它可用的特性,如用户鉴别系统(django.contrib.auth)、支持匿名会话(django.contrib.sessioins)以及用户评注系统(django.contrib.comments)。这些,我们将在第十六章详细讨论。在成为一个Django专家以前,你将会知道更多django.contrib的特性。 目前,你只需要知道Django自带很多优秀的附加组件,它们都存在于django.contrib包里。
Django管理站点完全是可选择的,因为仅仅某些特殊类型的站点才需要这些功能。 这意味着你需要在你的项目中花费几个步骤去激活它。
第一步,对你的settings文件做如下这些改变:
将'django.contrib.admin'加入setting的INSTALLED_APPS配置中 (INSTALLED_APPS中的配置顺序是没有关系的, 但是我们喜欢保持一定顺序以方便人来阅读)
保证INSTALLED_APPS中包含'django.contrib.auth','django.contrib.contenttypes'和'django.contrib.sessions',Django的管理工具需要这3个包。 (如果你跟随本文制作mysite项目的话,那么请注意我们在第五章的时候把这三项INSTALLED_APPS条目注释了。现在,请把注释取消。)
确保MIDDLEWARE_CLASSES 包含'django.middleware.common.CommonMiddleware' 、'django.contrib.sessions.middleware.SessionMiddleware' 和'django.contrib.auth.middleware.AuthenticationMiddleware' 。(再次提醒,如果有跟着做mysite的话,请把在第五章做的注释取消。)
运行 python manage.py syncdb 。这一步将生成管理界面使用的额外数据库表。 当你把'django.contrib.auth'加进INSTALLED_APPS后,第一次运行syncdb命令时, 系统会请你创建一个超级用户。 如果你不这么作,你需要运行python manage.py createsuperuser来另外创建一个admin的用户帐号,否则你将不能登入admin (提醒一句: 只有当INSTALLED_APPS包含'django.contrib.auth'时,python manage.py createsuperuser这个命令才可用.)
第三,将admin访问配置在URLconf(记住,在urls.py中). 默认情况下,命令django-admin.py startproject生成的文件urls.py是将Django admin的路径注释掉的,你所要做的就是取消注释。 请注意,以下内容是必须确保存在的:
# Include these import statements... from django.contrib import admin admin.autodiscover() # And include this URLpattern... urlpatterns = patterns('', # ... (r'^admin/', include(admin.site.urls)), # ... )
当这一切都配置好后,现在你将发现Django管理工具可以运行了。 启动开发服务器(如前:`` python manage.py runserver`` ),然后在浏览器中访问:http://127.0.0.1:8000/admin/
管理界面的设计是针对非技术人员的,所以它应该是自我解释的。 尽管如此,这里简单介绍一下它的基本特性。
你看到的第一件事是如图6-1所示的登录屏幕。
图 6-1. Django的登录截图
你要使用你原来设置的超级用户的用户名和密码。 如果无法登录,请运行`` python manage.py createsuperuser`` ,确保你已经创建了一个超级用户。
一旦登录了,你将看到管理页面。 这个页面列出了管理工具中可编辑的所有数据类型。 现在,由于我们还没有创建任何模块,所以这个列表只有寥寥数条类目: 它仅有两个默认的管理-编辑模块:用户组(Groups)和用户(Users)。
图 6-2。 Django admin的首页
在Django管理页面中,每一种数据类型都有一个* change list* 和* edit form* 。前者显示数据库中所有的可用对象;后者可让你添加、更改和删除数据库中的某条记录。
其它语言
如果你的母语不是英语,而你不想用它来配置你的浏览器,你可以做一个快速更改来观察Django管理工具是否被翻译成你想要的语言。 仅需添加`` ‘django.middleware.locale.LocaleMiddleware’`` 到`` MIDDLEWARE_CLASSES`` 设置中,并确保它在’django.contrib.sessions.middleware.SessionMiddleware’* 之后* 。 (见上)
完成后,请刷新页面。 如果你设置的语言可用,一系列的链接文字将被显示成这种语言。这些文字包括页面顶端的Change password和Log out,页面中部的Groups和Users。 Django自带了多种语言的翻译。
关于Django更多的国际化特性,请参见第十九章。
点击Uers行中的Change链接,引导用户更改列表。
图 6-3. 典型的改变列表视图 (见上)
这个页面显示了数据库中所有的用户。你可以将它看作是一个漂亮的网页版查询:`` SELECT * FROM auth_user;`` 如果你一直跟着作练习,并且只添加了一个用户,你会在这个页面中看到一个用户。但是如果你添加了多个用户,你会发现页面中还有过滤器、排序和查询框。 过滤器在右边;排序功能可通过点击列头查看;查询框在页面顶部,它允许你通过用户名查询。
点击其中一个用户名,你会看见关于这个用户的编辑窗口。
图 6-4. 典型的编辑表格 (见上)
这个页面允许你修改用户的属性,如姓名和权限。 (如果要更改用户密码,你必须点击密码字段下的change password form,而不是直接更改字段值中的哈西码。)另外需要注意的是,不同类型的字段会用不同的窗口控件显示。例如,日期/时间型用日历控件,布尔型用复选框,字符型用简单文本框显示。
你可以通过点击编辑页面下方的删除按钮来删除一条记录。 你会见到一个确认页面。有时候,它会显示有哪些关联的对象将会一并被删除。 (例如,如果你要删除一个出版社,它下面所有的图书也将被删除。)
你可以通过点击管理主页面中某个对象的Add来添加一条新记录。 一个空白记录的页面将被打开,等待你填充。
你还能看到管理界面也控制着你输入的有效性。 你可以试试不填必需的栏目或者在时间栏里填错误的时间,你会发现当你要保存时会出现错误信息,如图6-5所示。
图6-5. 编辑表格显示错误信息 (见上)
当你编辑已有的对像时,你在窗口的右上角可以看到一个历史按钮。 通过管理界面做的每一个改变都留有记录,你可以按历史键来检查这个记录(见图6-6)。
图6-6. Django 对像历史页面 (见上)
有一个关键步骤我们还没做。 让我们将自己的模块加入管理工具中,这样我们就能够通过这个漂亮的界面添加、修改和删除数据库中的对象了。 我们将继续第五章中的`` book`` 例子。在其中,我们定义了三个模块: Publisher 、 Author 和 Book 。
在`` books`` 目录下(`` mysite/books`` ),创建一个文件:`` admin.py`` ,然后输入以下代码:
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/`` ,你会看到一个Books区域,其中包含Authors、Books和Publishers。 (你可能需要先停止,然后再启动服务(`` runserver`` ),才能使其生效。)
现在你拥有一个功能完整的管理界面来管理这三个模块了。 很简单吧!
花点时间添加和修改记录,以填充数据库。 如果你跟着第五章的例子一起创建Publisher对象的话(并且没有删除),你会在列表中看到那些记录。
这里需要提到的一个特性是,管理工具处理外键和多对多关系(这两种关系可以在`` Book`` 模块中找到)的方法。 作为提醒,这里有个`` Book`` 模块的例子:
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title
在Add book页面中(`` http://127.0.0.1:8000/admin/books/book/add/`` ),`` 外键`` publisher用一个选择框显示,`` 多对多`` 字段author用一个多选框显示。 点击两个字段后面的绿色加号,可以让你添加相关的记录。 举个例子,如果你点击Publisher后面的加号,你将会得到一个弹出窗口来添加一个publisher。 当你在那个窗口中成功创建了一个publisher后,Add book表单会自动把它更新到字段上去 花巧.
在幕后,管理工具是如何工作的呢? 其实很简单。
当服务启动时,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模块(回想第五章,它在mysite/books/models.py文件里),在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,这使得它们不允许输入空值。
这里会发生一些有趣的事情。 直到现在,除了__unicode__()方法,我们的模块充当数据库中表定义的角色,即本质上是用Python的语法来写CREATE TABLE语句。 在添加blank=True过程中,我们已经开始在简单的定义数据表上扩展我们的模块了。 现在,我们的模块类开始成为一个富含Author对象属性和行为的集合了。 email不但展现为一个数据库中的VARCHAR类型的字段,它还是页面中可选的字段,就像在管理工具中看到的那样。
当你添加blank=True以后,刷新页面Add author edit form (http://127.0.0.1:8000/admin/books/author/add/ ),将会发现Email的标签不再是粗体了。 这意味它不是一个必填字段。 现在你可以添加一个作者而不必输入邮箱地址,即使你为这个字段提交了一个空值,也再不会得到那刺眼的红色信息“This field is required”。
虽然blank=True同样适用于日期型和数字型字段,但是这里需要详细讲解一些背景知识。
SQL有指定空值的独特方式,它把空值叫做NULL。NULL可以表示为未知的、非法的、或其它程序指定的含义。
在SQL中, NULL的值不同于空字符串,就像Python中None不同于空字符串("")一样。这意味着某个字符型字段(如VARCHAR)的值不可能同时包含NULL和空字符串。
这会引起不必要的歧义或疑惑。 为什么这条记录有个NULL,而那条记录却有个空字符串? 它们之间有区别,还是数据输入不一致? 还有: 我怎样才能得到全部拥有空值的记录,应该按NULL和空字符串查找么?还是仅按字符串查找?
为了消除歧义,Django生成CREATE TABLE语句自动为每个字段显式加上NOT NULL。 这里有个第五章中生成Author模块的例子:
CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL ) ;
在大多数情况下,这种默认的行为对你的应用程序来说是最佳的,因为它可以使你不再因数据一致性而头痛。 而且它可以和Django的其它部分工作得很好。如在管理工具中,如果你留空一个字符型字段,它会为此插入一个空字符串(而* 不是*NULL)。
但是,其它数据类型有例外:日期型、时间型和数字型字段不接受空字符串。 如果你尝试将一个空字符串插入日期型或整数型字段,你可能会得到数据库返回的错误,这取决于那个数据库的类型。 (PostgreSQL比较严禁,会抛出一个异常;MySQL可能会也可能不会接受,这取决于你使用的版本和运气了。)在这种情况下,NULL是唯一指定空值的方法。 在Django模块中,你可以通过添加null=True来指定一个字段允许为NULL。
因此,这说起来有点复杂: 如果你想允许一个日期型(DateField、TimeField、DateTimeField)或数字型(IntegerField、DecimalField、FloatField)字段为空,你需要使用null=True * 和* blank=True。
为了举例说明,让我们把Book模块修改成允许 publication_date为空。修改后的代码如下:
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删除了。 要完成这些改动,我们还需要更新数据库。
出于某种原因,Django不会尝试自动更新数据库结构。所以你必须执行ALTER TABLE语句将模块的改动更新至数据库。 像先前那样,你可以使用manage.py dbshell进入数据库服务环境。 以下是在这个特殊情况下如何删除NOT NULL:
ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL;
(注意:以下SQL语法是PostgreSQL特有的。)
我们将在第十章详细讲述数据库结构更改。
现在让我们回到管理工具,添加book的编辑页面允许输入一个空的publication date。
在编辑页面中,每个字段的标签都是从模块的字段名称生成的。 规则很简单: 用空格替换下划线;首字母大写。例如: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编辑页面中看到这个新标签。
请注意,你不必把verbose_name的首字母大写,除非是连续大写(如:"USA state")。Django会自动适时将首字母大写,并且在其它不需要大写的地方使用verbose_name的精确值。
最后还需注意的是,为了使语法简洁,你可以把它当作固定位置的参数传递。 这个例子与上面那个的效果相同。
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这个参数名称。
迄今为止,我们做的blank=True、null=True和verbose_name修改其实是模块级别,而不是管理级别的。 也就是说,这些修改实质上是构成模块的一部分,并且正好被管理工具使用,而不是专门针对管理工具的。
除了这些,Django还提供了大量选项让你针对特别的模块自定义管理工具。 这些选项都在ModelAdmin classes里面,这些类包含了管理工具中针对特别模块的配置。
让我们更深一步:自定义Author模块的列表中的显示字段。 列表默认地显示查询结果中对象的__unicode__()。 在第五章中,我们定义Author对象的__unicode__()方法,用以同时显示作者的姓和名。
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') **def __unicode__(self):** **return u'%s %s' % (self.first_name, self.last_name)**
结果正如图6-7所示,列表中显示的是每个作者的姓名。
图 6-7. 作者列表
我们可以在这基础上改进,添加其它字段,从而改变列表的显示。 这个页面应该提供便利,比如说:在这个列表中可以看到作者的邮箱地址。如果能按照姓氏或名字来排序,那就更好了。
为了达到这个目的,我们将为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列表页面,你会看到列表中有三列:姓氏、名字和邮箱地址。 另外,点击每个列的列头可以对那列进行排序。 (参见图 6-8)
图 6-8. 修改后的author列表页面
接下来,让我们添加一个快速查询栏。 向AuthorAdmin追加search_fields,如:
class AuthorAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email') **search_fields = ('first_name', 'last_name')**
刷新浏览器,你会在页面顶端看到一个查询栏。 (见图6-9.)我们刚才所作的修改列表页面,添加了一个根据姓名查询的查询框。 正如用户所希望的那样,它是大小写敏感,并且对两个字段检索的查询框。如果查询"bar",那么名字中含有Barney和姓氏中含有Hobarson的作者记录将被检索出来。
图 6-9. 含search_fields的author列表页面
接下来,让我们为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为日期型字段提供了快捷过滤方式,它包含:今天、过往七天、当月和今年。这些是开发人员经常用到的。 图 6-10显示了修改后的页面。
图 6-10. 含过滤器的book列表页面
`` 过滤器`` 同样适用于其它类型的字段,而不单是`` 日期型`` (请在`` 布尔型`` 和`` 外键`` 字段上试试)。当有两个以上值时,过滤器就会显示。
另外一种过滤日期的方式是使用date_hierarchy选项,如:
class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) **date_hierarchy = 'publication_date'**
修改好后,页面中的列表顶端会有一个逐层深入的导航条,效果如图 6-11. 它从可用的年份开始,然后逐层细分到月乃至日。
图 6-11. 含date_hierarchy的book列表页面
请注意,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',)**
这个ordering选项基本像模块中class Meta的ordering那样工作,除了它只用列表中的第一个字段名。 如果要实现降序,仅需在传入的列表或元组的字段前加上一个减号(-)。
刷新book列表页面观看实际效果。 注意Publication date列头现在有一个小箭头显示排序。 (见图 6-12.)
图 6-12 含排序的book列表页面
我们已经学习了主要的选项。 通过使用它们,你可以仅需几行代码就能创建一个功能强大、随时上线的数据编辑界面。
正如自定义列表那样,编辑表单多方面也能自定义。
首先,我们先自定义字段顺序。 默认地,表单中的字段顺序是与模块中定义是一致的。 我们可以通过使用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')**
完成之后,编辑表单将按照指定的顺序显示各字段。 它看起来自然多了——作者排在书名之后。 字段顺序当然是与数据条目录入顺序有关, 每个表单都不一样。
通过fields这个选项,你可以排除一些不想被其他人编辑的fields 只要不选上不想被编辑的field(s)即可。 当你的admi用户只是被信任可以更改你的某一部分数据时,或者,你的数据被一些外部的程序自动处理而改变了了,你就可以用这个功能。 例如,在book数据库中,我们可以隐藏publication_date,以防止它被编辑。
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进行改动。 如果你是一个编辑,不希望作者推迟出版日期的话,这个功能就很有用。 (当然,这纯粹是一个假设的例子。)
当一个用户用这个不包含完整信息的表单添加一本新书时,Django会简单地将publication_date设置为None,以确保这个字段满足null=True的条件。
另一个常用的编辑页面自定义是针对多对多字段的。 真如我们在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',)**
(如果你一着跟着做练习,请注意移除fields选项,以使得编辑页面包含所有字段。)
刷新book编辑页面,你会看到Author区中有一个精巧的JavaScript过滤器,它允许你检索选项,然后将选中的authors从Available框移到Chosen框,还可以移回来。
图 6-13. 含filter_horizontal的book编辑页面
我们强烈建议针对那些拥有十个以上选项的`` 多对多字段`` 使用filter_horizontal。 这比多选框好用多了。 你可以在多个字段上使用filter_horizontal,只需在这个元组中指定每个字段的名字。
ModelAdmin类还支持filter_vertical选项。 它像filter_horizontal那样工作,除了控件都是垂直排列,而不是水平排列的。 至于使用哪个,只是个人喜好问题。
filter_horizontal和filter_vertical选项只能用在多对多字段 上, 而不能用于 ForeignKey字段。 默认地,管理工具使用`` 下拉框`` 来展现`` 外键`` 字段。但是,正如`` 多对多字段`` 那样,有时候你不想忍受因装载并显示这些选项而产生的大量开销。 例如,我们的book数据库膨胀到拥有数千条publishers的记录,以致于book的添加页面装载时间较久,因为它必须把每一个publishe都装载并显示在`` 下拉框`` 中。
解决这个问题的办法是使用`` raw_id_fields`` 选项。它是一个包含外键字段名称的元组,它包含的字段将被展现成`` 文本框`` ,而不再是`` 下拉框`` 。见图 6-14。
class BookAdmin(admin.ModelAdmin): list_display = ('title', 'publisher', 'publication_date') list_filter = ('publication_date',) date_hierarchy = 'publication_date' ordering = ('-publication_date',) filter_horizontal = ('authors',) **raw_id_fields = ('publisher',)**
图 6-14. 含raw_id_fields的book编辑页面
在这个输入框中,你输入什么呢? publisher的数据库ID号。 考虑到人们通常不会记住这些数据库ID,管理工具提供了一个放大镜图标方便你输入。点击那个图标将会弹出一个窗口,在那里你可以选择想要添加的publishe。
因为你是用超级用户登录的,你可以创建,编辑和删除任何对像。 然而,不同的环境要求有不同的权限,系统不允许所有人都是超级用户。 管理工具有一个用户权限系统,通过它你可以根据用户的需要来指定他们的权限,从而达到部分访问系统的目的。
用户帐号应该是通用的、独立于管理界面以外仍可以使用。但我们现在把它看作是管理界面的一部分。 在第十四章,我们将讲述如何把用户帐号与你的网站(不仅仅是管理工具)集成在一起。
你通过管理界面编辑用户及其许可就像你编辑别的对象一样。 我们在本章的前面,浏览用户和用户组区域的时候已经见过这些了。 如你所想,用户对象有标准的用户名、密码、邮箱地址和真实姓名,同时它还有关于使用管理界面的权限定义。 首先,这有一组三个布尔型标记:
活动标志,它用来控制用户是否已经激活。 如果一个用户帐号的这个标记是关闭状态,而用户又尝试用它登录时,即使密码正确,他也无法登录系统。
成员标志,它用来控制这个用户是否可以登录管理界面(即:这个用户是不是你们组织里的成员) 由于用户系统可以被用于控制公众页面(即:非管理页面)的访问权限(详见第十四章),这个标志可用来区分公众用户和管理用户。
超级用户标志,它赋予用户在管理界面中添加、修改和删除任何项目的权限。 如果一个用户帐号有这个标志,那么所有权限设置(即使没有)都会被忽略。
普通的活跃,非超级用户的管理用户可以根据一套设定好的许可进入。 管理界面中每种可编辑的对象(如:books、authors、publishers)都有三种权限: 创建 许可, 编辑 许可和 删除 许可。 给一个用户授权许可也就表明该用户可以进行许可描述的操作。
当你创建一个用户时,它没有任何权限,该有什么权限是由你决定的。 例如,你可以给一个用户添加和修改publishers的权限,而不给他删除的权限。 请注意,这些权限是定义在模块级别上,而不是对象级别上的。据个例子,你可以让小强修改任何图书,但是不能让他仅修改由机械工业出版社出版的图书。 后面这种基于对象级别的权限设置比较复杂,并且超出了本书的覆盖范围,但你可以在Django documentation中寻找答案。
注释
权限管理系统也控制编辑用户和权限。 如果你给某人编辑用户的权限,他可以编辑自己的权限,这种能力可能不是你希望的。 赋予一个用户修改用户的权限,本质上说就是把他变成一个超级用户。
你也可以给组中分配用户。 一个 组 简化了给组中所有成员应用一套许可的动作。 组在给大量用户特定权限的时候很有用。
经过这一章的学习,你应该对Django管理工具有所认识。 但是我们需要表明一个观点:* 什么时候* 、* 为什么* 用,以及什么时候又* 不* 用。
Django的管理界面对非技术用户要输入他们的数据时特别有用;事实上这个特性就是专门为这个 实现的。 在Django最开始开发的新闻报道的行业应用中,有一个典型的在线自来水的水质专题报道 应用,它的实现流程是这样的:
负责这个报道的记者和要处理数据的开发者碰头,提供一些数据给开发者。
开发者围绕这些数据设计模型然后配置一个管理界面给记者。
记者检查管理界面,尽早指出缺少或多余的字段。 开发者来回地修改模块。
当模块认可后,记者就开始用管理界面输入数据。 同时,程序员可以专注于开发公众访问视图和模板(有趣的部分)。
换句话说,Django的管理界面为内容输入人员和编程人员都提供了便利的工具。
当然,除了数据输入方面,我们发现管理界面在下面这些情景中也是很有用的:
检查模块* :当你定义好了若干个模块,在管理页面中把他们调出来然后输入一些虚假的数据,这是相当有用的。 有时候,它能显示数据建模的错误或者模块中其它问题。
管理既得数据* :如果你的应用程序依赖外部数据(来自用户输入或网络爬虫),管理界面提供了一个便捷的途径,让你检查和编辑那些数据。 你可以把它看作是一个功能不那么强大,但是很方便的数据库命令行工具。
临时的数据管理程序* :你可以用管理工具建立自己的轻量级数据管理程序,比如说开销记录。 如果你正在根据自己的,而不是公众的需要开发些什么,那么管理界面可以带给你很大的帮助。 从这个意义上讲,你可以把它看作是一个增强的关系型电子表格。
最后一点要澄清的是: 管理界面不是终结者。 过往许多年间,我们看到它被拆分、修改成若干个功能模块,而这些功能不是它所支持的。 它不应成为一个* 公众* 数据访问接口,也不应允许对你的数据进行复杂的排序和查询。 正如本章开头所说,它仅提供给可信任的管理员。 请记住这一点,它是有效使用管理界面的钥匙。
到现在,我们已经创建了一些模块,并且为编辑数据配置了一个优秀的界面。 ` 下一章 <../chapter07/>`__ ,我们将转入到网站开发中最重要的部分: 表单的创建和处理。
关于本评注系统
本站使用上下文关联的评注系统来收集反馈信息。不同于一般对整章做评注的做法, 我们允许你对每一个独立的“文本块”做评注。一个“文本块”看起来是这样的:
一个“文本块”是一个段落,一个列表项,一段代码,或者其他一小段内容。 你选中它会高亮度显示:
要对文本块做评注,你只需要点击它旁边的标识块:
我们会仔细阅读每个评论,如果可能的话我们也会把评注考虑到未来的版本中去:
如果你愿意你的评注被采用,请确保留下你的全名 (注意不是昵称或简称)
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.ext
library. Thanks also to Yahoo for YUI itself.