Django 教程第四部分:Django 管理站点

现在我们已经为 LocalLibrary 网站创建了模型,我们将使用 Django 管理站点来添加一些“真实”的图书数据。首先,我们将向你展示如何将模型注册到管理站点,然后我们将向你展示如何登录并创建一些数据。在文章的最后,我们将展示一些可以进一步改进管理站点呈现方式的方法。

预备知识 首先完成:Django 教程第三部分:使用模型
目标 了解 Django 管理站点的优点和局限性,并使用它为我们的模型创建一些记录。

概述

Django 管理应用程序可以使用你的模型自动构建一个站点区域,你可以使用该区域创建、查看、更新和删除记录。这可以为你节省大量的开发时间,使测试模型和了解你是否有正确数据变得非常容易。管理应用程序对于生产中的数据管理也很有用,具体取决于网站的类型。Django 项目建议仅将其用于内部数据管理(即,仅供管理员或组织内部人员使用),因为以模型为中心的方法不一定是所有用户的最佳界面,并且暴露了模型中许多不必要的细节。

在你创建骨架项目时,包含管理应用程序到你的网站所需的所有配置都已自动完成(有关实际所需依赖项的信息,请参阅此处 Django 文档)。因此,你必须做的只是将模型注册到管理应用程序。在本文的最后,我们将简要演示如何进一步配置管理区域以更好地显示我们的模型数据。

注册模型后,我们将展示如何创建新的“超级用户”,登录站点,并创建一些图书、作者、图书实例和类型。这些将有助于测试我们在下一教程中开始创建的视图和模板。

注册模型

首先,在 catalog 应用程序中打开 admin.py (/django-locallibrary-tutorial/catalog/admin.py)。它目前看起来是这样的 — 请注意它已经导入了 django.contrib.admin

python
from django.contrib import admin

# Register your models here.

通过将以下文本复制到文件底部来注册模型。此代码导入模型,然后调用 admin.site.register 来注册它们。

python
from .models import Author, Genre, Book, BookInstance, Language

admin.site.register(Book)
admin.site.register(Author)
admin.site.register(Genre)
admin.site.register(BookInstance)
admin.site.register(Language)

注意:上面的行假定你接受了创建表示图书自然语言的模型的挑战(请参阅模型教程文章)!

这是向站点注册模型的最简单方法。管理站点是高度可定制的,我们将在后面更详细地讨论注册模型的其他方法。

创建超级用户

为了登录管理站点,我们需要一个启用了员工状态的用户帐户。为了查看和创建记录,我们还需要此用户拥有管理我们所有对象的权限。你可以使用 manage.py 创建一个对站点具有完全访问权限和所有必要权限的“超级用户”帐户。

manage.py 所在的同一目录中,调用以下命令创建超级用户。系统将提示你输入用户名、电子邮件地址和密码。

bash
python3 manage.py createsuperuser

此命令完成后,一个新的超级用户将被添加到数据库中。现在重新启动开发服务器,以便我们可以测试登录

bash
python3 manage.py runserver

登录并使用站点

要登录站点,请打开 /admin URL(例如,http://127.0.0.1:8000/admin)并输入你的新超级用户 ID 和密码凭据(你将被重定向到登录页面,然后在输入详细信息后返回到 /admin URL)。

站点的这一部分显示了我们所有的模型,按已安装的应用程序分组。你可以单击模型名称以转到列出其所有相关记录的屏幕,并且可以进一步单击这些记录进行编辑。你还可以直接单击每个模型旁边的添加链接,以开始创建该类型的记录。

Admin Site - Home page

单击图书右侧的添加链接以创建一本新书(这将显示一个类似下面对话框)。请注意,每个字段的标题、使用的控件类型以及 help_text(如果有)如何与你在模型中指定的值匹配。

输入字段的值。你可以通过按相应字段旁边的+按钮来创建新的作者或类型(如果已经创建了,也可以从列表中选择现有值)。完成后,你可以按保存保存并添加另一个保存并继续编辑来保存记录。

Admin Site - Book Add

注意:此时,我们希望你花一些时间向应用程序添加一些图书、作者、语言和类型(例如,幻想)。确保每个作者和类型都包含几本不同的图书(这将在我们稍后在系列文章中实现列表和详细视图时使它们更有趣)。

完成添加图书后,单击顶部书签中的主页链接返回主管理页面。然后单击图书链接以显示当前图书列表(或单击其他链接以查看其他模型列表)。现在你已经添加了一些图书,列表可能类似于下面的屏幕截图。显示了每本书的标题;这是我们在上一篇文章中指定的 Book 模型的 __str__() 方法返回的值。

Admin Site - List of book objects

在此列表中,你可以通过选择不需要的图书旁边的复选框,从操作下拉列表中选择删除…操作,然后按Go按钮来删除图书。你还可以通过按添加图书按钮来添加新图书。

你可以通过选择链接中的名称来编辑图书。下面显示的图书编辑页面几乎与“添加”页面相同。主要区别在于页面标题(更改图书)和删除历史在网站上查看按钮的添加(最后一个按钮出现是因为我们在模型中定义了 get_absolute_url() 方法)。

注意:单击在网站上查看按钮会引发 NoReverseMatch 异常,因为 get_absolute_url() 方法尝试 reverse() 尚未定义的命名 URL 映射(“book-detail”)。我们将在Django 教程第六部分:通用列表和详细视图中定义 URL 映射和相关视图。

Admin Site - Book Edit

现在导航回主页(使用面包屑导航中的主页链接),然后查看作者类型列表 — 你在添加新书时应该已经创建了相当多的内容,但请随意添加更多。

你不会拥有任何图书实例,因为它们不是从图书创建的(尽管你可以从 BookInstance 创建 Book — 这是 ForeignKey 字段的性质)。导航回主页并按相关添加按钮以显示下面的添加图书实例屏幕。请注意大型、全局唯一的 ID,它可用于在图书馆中单独识别一本书的单个副本。

Admin Site - BookInstance Add

为你的每本书创建多个这些记录。将至少一些记录的状态设置为可用,将其他记录的状态设置为借出中。如果状态不是可用,则还要设置未来的归还日期

就是这样!你现在已经学会了如何设置和使用管理站点。你还为 BookBookInstanceGenreLanguageAuthor 创建了记录,一旦我们创建了自己的视图和模板,我们就可以使用这些记录。

高级配置

Django 很好地利用注册模型中的信息创建了一个基本的管理站点

  • 每个模型都有一个由模型 __str__() 方法创建的字符串标识的单个记录列表,并链接到用于编辑的详细视图/表单。默认情况下,此视图顶部有一个操作菜单,你可以使用该菜单对记录执行批量删除操作。
  • 用于编辑和添加记录的模型详细记录表单包含模型中的所有字段,按其声明顺序垂直布局。

你可以进一步自定义界面,使其更易于使用。你可以做的一些事情是

  • 列表视图

    • 为每个记录添加额外的字段/信息。
    • 添加过滤器以根据日期或其他选择值(例如,图书借阅状态)选择要列出的记录。
    • 向列表视图中的操作菜单添加额外的选项,并选择此菜单在表单上的显示位置。
  • 详细视图

    • 选择要显示(或排除)的字段,以及它们的顺序、分组、是否可编辑、使用的控件、方向等。
    • 向记录添加相关字段以允许内联编辑(例如,在你创建作者记录时添加添加和编辑图书记录的功能)。

在本节中,我们将介绍一些将改进我们的 LocalLibrary 界面的更改,包括向 BookAuthor 模型列表添加更多信息,并改进其编辑视图的布局。我们不会更改 LanguageGenre 模型的呈现,因为它们每个只有一个字段,因此这样做没有实际好处!

你可以在Django Admin site(Django 文档)中找到所有管理站点自定义选项的完整参考。

注册 ModelAdmin 类

要更改模型在管理界面中的显示方式,你需要定义一个 ModelAdmin 类(描述布局)并将其注册到模型。

让我们从 Author 模型开始。在 catalog 应用程序中打开 admin.py (/django-locallibrary-tutorial/catalog/admin.py)。注释掉你为 Author 模型进行的原始注册(在其前面加上 #)

python
# admin.site.register(Author)

现在添加一个新的 AuthorAdmin 和注册,如下所示。

python
# Define the admin class
class AuthorAdmin(admin.ModelAdmin):
    pass

# Register the admin class with the associated model
admin.site.register(Author, AuthorAdmin)

现在我们将为 BookBookInstance 添加 ModelAdmin 类。我们再次需要注释掉原始注册

python
# admin.site.register(Book)
# admin.site.register(BookInstance)

现在创建并注册新模型;为了本演示的目的,我们将改用 @register 装饰器来注册模型(这与 admin.site.register() 语法完全相同)

python
# Register the Admin classes for Book using the decorator
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    pass

# Register the Admin classes for BookInstance using the decorator
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
    pass

目前,我们所有的管理类都是空的(请参见 pass),因此管理行为将保持不变!我们现在可以扩展这些类来定义模型特定的管理行为。

配置列表视图

LocalLibrary 目前使用从模型 __str__() 方法生成的对象名称列出所有作者。当你只有少数作者时,这很好,但一旦你有许多作者,你可能会遇到重复。为了区分它们,或者仅仅因为你想显示每个作者更多有趣的信息,你可以使用 list_display 将附加字段添加到视图中。

用下面的代码替换你的 AuthorAdmin 类。列表中要显示的字段按所需顺序在元组中声明,如所示(这些与你的原始模型中指定的名称相同)。

python
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')

现在导航到你网站中的作者列表。上面字段现在应该显示,如下所示

Admin Site - Improved Author List

对于我们的 Book 模型,我们还将显示 authorgenreauthor 是一个 ForeignKey 字段(一对多)关系,因此将由关联记录的 __str__() 值表示。用下面的版本替换 BookAdmin 类。

python
class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'display_genre')

不幸的是,我们不能直接在 list_display 中指定 genre 字段,因为它是一个 ManyToManyField(Django 阻止这样做是因为这样做会有很大的数据库访问“成本”)。相反,我们将定义一个 display_genre 函数来获取信息作为字符串(这是我们上面调用的函数;我们将在下面定义它)。

注意:在此处获取 genre 可能不是一个好主意,因为数据库操作的“成本”。我们之所以向你展示,是因为在你的模型中调用函数在其他原因下可能非常有用——例如,在列表中的每个项目旁边添加一个删除链接。

将以下代码添加到你的 Book 模型 (models.py) 中。这将从 genre 字段的前三个值(如果存在)创建一个字符串,并为此方法创建一个可在管理站点中使用的 short_description

python
def display_genre(self):
    """Create a string for the Genre. This is required to display genre in Admin."""
    return ', '.join(genre.name for genre in self.genre.all()[:3])

display_genre.short_description = 'Genre'

保存模型并更新管理后,打开你的网站并转到图书列表页面;你应该看到一个如下所示的图书列表

Admin Site - Improved Book List

Genre 模型(以及 Language 模型,如果你定义了一个)都只有一个字段,因此没有必要为它们创建额外的模型来显示额外的字段。

注意:值得更新 BookInstance 模型列表以至少显示状态和预期归还日期。我们已将其作为本文末尾的一个挑战!

添加列表过滤器

一旦列表中有很多项目,能够过滤显示哪些项目会很有用。这是通过在 list_filter 属性中列出字段来完成的。用下面的代码片段替换你当前的 BookInstanceAdmin 类。

python
class BookInstanceAdmin(admin.ModelAdmin):
    list_filter = ('status', 'due_back')

列表视图现在将包含一个位于右侧的过滤器框。请注意如何选择日期和状态来过滤值

Admin Site - BookInstance List Filters

组织详细视图布局

默认情况下,详细视图垂直布局所有字段,按其在模型中的声明顺序。你可以更改声明顺序、显示(或排除)的字段、是否使用节来组织信息、字段是水平还是垂直显示,甚至是在管理表单中使用哪些编辑控件。

注意:LocalLibrary 模型相对简单,所以我们没有很大的必要更改布局;不过,我们还是会做一些更改,只是为了向你展示如何操作。

控制哪些字段显示和布局

更新你的 AuthorAdmin 类以添加 fields 行,如下所示

python
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')

    fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]

fields 属性仅列出要在表单上显示的字段,按顺序排列。字段默认垂直显示,但如果你将它们进一步分组到元组中(如上面“日期”字段所示),则会水平显示。

在你的网站中,转到作者详细视图 — 它现在应该显示如下

Admin Site - Improved Author Detail

注意:你还可以使用 exclude 属性声明要从表单中排除的属性列表(模型中的所有其他属性都将显示)。

详细视图分段

你可以使用 fieldsets 属性在详细表单中添加“节”以分组相关模型信息。

BookInstance 模型中,我们有与图书相关的信息(即 nameimprintid)以及何时可用(statusdue_back)。我们可以将它们添加到我们的 BookInstanceAdmin 类中,如下所示,使用 fieldsets 属性。

python
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
    list_filter = ('status', 'due_back')

    fieldsets = (
        (None, {
            'fields': ('book', 'imprint', 'id')
        }),
        ('Availability', {
            'fields': ('status', 'due_back')
        }),
    )

每个节都有自己的标题(如果你不需要标题,则为 None)和一个关联的字段元组在字典中 — 格式描述起来很复杂,但如果你查看上面的代码片段,则很容易理解。

现在导航到你网站中的图书实例视图;表单应该显示如下

Admin Site - Improved BookInstance Detail with sections

关联记录的内联编辑

有时能够同时添加关联记录是有意义的。例如,在同一详细页面上同时拥有图书信息和有关你拥有的特定副本的信息可能是有意义的。

你可以通过声明 inlines,类型为 TabularInline(水平布局)或 StackedInline(垂直布局,就像默认的模型布局)来实现这一点。你可以通过在 BookAdmin 中指定 inlines 来将 BookInstance 信息内联到我们的 Book 详细信息中

python
class BooksInstanceInline(admin.TabularInline):
    model = BookInstance

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'display_genre')

    inlines = [BooksInstanceInline]

现在导航到你网站中某个 Book 的视图 — 在底部,你应该会看到与这本书相关的图书实例(紧邻图书的类型字段下方)

Admin Site - Book with Inlines

在这种情况下,我们所做的只是声明我们的表格内联类,它只添加内联模型中的所有字段。你可以为布局指定各种附加信息,包括要显示的字段、它们的顺序、它们是否只读等(有关更多信息,请参阅TabularInline)。

注意:此功能有一些令人头痛的限制!在上面的屏幕截图中,我们有三个现有的图书实例,然后是三个新图书实例的占位符(它们看起来非常相似!)。默认情况下没有备用图书实例,只需使用添加另一个图书实例链接添加它们,或者能够将 BookInstances 列为非可读链接,这样会更好。第一个选项可以通过在 BooksInstanceInline 模型中将 extra 属性设置为 0 来完成,你可以自己尝试一下。

挑战自我

我们在这部分学到了很多东西,现在是时候尝试一些事情了。

  1. 对于 BookInstance 列表视图,添加代码以显示图书、状态、归还日期和 ID(而不是默认的 __str__() 文本)。
  2. 使用与我们对 Book/BookInstance 相同的方法,将 Book 项的内联列表添加到 Author 详细视图中。

总结

就是这样!你现在已经学会了如何以最简单和改进的形式设置管理站点,如何创建超级用户,以及如何导航管理站点并查看、删除和更新记录。在此过程中,你创建了一批图书、图书实例、类型和作者,一旦我们创建了自己的视图和模板,我们就可以列出和显示它们。

延伸阅读