你的第一个 django 应用程序,第二部分

上接 Tutorial 1 . 继续这个 WEB-民间测验程序,并聚焦于 Django 的自动管理站点 admin .

哲学

生成一个 admin 站点给你的同事或客户, 用它来添加,改变及删除内容是相当单调的工作, 嗯,没错,这就是一个没啥创造力的体力活. 基于同样的考虑, Django 很体贴的为你的 model 生成自动的管理界面. Django 是在一个编辑部环境下被发明出来的, 在那个环境里, 内容发布及站点发布是泾渭分明的两件事. 站点管理人员使用这套系统添加新的故事,事件,体育比分等等, 这些内容随即被公开发布. Django 发明了一个统一的接口来解决编辑发布内容的问题.

admin 通常没有必要提供给站点用户, 它是为站点管理人员设计的.

激活 admin 站点

Django admin site 默认是不能使用的 -- 它是可选的. 要激活并使用的,需要以下三个步骤:

  • 在你的 INSTALLED_APPS 设置中添加 "django.contrib.admin" .
  • 在你新增应用程序到 INSTALLED_APPS 之后运行 python manage.py syncdb 以更新数据库.
  • 编辑 mysite/urls.py 文件并将 "Uncomment this for admin:" 行下的行解除注释.这个文件是 URLconf; 我们将在下一教程中深入了解 URLconfs.现在你只需要知道怎么做就行了.

启动开发版 WEB 服务器

让我们启动开发版WEB服务器并开始遨游 admin 管理站点.

温习一下 Tutorial 1 , 你就知道该这样启动WEB服务器:

python manage.py runserver

Now, open a Web browser and go to "/admin/" on your local domain -- e.g., http://127.0.0.1:8000/admin/. You should see the admin's login screen:

Django admin login screen

登录 admin site

Now, try logging in. You should see the Django admin index page:

Django admin index page

默认你会看到两个可编辑的内容: groups and users. 这是 Django 自带的核心特性.

让 poll app 可以在 admin 中编辑

我们的 poll app 在哪? 在 admin 索引页中根本看不到它!

只需要做一件事: 我们需要在 Poll model 中指定 Poll 对象需要有一个 admin 界面. 编辑 mysite/polls/models.py 文件并做以下改动:

class Poll(models.Model):
    # ...
    class Admin:
        pass

class Admin 的设置将决定该 model 在 django admin 中如何表现. 所有设置都是可选的.创建一个空的 Admin 类意味着使用所有默认选项.

现在重新载入 Django admin 页. 注意不必重新启动 WEB 服务器 -- 服务器会自动重新载入你的 project,因此在浏览器中你会立马看到你的更改.

自由探索 admin 的功能

现在 Poll 有内嵌类 Admin 了, Django 知道如何在 admin 索引页显示它:

Django admin index page, now with polls displayed

点击 "Polls." 现在你处于polls的 "change list" 页. 该页显示数据库中所有的 polls , 并允许你选中一个进行修改. 这是我们在第一个教程中创建的 "What's up?" poll:

Polls change list page

点击 "What's up?" poll 可以编辑它:

Editing form for poll object

注意事项:

在页面的底部提供给你以下选择:

通过点击 "Today" 和 "Now" 捷径可以改变 "Date published" . 然后点击 "Save and continue editing." 然后点击右上角的 "History" . 你会看到一个页面列出了所有通过 Django admin 对该对象做的改变, 并注意了修改时间及修改人:

History page for poll object

定制 admin 表单

Take a few minutes to marvel at all the code you didn't have to write.

我们来稍微定制它一下. 我们通过显式的添加一个 fields 参数给 Admin 类来自定义字段排列顺序:

class Admin:
    fields = (
        (None, {'fields': ('pub_date', 'question')}),
    )

这就使得 "Publication date" 显示在 "question" 前面:

Fields have been reordered

仅于两个字段, 这难免让人印象不够深刻, 不过对于 admin 表单拥有成打的字段时, 能够选择一个直观的顺序是相当重要的特性.

既然谈到拥有成打的字段, 你可能会想到将他们分成不同的字段集:

class Admin:
    fields = (
        (None, {'fields': ('question',)}),
        ('Date information', {'fields': ('pub_date',)}),
    )

每个 fields 子tuple的第一个元素是字段集的标题, 现在我们的表单看起来就是这个样子:

Form has fieldsets now

你可以为每个字段集指定任意的 HTML classes. Django 提供了一个 "collapse" class 来显示一个初始被卷起来的字段集. 当你有一个不常用但包含一堆字段的很长的表单时会很有用:

class Admin:
    fields = (
        (None, {'fields': ('question',)}),
        ('Date information', {'fields': ('pub_date',), 'classes': 'collapse'}),
    )
Fieldset is initially collapsed

添加关联对象

不错,我们有了 Poll admin 页. 不过一个 Poll 对应多个 Choices, 而 admin 页不能显示 choices.

这不算问题,嘿嘿.

有两个方法解决这个问题. 第一种,给 Choice model 也指定一个内嵌类 Admin , 就象我们在 Poll model 中做的一样:

class Choice(models.Model):
    # ...
    class Admin:
        pass

现在 "Choices" 在 Django admin 中也可以编辑了. "Add choice" 表单看起来就象下面这样:

Choice admin page

在这个表单里, "Poll" 字段是一个选择框包含数据库中每一个 poll. Django 知道一个 ForeignKey 应该在 admin 中以一个 <select> 框表示. 同一时刻只能存在一个 poll .

你一定注意到"Poll." 之后有一个 "Add Another". 每一个有外键关系的对象都会有这么一个选项. 当你点击 "Add Another," 则弹出一个带有"Add poll"表单的窗口. 如果你在这个窗口里添加了一个 poll并点击了 "Save," , Django 会保存这个 poll 到数据库,并动态的添加它作为选中的 choice 的关联 poll.

不过,这实在是一个低效率的添加 choice 对象的方式. 要是能在添加一个 Poll 对象时直接添加一堆 choice 该有多好! 没错,我也这么想.

在Choice model 中删去内嵌类 Admin . 然后编辑 ForeignKey(Poll) 字段成下面这样:

poll = models.ForeignKey(Poll, edit_inline=models.STACKED, num_in_admin=3)

这就告诉 Django: "Choice 对象要在 Poll 对象的 admin 页进行编辑.默认提供足够 3 个 Choices 使用的字段."

然后编辑 Choice model 的其它字段,添加 core=True 参数:

choice = models.CharField(maxlength=200, core=True)
votes = models.IntegerField(core=True)

这是通知 Django: "当你在 Poll admin 页编辑一个 Choice 时, 'choice' 及 'votes' 字段是必须的. 至少一个字段拥有有效值表示新的 choice 对象有效.将二者清除则表示该 choice 对象在保存时会被删除."

点击 "Add poll" 看看它如何工作:

Add poll page now has choices on it

如你所见共提供有三个位置让创建关联对象--和你指定的 num_in_admin 参数一致. 不过在你每次返回来编辑一个已经存在的对象时, 会额外提供一个新对象的位置.(这意味着其实并没有硬性限制你可以添加多少个关联对象). 如果你希望改变一个 poll 时每次提供三个新对象位置,使用这个选项: num_extra_on_change=3.

还有一个小问题. 用于显示和输入关联对象的所有字段占用了太多的屏幕空间. 就因为这个, Django 提供了另外一种显示关联对象的方法:

poll = models.ForeignKey(Poll, edit_inline=models.TABULAR, num_in_admin=3)

其中 edit_inline=models.TABULAR (而不是 models.STACKED), 关联对象被显示为精简的, 基于表格的格式:

Add poll page now has more compact choices

定制 admin change list

现在 Poll admin 页看上去不错了, 现在让我们来优化一下 "change list" 页 -- 这个用来显示所有 polls 的页.

现在该页看起来这样:

Polls change list page

默认的, Django 调用每个对象的 str() 方法表示每个对象. 不过有时候我们需要显示更有表现力的信息.要做到这一点也不难, 利用 list_display 选项就可以.这是一个用于在 change list 页显示的字段列表:

class Poll(models.Model):
    # ...
    class Admin:
        # ...
        list_display = ('question', 'pub_date')

让我们将自定义方法 was_published_today 也放进去:

list_display = ('question', 'pub_date', 'was_published_today')

现在 poll change list 页看起来这样:

Polls change list page, updated

点击列标题可以对其排序 -- 不过 was_published_today 标题除外, 目前还不支持对方法的输出进行排序. 注意一下 was_published_today 方法对应的标题, 默认该标题来自方法名称 (下划线被空格替换). 你可以通过给该方法一个简短描述来改变这个默认行为:

def was_published_today(self):
    return self.pub_date.date() == datetime.date.today()
was_published_today.short_description = 'Published today?'

让我们对 Poll change list 页再做一点点改进: Filters. 在 Poll.admin 中添加下面这行:

list_filter = ['pub_date']

这就添加了一个 "Filter" 工具条以允许用户通过 pub_date 字段过滤结果集:

Polls change list page, updated

过滤器显示的类型依赖过滤字段的类型, 由于 pub_date 是一个日期时间字段, Django 知道给出默认的过滤选项: "Any date," "Today," "Past 7 days," "This month," "This year."

进展不错. 现在我们来为该页面添加搜索能力:

search_fields = ['question']

这就在 change list 的顶部添加了一个搜索框. 输入搜索关键字后, Django 会搜索 question 字段. 你可以使用多个字段,只要你愿意 -- 由于后台使用 LIKE 查询, 所以尽可能合理使用这一功能, 这样你的数据库就好过些.

Finally, because Poll objects have dates, it'd be convenient to be able to drill down by date. Add this line:

date_hierarchy = 'pub_date'

That adds hierarchical navigation, by date, to the top of the change list page. At top level, it displays all available years. Then it drills down to months and, ultimately, days.

Now's also a good time to note that change lists give you free pagination. The default is to display 50 items per page. Change-list pagination, search boxes, filters, date-hierarchies and column-header-ordering all work together like you think they should.

定制 admin 的外观

显然, 在每个管理页的顶上都标着 "Django administration" 和 "example.com" 是很好笑的. 它们仅仅是占位符文本.

很容易改变这些, admin 使用的是 Django 的模板系统. Django admin 基于 Django 开发, 并且它的界面使用 Django 自己的模板系统. (How meta!)

打开你的设置文件 (mysite/settings.py, 记住) 看看 TEMPLATE_DIRS 设置. TEMPLATE_DIRS 是一个文件目录的 tuple . 它是一些搜索路径.

默认情况, TEMPLATE_DIRS 是空的. 那我们就添加一行进去, 告诉 Django 我们的模板存在哪里:

TEMPLATE_DIRS = (
    "/home/mytemplates", # Change this to your own directory.
)

现在将模板 admin/base_site.html 从默认的 Django admin 模板目录 (django/contrib/admin/templates) 复制到一个任意一个 TEMPLATE_DIRS 目录的 admin 子目录中. 举例来说,如果你的 TEMPLATE_DIRS 包括 "/home/mytemplates", 那就复制 django/contrib/admin/templates/admin/base_site.html/home/mytemplates/admin/base_site.html. 不要忘记是放到 admin 子目录下.

然后, 编辑这个文件并替换掉那些通用占位文本.

Django 的每个默认 admin 模板都是可以覆盖的. 要覆盖一个模板, 只要象改变 base_site.html 那样去做就可以-- 将它从默认目录下拷贝到定制目录,然后修改它.

聪明的读者会问: TEMPLATE_DIRS 默认是空的, Django 怎么知道去哪儿找 admin 模板呢? 答案就是, 默认的, 当Django实在找不到模板时,Django 就自动到每个app包下面的 templates/ 目录去找模板. 参阅 loader types documentation 以了解完整的信息.

定制 admin 索引页

大胆一点去想,你就会想到定制一下 Django 的 admin 索引页.

默认情况, 它会根据你的 INSTALLED_APPS 设置显示所有可用的 app, 不过 app 显示的顺序是随机的, 而且你也许还想对页面的布局调整一下. 毕竟, 索引页几乎是 admin 站点中最重要的页面了, 它应该易于使用.

The template to customize is admin/index.html. (Do the same as with admin/base_site.html in the previous section -- copy it from the default directory to your custom template directory.) Edit the file, and you'll see it uses a template tag called {% get_admin_app_list as app_list %}. That's the magic that retrieves every installed Django app. Instead of using that, you can hard-code links to object-specific admin pages in whatever way you think is best.

Django offers another shortcut in this department. Run the command python manage.py adminindex polls to get a chunk of template code for inclusion in the admin index template. It's a useful starting point.

For full details on customizing the look and feel of the Django admin site in general, see the Django admin CSS guide.

当你对 admin site 已经用得滚瓜烂熟时, 阅读 part 3 of this tutorial 开始准备发布 poll views.