本教程从教程 6 停止的地方开始。我们将继续使用网络投票应用程序,并将专注于自定义 Django 自动生成的管理站点,这是我们在教程 2 中首次探索的。
一、自定义管理表单
通过用 admin.site.register(Question) 注册 Question 模型,Django能够构造一个默认的表单表示。
通常,您需要自定义管理表单的外观和工作方式。你将通过在注册对象时告诉 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() - 每当您需要更改模型的管理选项时。
上面的这一特殊更改使“发布日期”位于“问题”字段之前:

只有两个字段并不令人印象深刻,但对于具有数十个字段的管理表单,选择直观的顺序是一个重要的可用性细节。
说到具有数十个字段的表单,您可能希望将表单拆分为字段集:
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 ,并且管理页面不显示选项。
有两种方法可以解决此问题。首先是向管理员注册 Choice ,就像我们对 Question 所做的那样:
from django.contrib import admin from .models import Choice, Question # ... admin.site.register(Choice)
现在“选择”是 Django 管理中的一个可用选项。“添加选项”表单如下所示:

在这种形式中,“问题”字段是一个选择框,其中包含数据库中的每个问题。
Django 知道 ForeignKey 应该在管理员中表示为 <select> 框。在我们的例子中,目前只存在一个问题。
另请注意“问题”旁边的“添加其他问题”链接。每个与另一个对象具有 ForeignKey 关系的对象都可以免费获得此内容。
当您单击“添加其他问题”时,您将获得一个带有“添加问题”表单的弹出窗口。
如果你在该窗口中添加一个问题并单击“保存”,Django 会将问题保存到数据库中,并将其动态添加为你正在查看的“添加选择”表单上的选定选项。
但是,实际上,这是将 Choice 对象添加到系统的低效方法。如果您可以在创建 Question 对象时直接添加一堆选择,那就更好了。让我们实现这一点。
删除 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)
This tells Django: “Choice objects are edited on the Question admin page. By default, provide enough fields for 3 choices.”
这告诉 Django:“ Choice 对象在 Question 管理页面上编辑。默认情况下,为 3 个选项提供足够的字段。
Load the “Add question” page to see how that looks:
加载“添加问题”页面以查看其外观:

它的工作原理是这样的:相关选项有三个插槽 - 由 extra 指定 - 每次您返回已创建对象的“更改”页面时,您都会获得另外三个额外的插槽。
在当前三个插槽的末尾,您将找到“添加另一个选项”链接。如果单击它,将添加一个新插槽。
如果要删除添加的插槽,可以单击添加的插槽右上角的 X。此图显示了一个添加的插槽:

不过,有一个小问题。显示用于输入相关 Choice 对象的所有字段需要大量屏幕空间。
出于这个原因,Django 提供了一种显示内联相关对象的表格方式。若要使用它,请将 ChoiceInline 声明更改为:
# 使用 TabularInline (而不是 StackedInline ), #相关对象以更紧凑的、基于表的格式显示 class ChoiceInline(admin.TabularInline): model = Choice extra = 3 class QuestionAdmin(admin.ModelAdmin): #fields = ["pub_date", "question_text"] fieldsets = [ (None, {"fields": ["question_text"]}), ("Date_information", {"fields": ["pub_date"], "classes": ["collapse"]}), ] #问题 关联 投票 inlines = [ChoiceInline]
使用 TabularInline (而不是 StackedInline ),相关对象以更紧凑的、基于表的格式显示:

三、自定义管理员更改列表
Now that the Question admin page is looking good, let’s make some tweaks to the “change list” page – the one that displays all the questions in the system.
现在问题管理页面看起来不错,让我们对“更改列表”页面进行一些调整 - 显示系统中所有问题的页面。
Here’s what it looks like at this point:
这是它此时的样子:

默认情况下,Django 显示每个对象的 str() 。但有时,如果我们可以显示单个字段会更有帮助。
为此,请使用 list_display admin 选项,该选项是字段名称的元组,在对象的更改列表页面上显示为列:
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 的列标题是方法的名称(下划线替换为空格),并且每行都包含输出的字符串表示形式。
您可以通过在该方法上使用 display() 装饰器(在 polls/models.py 中)来改进这一点,如下所示:
from django.contrib import admin class Question(models.Model): # ... @admin.display( boolean=True, ordering="pub_date", description="Published recently?", ) def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now
有关可通过装饰器配置的属性的详细信息,请参阅 list_display 。
再次编辑 polls/admin.py 文件,并向 Question 更改列表页面添加改进:使用 list_filter 进行筛选。
将以下行添加到 QuestionAdmin :
list_filter = ["pub_date"]
这增加了一个“过滤器”侧边栏,让人们通过 pub_date 字段过滤更改列表:

显示的筛选器类型取决于要筛选的字段类型。因为 pub_date 是 DateTimeField ,
Django知道给出适当的过滤器选项:“任何日期”,“今天”,“过去7天”,“本月”,“今年”。
这正在形成。让我们添加一些搜索功能:
class QuestionAdmin(admin.ModelAdmin): #fields = ["pub_date", "question_text"] fieldsets = [ (None, {"fields": ["question_text"]}), ("Date_information", {"fields": ["pub_date"], "classes": ["collapse"]}), ] #问题 关联 投票 inlines = [ChoiceInline] # 装饰器 list_display = ["question_text", "pub_date", "was_published_recently"] # 过滤器 list_filter = ["pub_date"] #搜索功能 search_fields = ["question_text"]
这会在更改列表的顶部添加一个搜索框。当有人输入搜索词时,Django 将搜索 question_text 字段。
您可以使用任意数量的字段 - 尽管因为它在后台使用 LIKE 查询,因此将搜索字段的数量限制为合理的数量将使数据库更容易进行搜索。
现在也是注意更改列表为您提供免费分页的好时机。默认值为每页显示 100 个项目。
Change list pagination 、 search boxes 、 filters 、 date-hierarchies 和 column-header-ordering 都像您认为的那样协同工作。
四、自定义管理员外观
显然,在每个管理页面的顶部都有“Django管理”是荒谬的。它只是占位符文本。
不过,你可以使用 Django 的模板系统来更改它。Django 管理员由 Django 本身提供支持,其界面使用 Django 自己的模板系统。
1、自定义项目的模板
在项目目录中创建一个 templates 目录(包含 manage.py 的目录)。模板可以位于文件系统上 Django 可以访问的任何位置。
(Django 以你的服务器运行的任何用户身份运行。但是,将模板保留在项目中是一个很好的约定。
打开设置文件(记住 mysite/settings.py )并在 TEMPLATES 设置中添加 DIRS 选项:
TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [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 模板时要检查的文件系统目录列表;这是一个搜索路径。
现在在 templates 中创建一个名为 admin 的目录,并将模板 admin/base_site.html 从 Django 本身源代码中默认的 Django 管理员模板目录中( django/contrib/admin/templates )复制到该目录中。
如果你很难找到 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 相同的操作 - 将其从默认目录复制到自定义目录中,然后进行更改。
2、自定义应用程序的模板
精明的读者会问:但是如果 DIRS 默认为空,Django 如何找到默认的管理模板?
答案是,由于 APP_DIRS 设置为 True ,Django 会自动在每个应用程序包中查找一个 templates/ 子目录,用作后备(不要忘记 django.contrib.admin 是一个应用程序)。
我们的投票应用程序不是很复杂,不需要自定义管理模板。但是,如果它变得更加复杂,并且需要修改 Django 的标准管理模板以实现其某些功能,
那么修改应用程序的模板比修改项目中的模板会更明智。这样,您可以将投票应用程序包含在任何新项目中,并确保它会找到所需的自定义模板。
有关 Django 如何找到其模板的更多信息,请参阅模板加载文档。
五、自定义管理索引页面
同样,你可能想要自定义 Django 管理索引页面的外观。
默认情况下,它按字母顺序显示 INSTALLED_APPS 中已注册到管理应用程序的所有应用。
您可能希望对布局进行重大更改。毕竟,索引可能是管理员最重要的页面,它应该易于使用。
要自定义的模板是 admin/index.html 。(与上一节中的 admin/base_site.html 执行相同的操作 – 将其从默认目录复制到自定义模板目录)。
编辑该文件,您将看到它使用了名为 app_list 的模板变量。该变量包含每个已安装的 Django 应用程序。
您可以以您认为最好的任何方式对指向特定于对象的管理页面的链接进行硬编码,而不是使用它。
熟悉管理员后,请阅读本教程的第 8 部分,了解如何使用第三方包。