本教程上接 教程 6。我们将继续完成网页投票应用,并着重讲解 如何用 Django 自动生成管理网站,我们曾在 教程 2 中初次探索过。
通过 admin.site.register(Question) 注册 Question 后,Django 可以自动构建一个
默认的表单。通常,您想自定义管理表单的外观和功能。您可以在注册时通过配置来实现。
现在先来试试排序表单上的字段。将 admin.site.register(Question) 所在行替换为:
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
您可以参照上面的形式 —— 创建一个模型管理类,将之作为第二个参数传入 admin.site.register()。
而且这种操作在任何时候都可以进行。
经过上面更改,“Publication date”字段会在“Question”字段之前:
目前的表单只有两个字段可能看不出什么,但是对于字段很多的表单,设计一个直观合理的排序方式非常重要。
并且在字段数据很多时,还可以将表单分割成多个字段的集合:
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
fieldsets 中的每个元组的第一个元素是字段集合的标题。
它让表单看起来像下面的样子:
现在我们有了问题管理页面,但是一个 Question 有多个 Choice,而管理页面并没有显示。
暂时。
现在有两个方法可以解决这个问题:一是就像刚刚 Question 一样也将 Choice 注册到管理界面。
这简单:
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
现在“Choice”也可以管理页面看到了,其中“Add choice”表单应该类似这样:
在这个表单中,“Question”字段是一个选择框,包含了当前数据库中每个问题实例。Django 知道将
ForeignKey 展示为一个 <select> 框。在我们的例子中,目前
只有一个问题对象存在。
请注意“Add Another”连接到“Question”。每一个包含 外键(ForeignKey) 关系的对象都会有这个。
当您点击“Add Another”时,会弹出一个“Add question”的表单窗口。如果在窗口中添加了问题并点击“Save”,
Django 会将问题保存在数据库中,并动态地把它添加为“Add choice”表单的选项。
但是,实话说,这种将 Choice 对象添加到系统的方式效率不高。如果在创建 Question 对象时
直接添加一些 Choice 则会更好。让我们实现它。
删除 Choice 模型对 register() 方法的调用。然后,编辑 Question 的注册代码如下:
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
上面的代码告诉 Django:“ Choice 对象在 Question 管理页面进行编辑,默认情况,为3个选项
提供了足够的字段。”
载入“Add question”页面来看变成了什么样:
它的工作原理如下:有3个插槽用于相关的选择 —— 由 extra 指定 —— 每当您回到已创建的对象的
“Change” 页面,您会再获得三个额外的插槽。
在三个当前插槽的最后,您将找到一个“Add another Choice”的链接。如果您点击它,将会添加一个新插槽。 如果要删除添加的插槽,可以单击添加插槽右上方的 X。请注意,您不能删除原来的三个插槽。 此图片显示所添加的插槽:
不过还有个小问题,它需要大量的屏幕空间来显示所有的用于输入相关 Choice 对象的字段。为此,
Django 提供了一个显示内联相关对象的表格方式;您只需要改变 ChoiceInline 声明如下:
class ChoiceInline(admin.TabularInline):
#...
使用 TabularInline (而不是 StackedInline ),相关的对象将以一种更紧凑的
表格形式显示出来:
请注意,有一个额外的“Delete?”列,允许删除使用“Add Another Choice”按钮添加的行和已保存的行。
现在 Question 的管理页面看起来已经差不多了,下面来看看“更改列表”页面,也就是显示系统中 所有问题的页面。
请看下图:
Django 默认显示每个对象的 str() 内容。但如果我们能显示个别字段或许会更有帮助。要做到
这一点,可以使用 list_display 管理选项,
这是一个要在对象的更改列表页面上显示作为列的字段名称的元组:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
为了有好的效果,我们还加入了 教程 2 中的
was_published_recently() 方法:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
现在问题更改列表页面看起来像这样:
您可以点击列标题按照这些值进行排序,但是是 was_published_recently 标题不行,因为
不支持按任意方法的输出进行排序。另请注意, 默认情况下 was_published_recently 的列标题
是方法名称(用空格替换下划线),并且每一行都包含输出的字符串表示形式。
您可以通过给出该方法(在 polls/models.py 中)中的几个属性来改进,如下所示:
class Question(models.Model):
# ...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
有关这些方法属性的更多信息,请参阅 list_display。
再次编辑您的 polls/admin.py 文件,并为 Question 更改列表页面增加一个改进:
使用 list_filter 过滤器。将以下行添加到
QuestionAdmin:
list_filter = ['pub_date']
这会增加一个“过滤器”侧边栏,让人们通过 pub_date 字段过滤变更列表:
显示的过滤器类型取决于您要过滤的字段类型。
因为 pub_date 是一个 DateTimeField,Django 知道给出
适当的过滤器选项:“任何日期”、“今天”、“过去7天”、“这个月”、“今年”。
这个态势不错。让我们添加一些搜索功能:
search_fields = ['question_text']
这会在更改列表的顶部添加一个搜索框。当有人输入搜索字词时,Django 会搜索 question_text
字段。您可以使用尽可能多的字段 —— 不过因为它在幕后使用 LIKE 查询,将搜索字段的数量限制为
合理的数字将使您的数据库更容易进行搜索。
现在是个很好的时间去关注更改列表给你自由分页。默认是每页显示 100 个项目。
更改列表分页、
搜索框、
过滤器、
日期层次 和
列标题排序
都能如你所愿地工作。
显然,每个管理页面顶部的“Django 管理”都是没用的。这只是占位符文本。
不过使用 Django 的模板系统就很容易改变。Django 管理由 Django 本身提供支持,其界面使用 Django 自己的模板系统。
在项目目录(包含 manage.py 的目录)中创建一个 templates 目录。模板可以是在
Django 可以访问的文件系统的任何地方。(Django 可以以您的服务器运行的任何用户运行。)
但是,将模板保留在项目中是一个很好的惯例。
打开你的设置文件(记住是 mysite/settings.py )并在 TEMPLATES
配置中添加一个 DIRS 选项:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
DIRS 是加载 Django 模板时要检查的文件系统目录的列表;
它是一个搜索路径。
组织模板
就像静态文件一样,我们 可以 将所有的模板放在一个大的模板目录中,并且工作得很好。
但是,属于特定应用程序的模板应该放在该应用程序的模板目录(例如 polls/templates )中,
而不是项目的 templates 中。我们将在 可复用应用教程
中探讨 为什么 我们这样做的更多细节。
现在在 templates 里创建一个名为 admin 的目录,并从 Django 自身源码的默认管理
模板目录中复制模板 admin/base_site.html 到那个目录。
Django源文件在哪里?
如果您难以找到系统上 Django 源文件的位置,请运行以下命令:
$ python -c "import django; print(django.__path__)"
然后,只需编辑该文件,并将 {{ site_header|default:_('Django administration') }}
(包括花括号)替换为您的网站名称。最终您应该得到这样一份代码:
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}
我们使用这种方法教你如何覆盖模板。在实际的项目中,您可能会使用
django.contrib.admin.AdminSite.site_header 属性,从而更容易地进行这种定制。
此模板文件包含许多文本,如 {% block branding %} 和 {{ title }}。 {% 和
{{ 标签是 Django 模板语言的一部分。当 Django 渲染 admin/base_site.html 时,
这个模板语言将被应用,以产生最终的 HTML 页面,就像我们在
教程 3 中看到的那样。
请注意,Django 的任何默认管理模板都可以被覆盖。要覆盖一个模板,只需要使用
base_site.html 做同样的事情 —— 将它从默认目录复制到您的自定义目录中并进行更改。
聪明的读者会问:如果 DIRS 默认为空,Django 如何能找到
默认的管理模板?答案是,由于 APP_DIRS 设置为 True,Django 会自动在每个应用程序包中查找 templates/ 的子目录,作为备用(不要忘了不要忘了 django.contrib.admin 是一个应用程序)。
我们的投票应用程序并不复杂,不需要自定义管理模板。但是如果它变得越来越复杂,且需要 Django 的标准管理模板的一些功能的修改,那么修改 应用程序的 模板,而不是 项目 中的模板会更加明智。 这样,您可以将投票应用程序放在在任何新项目中,并确保它会找到所需的自定义模板。
参见 template loading documentation 了解更多关于 Django 如何找到模板的信息。
在类似的情况下,您可能想要自定义 Django 管理首页的外观。
默认情况下,它会按照字母顺序显示所有 INSTALLED_APPS 中,已经在管理应用程序中注册的应用。您可能需要对布局进行重大更改。毕竟,首页可能是管理页面中最重要的,它应该要很容易被
使用。
要自定义的模板是 admin/index.html。(与上节中的 admin/base_site.html 相似 ——
将其从默认目录复制到您的自定义模板目录)。 编辑文件,你会看到它使用一个名为 app_list
的模板变量。该变量包含每个已安装的 Django 应用。您可以以任何您认为最佳的方式
将链接硬编码到特定于对象的管理页面,而不是使用它。
初学者教程结束于此。在此期间,您可能需要在 接下来做什么 中查看 一些建议。
如果您熟悉 Python 打包,并有兴趣了解如何将投票转化为“可重用的应用”,请查看 高级教程:如何编写可重用的应用。
9月 10, 2017