Django 管理站点

Django 最强大的部分之一是自动管理界面。它从模型中读取元数据,以提供一个快速、以模型为中心界面,受信任的用户可以在其中管理网站上的内容。建议将管理界面用作组织的内部管理工具。它不适用于构建整个前端。

管理界面有许多用于自定义的钩子,但请注意不要尝试专门使用这些钩子。如果你需要提供一个更以流程为中心的界面,该界面抽象了数据库表和字段的实现细节,那么可能需要编写自己的视图。

在本文档中,我们将讨论如何激活、使用和自定义 Django 的管理界面。

概览

管理界面在 startproject 使用的默认项目模板中启用。

如果你不使用默认项目模板,则以下为要求

  1. 添加 'django.contrib.admin' 及其依赖项 - django.contrib.authdjango.contrib.contenttypesdjango.contrib.messagesdjango.contrib.sessions - 到 INSTALLED_APPS 设置。
  2. TEMPLATES 设置中配置 DjangoTemplates 后端,并在 OPTIONS'context_processors' 选项中使用 django.template.context_processors.requestdjango.contrib.auth.context_processors.authdjango.contrib.messages.context_processors.messages
  3. 如果您自定义了 MIDDLEWARE 设置,则必须包含 django.contrib.auth.middleware.AuthenticationMiddlewaredjango.contrib.messages.middleware.MessageMiddleware
  4. 将管理的 URL 连接到您的 URLconf.

完成这些步骤后,您将可以通过访问您连接到的 URL(默认情况下为 /admin/)来使用管理站点。

如果您需要创建一个用户来登录,请使用 createsuperuser 命令。默认情况下,登录到管理需要用户将 is_staff 属性设置为 True

最后,确定应用程序的哪些模型应该在管理界面中可编辑。对于这些模型中的每一个,请按照 ModelAdmin 中所述将其注册到管理。

其他主题

另请参见

有关在生产中提供与管理关联的静态文件(图像、JavaScript 和 CSS)的信息,请参阅 提供文件

遇到问题?请尝试常见问题解答:管理员

ModelAdmin 对象

class ModelAdmin

ModelAdmin 类是管理员界面中模型的表示形式。通常,这些内容存储在应用程序中名为 admin.py 的文件中。我们来看看 ModelAdmin 的一个示例

from django.contrib import admin
from myapp.models import Author


class AuthorAdmin(admin.ModelAdmin):
    pass


admin.site.register(Author, AuthorAdmin)

您需要 ModelAdmin 对象吗?

在前面的示例中,ModelAdmin 类未定义任何自定义值(尚未定义)。因此,将提供默认管理员界面。如果您对默认管理员界面感到满意,则根本不需要定义 ModelAdmin 对象 - 您可以注册模型类而不提供 ModelAdmin 描述。前面的示例可以简化为

from django.contrib import admin
from myapp.models import Author

admin.site.register(Author)

register 装饰器

register(*models, site=django.contrib.admin.sites.site)

还有一个装饰器用于注册您的 ModelAdmin

from django.contrib import admin
from .models import Author


@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    pass

它给出了一个或多个模型类来使用 ModelAdmin 注册。如果您正在使用自定义 AdminSite,请使用 site 关键字参数传递

from django.contrib import admin
from .models import Author, Editor, Reader
from myproject.admin_site import custom_admin_site


@admin.register(Author, Reader, Editor, site=custom_admin_site)
class PersonAdmin(admin.ModelAdmin):
    pass

如果您必须在 __init__() 方法中引用您的模型管理员类,则不能使用此装饰器,例如 super(PersonAdmin, self).__init__(*args, **kwargs)。您可以使用 super().__init__(*args, **kwargs)

发现管理文件

当你在 INSTALLED_APPS 设置中放入 'django.contrib.admin' 时,Django 会自动在每个应用程序中查找 admin 模块并导入它。

class apps.AdminConfig

这是用于管理的默认 AppConfig 类。它在 Django 启动时调用 autodiscover()

class apps.SimpleAdminConfig

此类与 AdminConfig 类似,但它不会调用 autodiscover()

default_site

指向默认管理网站的类的点分导入路径或返回网站实例的可调用对象。默认为 'django.contrib.admin.sites.AdminSite'。有关用法,请参阅 覆盖默认管理网站

autodiscover()

此函数尝试在每个已安装的应用程序中导入 admin 模块。此类模块应向管理员注册模型。

通常,您无需直接调用此函数,因为 AdminConfig 在 Django 启动时会调用此函数。

如果您使用自定义 AdminSite,则通常将所有 ModelAdmin 子类导入到您的代码中,并将其注册到自定义 AdminSite 中。在这种情况下,为了禁用自动发现,您应在 INSTALLED_APPS 设置中将 'django.contrib.admin.apps.SimpleAdminConfig' 替换为 'django.contrib.admin'

ModelAdmin 选项

ModelAdmin 非常灵活。它有几个选项用于处理界面的自定义。所有选项均在 ModelAdmin 子类上定义

from django.contrib import admin


class AuthorAdmin(admin.ModelAdmin):
    date_hierarchy = "pub_date"
ModelAdmin.actions

在更改列表页面上可用的操作列表。有关详细信息,请参见 管理操作

ModelAdmin.actions_on_top
ModelAdmin.actions_on_bottom

控制操作栏在页面上的显示位置。默认情况下,管理更改列表在页面顶部显示操作(actions_on_top = True; actions_on_bottom = False)。

ModelAdmin.actions_selection_counter

控制是否在操作下拉菜单旁边显示一个选择计数器。默认情况下,管理员变更列表会显示它 (actions_selection_counter = True)。

ModelAdmin.date_hierarchy

date_hierarchy 设置为模型中 DateFieldDateTimeField 的名称,变更列表页面将包含按该字段进行基于日期的向下钻取导航。

示例

date_hierarchy = "pub_date"

您还可以使用 __ 查找指定相关模型上的字段,例如

date_hierarchy = "author__pub_date"

它将根据可用数据智能地填充自身,例如,如果所有日期都在一个月内,它将仅显示按天钻取。

注意

date_hierarchy 在内部使用 QuerySet.datetimes()。当启用时区支持时,请参阅其文档以了解一些注意事项 (USE_TZ = True)。

ModelAdmin.empty_value_display

此属性会覆盖记录中为空(None、空字符串等)的字段的默认显示值。默认值为 -(破折号)。例如

from django.contrib import admin


class AuthorAdmin(admin.ModelAdmin):
    empty_value_display = "-empty-"

您还可以使用 AdminSite.empty_value_display 覆盖所有管理页面的 empty_value_display,或针对特定字段执行此操作

from django.contrib import admin


class AuthorAdmin(admin.ModelAdmin):
    list_display = ["name", "title", "view_birth_date"]

    @admin.display(empty_value="???")
    def view_birth_date(self, obj):
        return obj.birth_date
ModelAdmin.exclude

如果给定了此属性,它应为要从表单中排除的字段名称列表。

例如,让我们考虑以下模型

from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=100)
    title = models.CharField(max_length=3)
    birth_date = models.DateField(blank=True, null=True)

如果您想要一个仅包含 nametitle 字段的 Author 模型表单,则可以像这样指定 fieldsexclude

from django.contrib import admin


class AuthorAdmin(admin.ModelAdmin):
    fields = ["name", "title"]


class AuthorAdmin(admin.ModelAdmin):
    exclude = ["birth_date"]

由于 Author 模型只有三个字段,即 nametitlebirth_date,因此由上述声明生成的表单将包含完全相同的字段。

ModelAdmin.fields

使用 fields 选项可在“添加”和“更改”页面上的表单中进行简单的布局更改,例如仅显示可用字段的子集、修改其顺序或将它们分组到行中。例如,您可以为 django.contrib.flatpages.models.FlatPage 模型定义一个更简单的管理表单版本,如下所示

class FlatPageAdmin(admin.ModelAdmin):
    fields = ["url", "title", "content"]

在上述示例中,只有字段 urltitlecontent 将按顺序显示在表单中。 fields 可以包含在 ModelAdmin.readonly_fields 中定义的值,以只读方式显示。

对于更复杂的布局需求,请参见 fieldsets 选项。

fields 选项接受与 list_display 相同类型的的值,但它不接受可调用对象。模型和模型管理方法的名称仅在它们列在 readonly_fields 中时才会使用。

要在同一行显示多个字段,请将这些字段包装在它们自己的元组中。在此示例中, urltitle 字段将显示在同一行,而 content 字段将显示在其下方自己的行上

class FlatPageAdmin(admin.ModelAdmin):
    fields = [("url", "title"), "content"]

可能与 ModelAdmin.fieldsets 选项混淆

fields 选项不应与 fieldsets 选项中的 fields 字典键混淆,如下一节所述。

如果 fieldsfieldsets 选项均不存在,Django 将默认显示每个非 AutoFieldeditable=True 的字段,在单个字段集中,按照模型中字段定义的顺序显示。

ModelAdmin.fieldsets

设置 fieldsets 以控制管理“添加”和“更改”页面的布局。

fieldsets 是一个 2 元组列表,其中每个 2 元组表示管理表单页面上的一个 <fieldset>。(<fieldset> 是表单的一个“部分”。)

2 元组的格式为 (name, field_options),其中 name 是表示字段集标题的字符串,field_options 是有关字段集的信息字典,包括要在其中显示的字段列表。

一个完整的示例,取自 django.contrib.flatpages.models.FlatPage 模型

from django.contrib import admin


class FlatPageAdmin(admin.ModelAdmin):
    fieldsets = [
        (
            None,
            {
                "fields": ["url", "title", "content", "sites"],
            },
        ),
        (
            "Advanced options",
            {
                "classes": ["collapse"],
                "fields": ["registration_required", "template_name"],
            },
        ),
    ]

这将生成一个类似于以下内容的管理页面

../../../_images/fieldsets.png

如果 fieldsetsfields 选项均不存在,Django 将默认显示每个非 AutoFieldeditable=True 的字段,在单个字段集中,按照模型中字段定义的顺序显示。

field_options 字典可以有以下键

  • fields

    要在该字段集中显示的字段名称列表或元组。此键是必需的。

    示例

    {
        "fields": ["first_name", "last_name", "address", "city", "state"],
    }
    

    fields 选项一样,若要在同一行显示多个字段,请将这些字段包装在它们自己的元组中。在此示例中,first_namelast_name 字段将显示在同一行

    {
        "fields": [("first_name", "last_name"), "address", "city", "state"],
    }
    

    fields 可以包含 readonly_fields 中定义的值,以只读方式显示。

    如果你将可调用对象的名称添加到 fields,则与 fields 选项一样,适用相同的规则:可调用对象必须列在 readonly_fields 中。

  • classes

    包含要应用于字段集的额外 CSS 类的列表或元组。

    示例

    {
        "classes": ["wide", "extrapretty"],
    }
    

    默认管理站点样式表定义的两个有用的类是 collapsewide。具有 collapse 样式的字段集最初将在管理中折叠,并替换为一个小的“点击展开”链接。具有 wide 样式的字段集将获得额外的水平空间。

  • description

    一个可选的额外文本字符串,显示在每个字段集的顶部,在字段集的标题下方。由于其布局,此字符串不会为 TabularInline 呈现。

    请注意,当在管理界面中显示此值时,此值不会转义 HTML。如果您愿意,这将允许您包含 HTML。或者,您可以使用纯文本和 django.utils.html.escape() 来转义任何 HTML 特殊字符。

ModelAdmin.filter_horizontal

默认情况下,ManyToManyField 在管理站点中显示为 <select multiple>。但是,在选择许多项目时,使用多选框可能很困难。将 ManyToManyField 添加到此列表中,将改为使用一个巧妙的不显眼的 JavaScript “筛选器”界面,该界面允许在选项中进行搜索。未选中的选项和选中的选项并排出现在两个框中。请参阅 filter_vertical 以使用垂直界面。

ModelAdmin.filter_vertical

filter_horizontal 相同,但使用筛选器界面的垂直显示,其中未选定选项的框显示在选定选项的框上方。

ModelAdmin.form

默认情况下,会为您的模型动态创建一个 ModelForm。它用于创建添加/更改页面上显示的表单。您可轻松提供自己的 ModelForm 以覆盖添加/更改页面上的任何默认表单行为。或者,您可以使用 ModelAdmin.get_form() 方法自定义默认表单,而不是指定一个全新的表单。

有关示例,请参阅部分 向管理中添加自定义验证

省略 Meta.model 属性

如果您在 ModelForm 上定义了 Meta.model 属性,则还必须定义 Meta.fields 属性(或 Meta.exclude 属性)。但是,由于管理有自己的字段定义方式,因此 Meta.fields 属性将被忽略。

如果 ModelForm 仅用于管理,最简单的解决方案是省略 Meta.model 属性,因为 ModelAdmin 将提供要使用的正确模型。或者,您可以在 Meta 类中设置 fields = [] 以满足 ModelForm 上的验证。

ModelAdmin.exclude 优先

如果您的 ModelFormModelAdmin 都定义了 exclude 选项,那么 ModelAdmin 优先

from django import forms
from django.contrib import admin
from myapp.models import Person


class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        exclude = ["name"]


class PersonAdmin(admin.ModelAdmin):
    exclude = ["age"]
    form = PersonForm

在上面的示例中,“age”字段将被排除,但“name”字段将包含在生成的表单中。

ModelAdmin.formfield_overrides

这提供了一种快速且简单的方法来覆盖一些 Field 选项以在管理中使用。 formfield_overrides 是一个字典,将字段类映射到在构造时传递给字段的参数字典。

由于这有点抽象,我们来看一个具体的示例。 formfield_overrides 最常见的用法是为特定类型的字段添加自定义小部件。因此,假设我们编写了一个 RichTextEditorWidget,我们希望将其用于大型文本字段,而不是默认的 <textarea>。以下是如何做到这一点

from django.contrib import admin
from django.db import models

# Import our custom widget and our model from where they're defined
from myapp.models import MyModel
from myapp.widgets import RichTextEditorWidget


class MyModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField: {"widget": RichTextEditorWidget},
    }

请注意,字典中的键是实际的字段类,而不是字符串。该值是另一个字典;这些参数将传递给表单字段的 __init__() 方法。有关详细信息,请参阅 表单 API

警告

如果您想对关系字段(即 ForeignKeyManyToManyField)使用自定义小部件,请确保您未在 raw_id_fieldsradio_fieldsautocomplete_fields 中包含该字段的名称。

formfield_overrides 不会允许您更改已设置 raw_id_fieldsradio_fieldsautocomplete_fields 的关系字段上的小部件。这是因为 raw_id_fieldsradio_fieldsautocomplete_fields 隐含了它们自己的自定义小部件。

ModelAdmin.inlines

另请参见下面的 InlineModelAdmin 对象以及 ModelAdmin.get_formsets_with_inlines()

ModelAdmin.list_display

设置 list_display 以控制在管理的更改列表页面上显示哪些字段。

示例

list_display = ["first_name", "last_name"]

如果您未设置 list_display,管理站点将显示一个单列,显示每个对象的 __str__() 表示形式。

有四种类型的值可用于 list_display。除了最简单的值,其他值都可以使用 display() 装饰器,该装饰器用于自定义字段的显示方式

  • 模型字段的名称。例如

    class PersonAdmin(admin.ModelAdmin):
        list_display = ["first_name", "last_name"]
    
  • 接受一个参数(模型实例)的可调用对象。例如

    @admin.display(description="Name")
    def upper_case_name(obj):
        return f"{obj.first_name} {obj.last_name}".upper()
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = [upper_case_name]
    
  • 表示接受一个参数(模型实例)的 ModelAdmin 方法的字符串。例如

    class PersonAdmin(admin.ModelAdmin):
        list_display = ["upper_case_name"]
    
        @admin.display(description="Name")
        def upper_case_name(self, obj):
            return f"{obj.first_name} {obj.last_name}".upper()
    
  • 表示模型属性或方法(没有任何必需参数)的字符串。例如

    from django.contrib import admin
    from django.db import models
    
    
    class Person(models.Model):
        name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        @admin.display(description="Birth decade")
        def decade_born_in(self):
            decade = self.birthday.year // 10 * 10
            return f"{decade}’s"
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ["name", "decade_born_in"]
    

关于 list_display 的一些特殊情况

  • 如果字段是 ForeignKey,Django 将显示相关对象的 __str__()

  • ManyToManyField 字段不受支持,因为这将需要为表中的每一行执行一个单独的 SQL 语句。如果您仍然希望执行此操作,请为您的模型提供一个自定义方法,并将该方法的名称添加到 list_display 中。(请参见下文以了解 list_display 中的自定义方法的更多信息。)

  • 如果字段是 BooleanField,Django 将显示漂亮的“是”、“否”或“未知”图标,而不是 TrueFalseNone

  • 如果给定的字符串是模型、ModelAdmin 或可调用对象的某个方法,Django 将默认对输出进行 HTML 转义。要转义用户输入并允许您自己的未转义标签,请使用 format_html()

    这是一个完整的示例模型

    from django.contrib import admin
    from django.db import models
    from django.utils.html import format_html
    
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        @admin.display
        def colored_name(self):
            return format_html(
                '<span style="color: #{};">{} {}</span>',
                self.color_code,
                self.first_name,
                self.last_name,
            )
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ["first_name", "last_name", "colored_name"]
    
  • 正如某些示例已经演示的那样,当使用可调用对象、模型方法或 ModelAdmin 方法时,您可以通过使用 display() 装饰器包装可调用对象并传递 description 参数来自定义列标题。

  • 如果字段的值为 None、空字符串或没有元素的可迭代对象,Django 将显示 -(破折号)。您可以使用 AdminSite.empty_value_display 覆盖此值

    from django.contrib import admin
    
    admin.site.empty_value_display = "(None)"
    

    您还可以使用 ModelAdmin.empty_value_display

    class PersonAdmin(admin.ModelAdmin):
        empty_value_display = "unknown"
    

    或在字段级别

    class PersonAdmin(admin.ModelAdmin):
        list_display = ["name", "birth_date_view"]
    
        @admin.display(empty_value="unknown")
        def birth_date_view(self, obj):
            return obj.birth_date
    
  • 如果给定的字符串是模型的方法,ModelAdmin 或返回 TrueFalseNone 的可调用对象,如果使用 display() 装饰器包装该方法并传递 boolean 参数(其值设置为 True),Django 将显示一个漂亮的“是”、“否”或“未知”图标

    from django.contrib import admin
    from django.db import models
    
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        @admin.display(boolean=True)
        def born_in_fifties(self):
            return 1950 <= self.birthday.year < 1960
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ["name", "born_in_fifties"]
    
  • list_display 中,__str__() 方法与任何其他模型方法一样有效,因此这样做完全可以

    list_display = ["__str__", "some_other_field"]
    
  • 通常,list_display 中不是实际数据库字段的元素不能用于排序(因为 Django 在数据库级别执行所有排序)。

    但是,如果 list_display 的元素表示某个数据库字段,则可以通过对方法使用 display() 装饰器来指示此事实,并传递 ordering 参数

    from django.contrib import admin
    from django.db import models
    from django.utils.html import format_html
    
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        @admin.display(ordering="first_name")
        def colored_first_name(self):
            return format_html(
                '<span style="color: #{};">{}</span>',
                self.color_code,
                self.first_name,
            )
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ["first_name", "colored_first_name"]
    

    上述内容将告诉 Django 在尝试按 colored_first_name 在管理中进行排序时,按 first_name 字段进行排序。

    要使用 ordering 参数指示降序,可以在字段名称前加上连字符前缀。使用上述示例,这将如下所示

    @admin.display(ordering="-first_name")
    def colored_first_name(self): ...
    

    ordering 参数支持查询查找,以按相关模型中的值进行排序。此示例在列表显示中包含“作者名”列,并允许按名进行排序

    class Blog(models.Model):
        title = models.CharField(max_length=255)
        author = models.ForeignKey(Person, on_delete=models.CASCADE)
    
    
    class BlogAdmin(admin.ModelAdmin):
        list_display = ["title", "author", "author_first_name"]
    
        @admin.display(ordering="author__first_name")
        def author_first_name(self, obj):
            return obj.author.first_name
    

    查询表达式 可与 ordering 参数一起使用

    from django.db.models import Value
    from django.db.models.functions import Concat
    
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
    
        @admin.display(ordering=Concat("first_name", Value(" "), "last_name"))
        def full_name(self):
            return self.first_name + " " + self.last_name
    
  • list_display 的元素也可以是属性

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
    
        @property
        @admin.display(
            ordering="last_name",
            description="Full name of the person",
            boolean=False,
        )
        def full_name(self):
            return self.first_name + " " + self.last_name
    
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ["full_name"]
    

    请注意,@property 必须位于 @display 之上。如果您使用旧方法(直接设置与显示相关的属性,而不是使用 display() 装饰器),请注意必须使用 property() 函数,而不是 @property 装饰器

    def my_property(self):
        return self.first_name + " " + self.last_name
    
    
    my_property.short_description = "Full name of the person"
    my_property.admin_order_field = "last_name"
    my_property.boolean = False
    
    full_name = property(my_property)
    
    在 Django 5.0 中更改

    添加了对属性中 boolean 属性的支持。

  • list_display 中的字段名称还将作为 CSS 类出现在 HTML 输出中,形式为 column-<field_name>,位于每个 <th> 元素上。例如,这可用于在 CSS 文件中设置列宽。

  • Django 将尝试按此顺序解释 list_display 的每个元素

    • 模型的字段。
    • 一个可调用对象。
    • 表示 ModelAdmin 属性的字符串。
    • 表示模型属性的字符串。

    例如,如果您将 first_name 作为模型字段和 ModelAdmin 属性,则将使用模型字段。

使用 list_display_links 控制 list_display 中哪些字段以及是否链接到对象的“更改”页面。

默认情况下,更改列表页面将链接第一列(list_display 中指定的第一个字段)到每个项目的更改页面。但 list_display_links 允许您更改此设置。

  • 将其设置为 None 以完全不显示链接。

  • 将其设置为一个字段列表或元组(与 list_display 相同的格式),将这些字段的列转换为链接。

    您可以指定一个或多个字段。只要这些字段出现在 list_display 中,Django 就不关心链接的字段数量(多少或多少)。唯一的要求是,如果您想以这种方式使用 list_display_links,则必须定义 list_display

在此示例中,first_namelast_name 字段将在更改列表页面上链接。

class PersonAdmin(admin.ModelAdmin):
    list_display = ["first_name", "last_name", "birthday"]
    list_display_links = ["first_name", "last_name"]

在此示例中,更改列表页面网格将不显示任何链接。

class AuditEntryAdmin(admin.ModelAdmin):
    list_display = ["timestamp", "message"]
    list_display_links = None
ModelAdmin.list_editable

list_editable 设置为模型上允许在更改列表页面上进行编辑的字段名称列表。也就是说,list_editable 中列出的字段将作为更改列表页面上的表单小部件显示,允许用户一次编辑和保存多行。

注意

list_editable 会以特定方式与其他一些选项交互;您应注意以下规则:

  • list_editable 中的任何字段也必须在 list_display 中。您无法编辑未显示的字段!
  • 同一字段不能同时列在 list_editablelist_display_links 中 - 一个字段不能同时是表单和链接。

如果违反了其中任何一条规则,您将收到验证错误。

ModelAdmin.list_filter

设置 list_filter 以激活管理页面更改列表页右侧的过滤器。

最简单的 list_filter 会采用一个字段名称列表或元组来激活过滤,但还有更多高级选项可用。有关详细信息,请参阅 ModelAdmin 列表过滤器

ModelAdmin.list_max_show_all

设置 list_max_show_all 以控制“显示全部”管理更改列表页上可以显示多少项。仅当结果总数小于或等于此设置时,管理页面才会在更改列表上显示“显示全部”链接。默认情况下,此设置设为 200

ModelAdmin.list_per_page

设置 list_per_page 以控制每个分页管理更改列表页上显示多少项。默认情况下,此设置设为 100

list_select_related 设置为告诉 Django 在检索管理员更改列表页面上的对象列表时使用 select_related()。这可以为你节省大量数据库查询。

该值应为布尔值、列表或元组。默认值为 False

当值为 True 时,将始终调用 select_related()。当值设置为 False 时,Django 将查看 list_display,并在存在任何 ForeignKey 时调用 select_related()

如果你需要更精细的控制,请使用元组(或列表)作为 list_select_related 的值。空元组将阻止 Django 调用 select_related()。任何其他元组都将直接作为参数传递给 select_related。例如

class ArticleAdmin(admin.ModelAdmin):
    list_select_related = ["author", "category"]

将调用 select_related('author', 'category')

如果你需要根据请求指定动态值,则可以实现 get_list_select_related() 方法。

注意

ModelAdmin 在更改列表的 QuerySet 上已经调用 select_related() 时,将忽略此属性。

ModelAdmin.ordering

设置 ordering 以指定对象列表在 Django 管理视图中排序的方式。这应为与模型的 ordering 参数格式相同的列表或元组。

如果未提供此项,Django 管理将使用模型的默认排序。

如果您需要指定动态顺序(例如取决于用户或语言),您可以实现 get_ordering() 方法。

排序方面的性能考虑

为了确保结果的确定性排序,如果找不到可提供全部排序的单个或唯一一起的字段集,则变更列表会将 pk 添加到排序中。

例如,如果默认排序按非唯一的 name 字段进行,则变更列表按 namepk 进行排序。如果您有大量行并且没有对 namepk 建立索引,则这可能会表现不佳。

ModelAdmin.paginator

用于分页的分页器类。默认情况下,使用 django.core.paginator.Paginator。如果自定义分页器类的构造器接口与 django.core.paginator.Paginator 不同,您还需要为 ModelAdmin.get_paginator() 提供实现。

ModelAdmin.prepopulated_fields

prepopulated_fields 设置为将字段名称映射到它应从中预填充的字段的字典

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ["title"]}

设置后,给定字段将使用一些 JavaScript 从分配的字段中填充。此功能的主要用途是从一个或多个其他字段自动生成 SlugField 字段的值。生成的值通过连接源字段的值产生,然后将该结果转换为有效的 slug(例如,用破折号替换空格,并将 ASCII 字母小写)。

预填充字段在值保存后不会被 JavaScript 修改。通常不希望 slug 发生更改(如果 slug 在其中使用,则会导致对象的 URL 更改)。

prepopulated_fields 不接受 DateTimeFieldForeignKeyOneToOneFieldManyToManyField 字段。

ModelAdmin.preserve_filters

默认情况下,在创建、编辑或删除对象后,应用的筛选器将保留在列表视图中。你可以通过将此属性设置为 False 来清除筛选器。

ModelAdmin.show_facets
Django 5.0 中的新增功能。

控制是否在管理更改列表中为筛选器显示分类计数。默认为 ShowFacets.ALLOW

显示时,分类计数将根据当前应用的筛选器进行更新。

class ShowFacets
Django 5.0 中的新增功能。

ModelAdmin.show_facets 允许的值的枚举。

ALWAYS

始终显示分类计数。

ALLOW

当提供 _facets 查询字符串参数时,显示分类计数。

NEVER

从不显示小分类计数。

show_facets 设置为所需的 ShowFacets 值。例如,要在不提供查询参数的情况下始终显示小分类计数

from django.contrib import admin


class MyModelAdmin(admin.ModelAdmin):
    ...
    # Have facets always shown for this model admin.
    show_facets = admin.ShowFacets.ALWAYS

小分类的性能注意事项

启用小分类过滤器将根据过滤器数量增加管理变更列表页面上的查询数量。这些查询可能会导致性能问题,特别是对于大型数据集。在这些情况下,可以将 show_facets 设置为 ShowFacets.NEVER 以完全禁用小分类。

ModelAdmin.radio_fields

默认情况下,Django 的管理界面使用选择框界面 (<select>) 来处理 ForeignKey 或设置了 choices 的字段。如果某个字段存在于 radio_fields 中,Django 将改用单选按钮界面。假设 groupPerson 模型上的 ForeignKey

class PersonAdmin(admin.ModelAdmin):
    radio_fields = {"group": admin.VERTICAL}

你可以选择使用 HORIZONTALVERTICAL 来自 django.contrib.admin 模块。

除非某个字段是 ForeignKey 或设置了 choices,否则不要将其包含在 radio_fields 中。

ModelAdmin.autocomplete_fields

autocomplete_fields 是一个 ForeignKey 和/或 ManyToManyField 字段列表,你希望将其更改为 Select2 自动完成输入。

默认情况下,管理员使用选择框界面 (<select>) 来处理这些字段。有时,您不想承担在下拉菜单中显示所有相关实例的开销。

Select2 输入看起来与默认输入类似,但它带有异步加载选项的搜索功能。如果相关模型有很多实例,这将更快且更易于用户使用。

您必须在相关对象的 ModelAdmin 中定义 search_fields,因为自动完成搜索使用它。

为避免未经授权的数据泄露,用户必须拥有相关对象的 viewchange 权限才能使用自动完成。

结果的排序和分页由相关 ModelAdminget_ordering()get_paginator() 方法控制。

在以下示例中,ChoiceAdmin 具有 ForeignKey 的自动完成字段,指向 Question。结果根据 question_text 字段进行筛选,并按 date_created 字段排序

class QuestionAdmin(admin.ModelAdmin):
    ordering = ["date_created"]
    search_fields = ["question_text"]


class ChoiceAdmin(admin.ModelAdmin):
    autocomplete_fields = ["question"]

大数据集的性能注意事项

使用 ModelAdmin.ordering 进行排序可能会导致性能问题,因为对大型查询集进行排序会很慢。

此外,如果您的搜索字段包含未由数据库编入索引的字段,则在极大的表上可能会遇到性能不佳的情况。

对于这些情况,最好编写自己的 ModelAdmin.get_search_results() 实现,使用全文索引搜索。

您可能还希望在非常大的表上更改 Paginator,因为默认分页器始终执行 count() 查询。例如,您可以覆盖 Paginator.count 属性的默认实现。

ModelAdmin.raw_id_fields

默认情况下,Django 的管理界面对 ForeignKey 字段使用选择框界面 (<select>)。有时您不希望承担必须选择所有相关实例以在下拉菜单中显示的开销。

raw_id_fields 是一个字段列表,您希望将其更改为 Input 小组件,用于 ForeignKeyManyToManyField

class ArticleAdmin(admin.ModelAdmin):
    raw_id_fields = ["newspaper"]

如果字段是 ForeignKey,则 raw_id_fields Input 小组件应包含一个主键;如果字段是 ManyToManyField,则应包含一个逗号分隔的值列表。 raw_id_fields 小组件在字段旁边显示一个放大镜按钮,允许用户搜索并选择一个值

../../../_images/raw_id_fields.png
ModelAdmin.readonly_fields

默认情况下,管理员会将所有字段显示为可编辑。此选项中的任何字段(应为 listtuple)将按原样显示其数据且不可编辑;它们还从用于创建和编辑的 ModelForm 中排除。请注意,在指定 ModelAdmin.fieldsModelAdmin.fieldsets 时,必须显示只读字段(否则将忽略它们)。

如果在未通过 ModelAdmin.fieldsModelAdmin.fieldsets 定义显式排序的情况下使用 readonly_fields,它们将在所有可编辑字段之后最后添加。

只读字段不仅可以显示模型字段中的数据,还可以显示模型方法或 ModelAdmin 类本身的方法的输出。这与 ModelAdmin.list_display 的行为非常相似。这提供了一种使用管理员界面来提供有关所编辑对象状态的反馈的方法,例如

from django.contrib import admin
from django.utils.html import format_html_join
from django.utils.safestring import mark_safe


class PersonAdmin(admin.ModelAdmin):
    readonly_fields = ["address_report"]

    # description functions like a model field's verbose_name
    @admin.display(description="Address")
    def address_report(self, instance):
        # assuming get_full_address() returns a list of strings
        # for each line of the address and you want to separate each
        # line by a linebreak
        return format_html_join(
            mark_safe("<br>"),
            "{}",
            ((line,) for line in instance.get_full_address()),
        ) or mark_safe("<span class='errors'>I can't determine this address.</span>")
ModelAdmin.save_as

save_as 设置为 True,以在管理更改表单中启用“另存为新”功能。

通常,对象有三个保存选项:“保存”、“保存并继续编辑”和“保存并添加另一个”。如果 save_asTrue,“保存并添加另一个”将被“另存为新”按钮取代,该按钮会创建一个新对象(具有新 ID),而不是更新现有对象。

默认情况下,save_as 设置为 False

ModelAdmin.save_as_continue

save_as=True 时,保存新对象后的默认重定向是该对象的更改视图。如果将 save_as_continue=False 设置为 False,则重定向将是更改列表视图。

默认情况下,save_as_continue 设置为 True

ModelAdmin.save_on_top

save_on_top 设置为 True,以在管理更改表单顶部添加保存按钮。

通常,保存按钮仅出现在表单底部。如果将 save_on_top 设置为 True,则按钮将同时出现在顶部和底部。

默认情况下,save_on_top 设置为 False

ModelAdmin.search_fields

设置 search_fields 以在管理更改列表页面上启用搜索框。这应设置为将在该文本框中提交搜索查询时进行搜索的字段名称列表。

这些字段应为某种文本字段,例如 CharFieldTextField。您还可以使用查找 API “follow” 符号在 ForeignKeyManyToManyField 上执行相关查找

search_fields = ["foreign_key__related_fieldname"]

例如,如果您有一个带有作者的博客条目,则以下定义将允许通过作者的电子邮件地址搜索博客条目

search_fields = ["user__email"]

当有人在管理搜索框中进行搜索时,Django 会将搜索查询拆分为单词,并返回包含每个单词的所有对象,不区分大小写(使用 icontains 查找),其中每个单词必须至少包含在 search_fields 中的一个单词中。例如,如果 search_fields 设置为 ['first_name', 'last_name'],并且用户搜索 john lennon,Django 将执行等效于此 SQL WHERE 子句

WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')

搜索查询可以包含带空格的引号短语。例如,如果用户搜索 "john winston"'john winston',Django 将执行等效于此 SQL WHERE 子句

WHERE (first_name ILIKE '%john winston%' OR last_name ILIKE '%john winston%')

如果您不想使用 icontains 作为查找,您可以通过将它附加到字段来使用任何查找。例如,您可以通过将 search_fields 设置为 ['first_name__exact'] 来使用 exact

还提供了一些(较旧的)用于指定字段查找的快捷方式。您可以在 search_fields 中的前缀字段带有以下字符,并且它等效于向字段添加 __<lookup>

前缀 查找
^ istartswith
= iexact
@ search
icontains

如果您需要自定义搜索,可以使用 ModelAdmin.get_search_results() 提供其他或备用的搜索行为。

ModelAdmin.search_help_text

设置 search_help_text 以指定搜索框的描述性文本,该文本将显示在搜索框下方。

ModelAdmin.show_full_result_count

设置 show_full_result_count 以控制是否在经过筛选的管理页面上显示对象的完整计数(例如 99 results (103 total))。如果此选项设置为 False,则会显示类似 99 results (Show all) 的文本。

默认情况下 show_full_result_count=True 会生成一个查询以对表执行完整计数,如果表包含大量行,这可能会很昂贵。

ModelAdmin.sortable_by

默认情况下,更改列表页面允许按所有模型字段(以及使用 ordering 参数的 display() 装饰器或具有 admin_order_field 属性)进行排序,这些属性在 list_display 中指定。

如果您想禁用某些列的排序,请将 sortable_by 设置为 list_display 子集的集合(例如 listtupleset),您希望它们可排序。空集合禁用所有列的排序。

如果您需要动态指定此列表,请改而实现 get_sortable_by() 方法。

ModelAdmin.view_on_site

设置 view_on_site 以控制是否显示“在网站上查看”链接。此链接应将您带到一个 URL,您可以在其中显示已保存的对象。

此值可以是布尔标志或可调用对象。如果为 True(默认值),则对象的 get_absolute_url() 方法将用于生成 URL。

如果你的模型有一个 get_absolute_url() 方法,但你不想让“在网站上查看”按钮出现,你只需要将 view_on_site 设置为 False

from django.contrib import admin


class PersonAdmin(admin.ModelAdmin):
    view_on_site = False

如果它是一个可调用对象,它接受模型实例作为参数。例如

from django.contrib import admin
from django.urls import reverse


class PersonAdmin(admin.ModelAdmin):
    def view_on_site(self, obj):
        url = reverse("person-detail", kwargs={"slug": obj.slug})
        return "https://example.com" + url

自定义模板选项

覆盖管理模板 部分描述了如何覆盖或扩展默认管理模板。使用以下选项覆盖 ModelAdmin 视图使用的默认模板

ModelAdmin.add_form_template

自定义模板的路径,由 add_view() 使用。

ModelAdmin.change_form_template

自定义模板的路径,由 change_view() 使用。

ModelAdmin.change_list_template

自定义模板的路径,由 changelist_view() 使用。

ModelAdmin.delete_confirmation_template

自定义模板的路径,由 delete_view() 使用,用于在删除一个或多个对象时显示确认页面。

ModelAdmin.delete_selected_confirmation_template

自定义模板的路径,由 delete_selected 操作方法使用,用于在删除一个或多个对象时显示确认页面。请参阅 操作文档

ModelAdmin.object_history_template

自定义模板的路径,由 history_view() 使用。

ModelAdmin.popup_response_template

自定义模板的路径,由 response_add()response_change()response_delete() 使用。

ModelAdmin 方法

警告

覆盖 ModelAdmin.save_model()ModelAdmin.delete_model() 时,代码必须保存/删除对象。它们并非用于否决目的,而是允许你执行额外的操作。

ModelAdmin.save_model(request, obj, form, change)

方法 save_model 提供 HttpRequest、模型实例、ModelForm 实例以及一个布尔值,该布尔值基于对象是添加还是更改。重写此方法允许执行预保存或后保存操作。调用 super().save_model() 以使用 Model.save() 保存对象。

例如,在保存之前将 request.user 附加到对象

from django.contrib import admin


class ArticleAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.user = request.user
        super().save_model(request, obj, form, change)
ModelAdmin.delete_model(request, obj)

方法 delete_model 提供 HttpRequest 和模型实例。重写此方法允许执行预删除或后删除操作。调用 super().delete_model() 以使用 Model.delete() 删除对象。

ModelAdmin.delete_queryset(request, queryset)

方法 delete_queryset() 提供 HttpRequest 和要删除的对象的 QuerySet。重写此方法以自定义“删除选定对象”操作 的删除过程。

ModelAdmin.save_formset(request, form, formset, change)

方法 save_formset 提供 HttpRequest、父 ModelForm 实例以及一个布尔值,该布尔值基于对象是添加还是更改父对象。

例如,将 request.user 附加到每个已更改的表单集模型实例

class ArticleAdmin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for obj in formset.deleted_objects:
            obj.delete()
        for instance in instances:
            instance.user = request.user
            instance.save()
        formset.save_m2m()

另请参见 在表单集中保存对象

ModelAdmin.get_ordering(request)

get_ordering 方法将 request 作为参数,并应返回类似于 ordering 属性的 listtuple 以进行排序。例如

class PersonAdmin(admin.ModelAdmin):
    def get_ordering(self, request):
        if request.user.is_superuser:
            return ["name", "rank"]
        else:
            return ["name"]
ModelAdmin.get_search_results(request, queryset, search_term)

get_search_results 方法将显示的对象列表修改为与提供的搜索词匹配的对象列表。它接受请求、应用当前筛选器的查询集以及用户提供的搜索词。它返回一个元组,其中包含一个已修改以实现搜索的查询集,以及一个指示结果可能包含重复项的布尔值。

默认实现搜索 ModelAdmin.search_fields 中命名的字段。

可以使用您自己的自定义搜索方法覆盖此方法。例如,您可能希望按整数字段搜索,或使用外部工具,例如 SolrHaystack。您必须确定由您的搜索方法实现的查询集更改是否会在结果中引入重复项,并在返回值的第二个元素中返回 True

例如,要按 nameage 搜索,可以使用

class PersonAdmin(admin.ModelAdmin):
    list_display = ["name", "age"]
    search_fields = ["name"]

    def get_search_results(self, request, queryset, search_term):
        queryset, may_have_duplicates = super().get_search_results(
            request,
            queryset,
            search_term,
        )
        try:
            search_term_as_int = int(search_term)
        except ValueError:
            pass
        else:
            queryset |= self.model.objects.filter(age=search_term_as_int)
        return queryset, may_have_duplicates

此实现比 search_fields = ('name', '=age') 更有效率,后者会导致对数字字段进行字符串比较,例如 ... OR UPPER("polls_choice"."votes"::text) = UPPER('4') 在 PostgreSQL 上。

save_related 方法给出了 HttpRequest、父 ModelForm 实例、内联表单集列表和一个基于父对象是正在添加还是正在更改的布尔值。在此,您可以对与父对象相关的对象执行任何预保存或后保存操作。请注意,此时父对象及其表单已保存。

ModelAdmin.get_autocomplete_fields(request)

get_autocomplete_fields() 方法给出了 HttpRequest,并且应返回一个 listtuple 的字段名称,这些字段名称将使用自动完成小部件显示,如上文 ModelAdmin.autocomplete_fields 部分所述。

ModelAdmin.get_readonly_fields(request, obj=None)

方法 get_readonly_fields 给定 HttpRequest 和正在编辑的 obj(或在添加表单上的 None),并且预期返回一个 listtuple 的字段名称,这些字段名称将显示为只读,如上文 ModelAdmin.readonly_fields 部分中所述。

ModelAdmin.get_prepopulated_fields(request, obj=None)

方法 get_prepopulated_fields 给定 HttpRequest 和正在编辑的 obj(或在添加表单上的 None),并且预期返回一个 dictionary,如上文 ModelAdmin.prepopulated_fields 部分中所述。

ModelAdmin.get_list_display(request)

方法 get_list_display 给定 HttpRequest,并且预期返回一个 listtuple 的字段名称,这些字段名称将显示在变更列表视图中,如上文 ModelAdmin.list_display 部分中所述。

get_list_display_links 方法赋予 HttpRequestlisttuple,由 ModelAdmin.get_list_display() 返回。期望返回 Nonelisttuple,其中包含变更列表中字段名称,该字段名称将链接到变更视图,如 ModelAdmin.list_display_links 部分中所述。

ModelAdmin.get_exclude(request, obj=None)

get_exclude 方法赋予 HttpRequest 和正在编辑的 obj(或在添加表单中为 None),期望返回字段列表,如 ModelAdmin.exclude 中所述。

ModelAdmin.get_fields(request, obj=None)

get_fields 方法赋予 HttpRequest 和正在编辑的 obj(或在添加表单中为 None),期望返回字段列表,如上文 ModelAdmin.fields 部分中所述。

ModelAdmin.get_fieldsets(request, obj=None)

get_fieldsets 方法给出了 HttpRequest 和正在编辑的 obj(或在添加表单上的 None),并期望返回一个 2 元组列表,其中每个 2 元组表示管理表单页面上的 <fieldset>,如上文 ModelAdmin.fieldsets 部分所述。

ModelAdmin.get_list_filter(request)

get_list_filter 方法给出了 HttpRequest,并期望返回与 list_filter 属性相同的序列类型。

get_list_select_related 方法给出了 HttpRequest,并且应返回布尔值或列表,就像 ModelAdmin.list_select_related 所做的那样。

ModelAdmin.get_search_fields(request)

get_search_fields 方法给出了 HttpRequest,并期望返回与 search_fields 属性相同的序列类型。

ModelAdmin.get_sortable_by(request)

get_sortable_by() 方法会传递 HttpRequest,并应返回一个集合(例如 listtupleset),其中包含更改列表页面中可排序的字段名称。

它的默认实现会返回 sortable_by(如果已设置),否则会推迟到 get_list_display()

例如,要防止一个或多个列可排序

class PersonAdmin(admin.ModelAdmin):
    def get_sortable_by(self, request):
        return {*self.get_list_display(request)} - {"rank"}
ModelAdmin.get_inline_instances(request, obj=None)

get_inline_instances 方法提供了 HttpRequest 和正在编辑的 obj(或在添加表单中为 None),并应返回 listInlineModelAdmin 对象的 tuple,如下文 InlineModelAdmin 部分所述。例如,以下内容将返回不基于添加、更改、删除和查看权限的默认筛选的内联

class MyModelAdmin(admin.ModelAdmin):
    inlines = [MyInline]

    def get_inline_instances(self, request, obj=None):
        return [inline(self.model, self.admin_site) for inline in self.inlines]

如果您覆盖此方法,请确保返回的内联是 inlines 中定义的类的实例,否则在添加相关对象时可能会遇到“错误的请求”错误。

ModelAdmin.get_inlines(request, obj)

get_inlines 方法提供了 HttpRequest 和正在编辑的 obj(或在添加表单中为 None),并应返回内联的可迭代对象。您可以覆盖此方法以根据请求或模型实例动态添加内联,而不是在 ModelAdmin.inlines 中指定内联。

ModelAdmin.get_urls()

ModelAdmin 上的 get_urls 方法以与 URLconf 相同的方式返回要用于该 ModelAdmin 的 URL。因此,你可以使用 AdminSite.admin_view() 包装器在视图中扩展它们,如 URL 调度程序 中所述

from django.contrib import admin
from django.template.response import TemplateResponse
from django.urls import path


class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super().get_urls()
        my_urls = [path("my_view/", self.admin_site.admin_view(self.my_view))]
        return my_urls + urls

    def my_view(self, request):
        # ...
        context = dict(
            # Include common variables for rendering the admin template.
            self.admin_site.each_context(request),
            # Anything else you want in the context...
            key=value,
        )
        return TemplateResponse(request, "sometemplate.html", context)

如果你想使用管理布局,请从 admin/base_site.html 扩展

{% extends "admin/base_site.html" %}
{% block content %}
...
{% endblock %}

注意

请注意 self.my_view 函数是如何包装在 self.admin_site.admin_view 中的。这一点很重要,因为它确保了两件事

  1. 运行权限检查,确保只有活跃的工作人员用户才能访问视图。
  2. django.views.decorators.cache.never_cache() 装饰器用于防止缓存,确保返回的信息是最新的。

注意

请注意,自定义模式包含在常规管理 URL 之前:管理 URL 模式非常宽松,几乎可以匹配任何内容,因此你通常希望将自定义 URL 添加到内置 URL 之前。

在此示例中,my_view 将在 /admin/myapp/mymodel/my_view/ 处访问(假设管理 URL 包含在 /admin/ 中。)

如果页面可缓存,但你仍然希望执行权限检查,则可以将 cacheable=True 参数传递给 AdminSite.admin_view()

path("my_view/", self.admin_site.admin_view(self.my_view, cacheable=True))

ModelAdmin 视图具有 model_admin 属性。其他 AdminSite 视图具有 admin_site 属性。

ModelAdmin.get_form(request, obj=None, **kwargs)

返回 ModelForm 类,用于在管理添加和更改视图中,请参见 add_view()change_view()

基本实现使用 modelform_factory()form 进行子类化,由 fieldsexclude 等属性修改。因此,例如,如果您想为超级用户提供其他字段,则可以像这样换入不同的基本表单

class MyModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            kwargs["form"] = MySuperuserForm
        return super().get_form(request, obj, **kwargs)

您还可以直接返回自定义 ModelForm 类。

ModelAdmin.get_formsets_with_inlines(request, obj=None)

生成用于在管理添加和更改视图中使用的 (FormSet, InlineModelAdmin) 对。

例如,如果你想仅在更改视图中显示特定内联,你可以按如下方式覆盖 get_formsets_with_inlines

class MyModelAdmin(admin.ModelAdmin):
    inlines = [MyInline, SomeOtherInline]

    def get_formsets_with_inlines(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            # hide MyInline in the add view
            if not isinstance(inline, MyInline) or obj is not None:
                yield inline.get_formset(request, obj), inline
ModelAdmin.formfield_for_foreignkey(db_field, request, **kwargs)

ModelAdmin 上的 formfield_for_foreignkey 方法允许你覆盖外键字段的默认表单字段。例如,根据用户返回此外键字段的对象子集

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

这使用 HttpRequest 实例来过滤 Car 外键字段,以仅显示 User 实例拥有的汽车。

对于更复杂的过滤器,你可以使用 ModelForm.__init__() 方法根据模型的 instance 进行过滤(请参阅 处理关系的字段)。例如

class CountryAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["capital"].queryset = self.instance.cities.all()


class CountryAdmin(admin.ModelAdmin):
    form = CountryAdminForm
ModelAdmin.formfield_for_manytomany(db_field, request, **kwargs)

formfield_for_foreignkey 方法类似,formfield_for_manytomany 方法可以被覆盖以更改多对多字段的默认表单字段。例如,如果一个所有者可以拥有多辆车,而汽车可以属于多个所有者(多对多关系),则可以过滤 Car 外键字段,以仅显示 User 所拥有的汽车

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "cars":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super().formfield_for_manytomany(db_field, request, **kwargs)
ModelAdmin.formfield_for_choice_field(db_field, request, **kwargs)

formfield_for_foreignkeyformfield_for_manytomany 方法类似,formfield_for_choice_field 方法可以被覆盖以更改已声明选项的字段的默认表单字段。例如,如果超级用户可用的选项应与普通工作人员可用的选项不同,则可以按如下方式进行

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "status":
            kwargs["choices"] = [
                ("accepted", "Accepted"),
                ("denied", "Denied"),
            ]
            if request.user.is_superuser:
                kwargs["choices"].append(("ready", "Ready for deployment"))
        return super().formfield_for_choice_field(db_field, request, **kwargs)

choices 限制

在表单字段上设置的任何 choices 属性都将仅限于表单字段。如果模型上的相应字段设置了选项,则提供给表单的选项必须是这些选项的有效子集,否则在保存之前验证模型本身时,表单提交将因 ValidationError 而失败。

ModelAdmin.get_changelist(request, **kwargs)

返回用于列出的 Changelist 类。默认情况下,使用 django.contrib.admin.views.main.ChangeList。通过继承此类,可以更改列表的行为。

ModelAdmin.get_changelist_form(request, **kwargs)

返回 ModelForm 类,用于更改列表页面的 Formset。例如,要使用自定义表单,

from django import forms


class MyForm(forms.ModelForm):
    pass


class MyModelAdmin(admin.ModelAdmin):
    def get_changelist_form(self, request, **kwargs):
        return MyForm

省略 Meta.model 属性

如果在 ModelForm 上定义 Meta.model 属性,还必须定义 Meta.fields 属性(或 Meta.exclude 属性)。但是,ModelAdmin 会忽略此值,并用 ModelAdmin.list_editable 属性覆盖它。最简单的解决方案是省略 Meta.model 属性,因为 ModelAdmin 会提供要使用的正确模型。

ModelAdmin.get_changelist_formset(request, **kwargs)

如果使用 list_editable,则返回 ModelFormSet 类,用于更改列表页面。例如,要使用自定义表单集,

from django.forms import BaseModelFormSet


class MyAdminFormSet(BaseModelFormSet):
    pass


class MyModelAdmin(admin.ModelAdmin):
    def get_changelist_formset(self, request, **kwargs):
        kwargs["formset"] = MyAdminFormSet
        return super().get_changelist_formset(request, **kwargs)
ModelAdmin.lookup_allowed(lookup, value, request)

更改列表页面中的对象可以使用 URL 查询字符串中的查找器进行筛选。例如,list_filter 的工作方式就是这样。查找器类似于 QuerySet.filter() 中使用的查找器(例如 [email protected])。由于查询字符串中的查找器可以由用户操作,因此必须对其进行清理以防止未经授权的数据泄露。

lookup_allowed() 方法会从查询字符串(例如 'user__email')获得查找器路径,获得相应的 value(例如 '[email protected]'),获得请求,并返回一个布尔值,指示是否允许使用参数筛选更改列表的 QuerySet。如果 lookup_allowed() 返回 False,则会引发 DisallowedModelAdminLookupSuspiciousOperation 的子类)。

默认情况下,lookup_allowed() 允许访问模型的本地字段、list_filter 中使用的字段路径(但不是 get_list_filter() 中的路径),以及 limit_choices_toraw_id_fields 中正常运行所需的查找器。

覆盖此方法以自定义 ModelAdmin 子类的允许查找器。

在 Django 5.0 中更改

已添加 request 参数。

ModelAdmin.has_view_permission(request, obj=None)

如果允许查看 obj,则应返回 True,否则返回 False。如果 obj 为 None,则应返回 TrueFalse,以指示是否通常允许查看此类型的对象(例如,False 将被解释为当前用户不允许查看此类型的任何对象)。

如果用户具有“更改”或“查看”权限,则默认实现返回 True

ModelAdmin.has_add_permission(request)

如果允许添加对象,则应返回 True,否则返回 False

ModelAdmin.has_change_permission(request, obj=None)

如果允许编辑 obj,则应返回 True,否则返回 False。如果 objNone,则应返回 TrueFalse 以指示通常是否允许编辑此类型的对象(例如, False 将被解释为当前用户不允许编辑此类型的任何对象)。

ModelAdmin.has_delete_permission(request, obj=None)

如果允许删除 obj,则应返回 True,否则返回 False。如果 objNone,则应返回 TrueFalse 以指示通常是否允许删除此类型的对象(例如, False 将被解释为当前用户不允许删除此类型的任何对象)。

ModelAdmin.has_module_permission(request)

如果允许在管理索引页上显示模块和访问模块的索引页,则应返回 True,否则返回 False。默认情况下使用 User.has_module_perms()。覆盖它不会限制对视图、添加、更改或删除视图的访问权限,has_view_permission()has_add_permission()has_change_permission()has_delete_permission() 应用于此。

ModelAdmin.get_queryset(request)

get_queryset 方法在 ModelAdmin 上返回 QuerySet,其中包含管理站点可以编辑的所有模型实例。覆盖此方法的一个用例是显示登录用户拥有的对象

class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)
ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)

使用 django.contrib.messages 后端向用户发送消息。请参阅 自定义 ModelAdmin 示例

关键字参数允许您更改消息级别,添加额外的 CSS 标记,或在未安装 contrib.messages 框架时静默失败。这些关键字参数与 django.contrib.messages.add_message() 的参数匹配,有关更多详细信息,请参阅该函数的文档。一个区别是,除了整数/常量之外,还可以将级别作为字符串标签传递。

ModelAdmin.get_paginator(request, queryset, per_page, orphans=0, allow_empty_first_page=True)

返回要用于此视图的分词器实例。默认情况下,实例化 paginator 的实例。

ModelAdmin.response_add(request, obj, post_url_continue=None)

确定 HttpResponse 用于 add_view() 阶段。

response_add在提交管理表单后以及在对象和所有相关实例创建并保存之后被调用。你可以覆盖它以在创建对象后更改默认行为。

ModelAdmin.response_change(request, obj)

确定HttpResponse用于change_view()阶段。

response_change在提交管理表单后以及在对象和所有相关实例保存之后被调用。你可以覆盖它以在更改对象后更改默认行为。

ModelAdmin.response_delete(request, obj_display, obj_id)

确定HttpResponse用于delete_view()阶段。

response_delete在删除对象后被调用。你可以覆盖它以在删除对象后更改默认行为。

obj_display是一个字符串,其中包含已删除对象的名称。

obj_id是用于检索要删除的对象的序列化标识符。

ModelAdmin.get_formset_kwargs(request, obj, inline, prefix)

一个用于自定义传递给表单集构造函数的关键字参数的钩子。例如,要将 request 传递给表单集表单

class MyModelAdmin(admin.ModelAdmin):
    def get_formset_kwargs(self, request, obj, inline, prefix):
        return {
            **super().get_formset_kwargs(request, obj, inline, prefix),
            "form_kwargs": {"request": request},
        }

您还可以使用它为表单集表单设置 initial

ModelAdmin.get_changeform_initial_data(request)

用于管理更改表单上初始数据的钩子。默认情况下,字段的初始值来自 GET 参数。例如,?name=initial_value 将把 name 字段的初始值设置为 initial_value

此方法应返回一个形式为 {'fieldname': 'fieldval'} 的字典

def get_changeform_initial_data(self, request):
    return {"name": "custom_initial_value"}
ModelAdmin.get_deleted_objects(objs, request)

用于自定义 delete_view() 和“删除所选”操作 的删除过程的钩子。

objs 参数是要删除的对象的同类可迭代对象(QuerySet 或模型实例列表),requestHttpRequest

此方法必须返回 (deleted_objects, model_count, perms_needed, protected) 的 4 元组。

deleted_objects 是一个字符串列表,表示将要删除的所有对象。如果存在任何要删除的关联对象,则列表将被嵌套并包含这些关联对象。该列表使用 unordered_list 过滤器在模板中进行格式化。

model_count 是一个字典,将每个模型的 verbose_name_plural 映射到将要删除的对象数。

perms_needed 是一个集合,其中包含用户无权删除的模型的 verbose_name

protected 是一个字符串列表,表示所有无法删除的受保护关联对象。该列表显示在模板中。

其他方法

ModelAdmin.add_view(request, form_url='', extra_context=None)

模型实例添加页面的 Django 视图。请参阅下面的注释。

ModelAdmin.change_view(request, object_id, form_url='', extra_context=None)

模型实例编辑页面的 Django 视图。请参阅下面的注释。

ModelAdmin.changelist_view(request, extra_context=None)

模型实例更改列表/操作页面的 Django 视图。请参阅下面的注释。

ModelAdmin.delete_view(request, object_id, extra_context=None)

模型实例删除确认页面的 Django 视图。请参阅下面的注释。

ModelAdmin.history_view(request, object_id, extra_context=None)

用于显示给定模型实例的修改历史记录的页面的 Django 视图。

与上一部分中详述的挂钩类型 ModelAdmin 方法不同,这五个方法实际上被设计为从管理应用程序 URL 调度处理程序调用为 Django 视图,以呈现处理模型实例 CRUD 操作的页面。因此,完全覆盖这些方法将显著改变管理应用程序的行为。

覆盖这些方法的一个常见原因是扩充提供给呈现视图的模板的上下文数据。在以下示例中,覆盖更改视图,以便为呈现的模板提供一些原本不可用的额外映射数据

class MyModelAdmin(admin.ModelAdmin):
    # A template for a very customized change view:
    change_form_template = "admin/myapp/extras/openstreetmap_change_form.html"

    def get_osm_info(self):
        # ...
        pass

    def change_view(self, request, object_id, form_url="", extra_context=None):
        extra_context = extra_context or {}
        extra_context["osm_data"] = self.get_osm_info()
        return super().change_view(
            request,
            object_id,
            form_url,
            extra_context=extra_context,
        )

这些视图返回 TemplateResponse 实例,它允许您在呈现之前轻松自定义响应数据。有关更多详细信息,请参阅 TemplateResponse 文档

ModelAdmin 资产定义

有时您希望向添加/更改视图添加一些 CSS 和/或 JavaScript。这可以通过在 ModelAdmin 上使用 Media 内部类来实现

class ArticleAdmin(admin.ModelAdmin):
    class Media:
        css = {
            "all": ["my_styles.css"],
        }
        js = ["my_code.js"]

staticfiles 应用程序 会将 STATIC_URL(或 MEDIA_URL,如果 STATIC_URLNone)预置到任何资产路径。与 表单上的常规资产定义 相同的规则适用。

jQuery

Django 管理 JavaScript 使用 jQuery 库。

为了避免与用户提供的脚本或库发生冲突,Django 的 jQuery(版本 3.7.1)命名空间为 django.jQuery。如果您想在自己的管理 JavaScript 中使用 jQuery 而无需包含第二个副本,您可以在变更列表和添加/编辑视图中使用 django.jQuery 对象。此外,依赖于 django.jQuery 的您自己的管理表单或小部件必须在 声明表单媒体资产 时指定 js=['admin/js/jquery.init.js', …]

Django 4.2 中已更改

jQuery 已从 3.6.0 升级到 3.6.4。

在 Django 5.0 中更改

jQuery 已从 3.6.4 升级到 3.7.1。

ModelAdmin 类默认情况下需要 jQuery,因此无需将 jQuery 添加到 ModelAdmin 的媒体资源列表中,除非您有特殊需求。例如,如果您需要 jQuery 库位于全局名称空间中(例如,当使用第三方 jQuery 插件时)或如果您需要较新版本的 jQuery,您将必须包含您自己的副本。

Django 提供了未压缩和“最小化”版本的 jQuery,分别为 jquery.jsjquery.min.js

ModelAdminInlineModelAdmin 有一个 media 属性,它返回一个 Media 对象的列表,该列表存储指向表单和/或表单集的 JavaScript 文件的路径。如果 DEBUGTrue,它将返回各种 JavaScript 文件的未压缩版本,包括 jquery.js;如果不是,它将返回“缩小”版本。

向管理中添加自定义验证

你还可以向管理中的数据添加自定义验证。自动管理界面会重复使用 django.forms,而 ModelAdmin 类让你能够定义自己的表单

class ArticleAdmin(admin.ModelAdmin):
    form = MyArticleAdminForm

MyArticleAdminForm 可以定义在任何地方,只要在需要的地方导入即可。现在,在你的表单中,你可以为任何字段添加自己的自定义验证

class MyArticleAdminForm(forms.ModelForm):
    def clean_name(self):
        # do something that validates your data
        return self.cleaned_data["name"]

重要的是,你在这里使用 ModelForm,否则可能会出错。请参阅 表单 文档中的 自定义验证,更具体地说,请参阅 模型表单验证说明 以获取更多信息。

InlineModelAdmin 对象

class InlineModelAdmin
class TabularInline
class StackedInline

管理界面具有在与父模型相同的页面上编辑模型的能力。这些称为内联。假设您有这两个模型

from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=100)


class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)

您可以在作者页面上编辑由作者编写的书籍。您通过在 ModelAdmin.inlines 中指定内联来将内联添加到模型

from django.contrib import admin


class BookInline(admin.TabularInline):
    model = Book


class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]

Django 提供了 InlineModelAdmin 的两个子类,它们是

这两个之间的区别仅仅是用于呈现它们的模板。

InlineModelAdmin 选项

InlineModelAdmin 共享与 ModelAdmin 相同的许多功能,并添加了一些自己的功能(共享功能实际上是在 BaseModelAdmin 超类中定义的)。共享功能是

InlineModelAdmin 类添加或自定义

InlineModelAdmin.model

内联使用的模型。这是必需的。

InlineModelAdmin.fk_name

模型上的外键名称。在大多数情况下,这将自动处理,但如果存在多个外键指向同一父模型,则必须显式指定 fk_name

InlineModelAdmin.formset

这默认为 BaseInlineFormSet。使用您自己的表单集可以为您提供许多自定义可能性。内联围绕 模型表单集 构建。

InlineModelAdmin.form

form 的值默认为 ModelForm。这是在创建此内联的表单集时传递给 inlineformset_factory() 的内容。

警告

InlineModelAdmin 表单编写自定义验证时,请谨慎编写依赖于父模型特性的验证。如果父模型未通过验证,则可能会处于不一致的状态,如 ModelForm 上的验证 中的警告所述。

InlineModelAdmin.classes

包含要应用于为内联呈现的字段集的额外 CSS 类的列表或元组。默认为 None。与在 fieldsets 中配置的类一样,具有 collapse 类的内联将最初折叠,其标题将有一个小的“显示”链接。

InlineModelAdmin.extra

这控制表单集除了初始表单之外将显示的额外表单数量。默认为 3。有关更多信息,请参阅 表单集文档

对于启用了 JavaScript 的浏览器的用户,提供了一个“添加另一个”链接,以便除了作为 extra 参数的结果提供的内联之外,还可以添加任意数量的附加内联。

如果当前显示的表单数量超过 max_num,或者用户未启用 JavaScript,则动态链接将不会出现。

InlineModelAdmin.get_extra() 还允许您自定义额外表单的数量。

InlineModelAdmin.max_num

这控制了内联中显示的最大表单数。这与对象数没有直接关联,但如果值足够小,则可以关联。有关详细信息,请参阅 限制可编辑对象数

InlineModelAdmin.get_max_num() 还允许您自定义额外表单的最大数量。

InlineModelAdmin.min_num

这控制了内联中显示的最小表单数。有关详细信息,请参阅 modelformset_factory()

InlineModelAdmin.get_min_num() 还允许您自定义显示的最小表单数。

InlineModelAdmin.raw_id_fields

默认情况下,Django 的管理界面对 ForeignKey 字段使用选择框界面 (<select>)。有时您不希望承担必须选择所有相关实例以在下拉菜单中显示的开销。

raw_id_fields 是一个字段列表,您希望将其更改为 Input 小组件,用于 ForeignKeyManyToManyField

class BookInline(admin.TabularInline):
    model = Book
    raw_id_fields = ["pages"]
InlineModelAdmin.template

用于在页面上呈现内联的模板。

InlineModelAdmin.verbose_name

对模型内部 Meta 类的 verbose_name 的覆盖。

InlineModelAdmin.verbose_name_plural

来自模型内部 Meta 类的 verbose_name_plural 的覆盖。如果未给出此项且 InlineModelAdmin.verbose_name 已定义,Django 将使用 InlineModelAdmin.verbose_name + 's'

InlineModelAdmin.can_delete

指定是否可以在内联中删除内联对象。默认为 True

指定是否在管理员中可以更改的内联对象具有指向更改表单的链接。默认为 False

InlineModelAdmin.get_formset(request, obj=None, **kwargs)

返回一个 BaseInlineFormSet 类,用于管理添加/更改视图。 obj 是正在编辑的父对象,或在添加新父对象时为 None。有关 ModelAdmin.get_formsets_with_inlines 的示例,请参见示例。

InlineModelAdmin.get_extra(request, obj=None, **kwargs)

返回要使用的额外内联表单的数量。默认情况下,返回 InlineModelAdmin.extra 属性。

重写此方法以通过编程方式确定额外内联表单的数量。例如,这可能基于模型实例(作为关键字参数 obj 传递)

class BinaryTreeAdmin(admin.TabularInline):
    model = BinaryTree

    def get_extra(self, request, obj=None, **kwargs):
        extra = 2
        if obj:
            return extra - obj.binarytree_set.count()
        return extra
InlineModelAdmin.get_max_num(request, obj=None, **kwargs)

返回要使用的最大额外内联表单的数量。默认情况下,返回 InlineModelAdmin.max_num 属性。

重写此方法以通过编程方式确定最大内联表单的数量。例如,这可能基于模型实例(作为关键字参数 obj 传递)

class BinaryTreeAdmin(admin.TabularInline):
    model = BinaryTree

    def get_max_num(self, request, obj=None, **kwargs):
        max_num = 10
        if obj and obj.parent:
            return max_num - 5
        return max_num
InlineModelAdmin.get_min_num(request, obj=None, **kwargs)

返回要使用的最小内联表单的数量。默认情况下,返回 InlineModelAdmin.min_num 属性。

覆盖此方法以以编程方式确定内联表单的最小数量。例如,这可能基于模型实例(作为关键字参数 obj 传递)。

InlineModelAdmin.has_add_permission(request, obj)

如果允许添加内联对象,则应返回 True,否则返回 Falseobj 是正在编辑的父对象,或在添加新父对象时为 None

InlineModelAdmin.has_change_permission(request, obj=None)

如果允许编辑内联对象,则应返回 True,否则返回 Falseobj 是正在编辑的父对象。

InlineModelAdmin.has_delete_permission(request, obj=None)

如果允许删除内联对象,则应返回 True,否则返回 Falseobj 是正在编辑的父对象。

注意

传递给 InlineModelAdmin 方法的 obj 参数是正在编辑的父对象,或在添加新父对象时为 None

使用具有两个或更多外键指向同一父模型的模型

有时可能有多个外键指向同一模型。以这个模型为例

from django.db import models


class Friendship(models.Model):
    to_person = models.ForeignKey(
        Person, on_delete=models.CASCADE, related_name="friends"
    )
    from_person = models.ForeignKey(
        Person, on_delete=models.CASCADE, related_name="from_friends"
    )

如果您想在 Person 管理添加/更改页面上显示内联,则需要明确定义外键,因为它无法自动执行此操作

from django.contrib import admin
from myapp.models import Friendship


class FriendshipInline(admin.TabularInline):
    model = Friendship
    fk_name = "to_person"


class PersonAdmin(admin.ModelAdmin):
    inlines = [
        FriendshipInline,
    ]

使用多对多模型

默认情况下,多对多关系的管理小部件将显示在包含 ManyToManyField 实际引用的任何模型上。根据您的 ModelAdmin 定义,模型中的每个多对多字段将由标准 HTML <select multiple>、水平或垂直筛选器或 raw_id_fields 小部件表示。但是,也可以用内联替换这些小部件。

假设我们有以下模型

from django.db import models


class Person(models.Model):
    name = models.CharField(max_length=128)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, related_name="groups")

如果您想使用内联显示多对多关系,可以通过为该关系定义 InlineModelAdmin 对象来实现

from django.contrib import admin


class MembershipInline(admin.TabularInline):
    model = Group.members.through


class PersonAdmin(admin.ModelAdmin):
    inlines = [
        MembershipInline,
    ]


class GroupAdmin(admin.ModelAdmin):
    inlines = [
        MembershipInline,
    ]
    exclude = ["members"]

此示例中有两个值得注意的功能。

首先 - MembershipInline 类引用 Group.members.throughthrough 属性是对管理多对多关系的模型的引用。当您定义多对多字段时,Django 会自动创建此模型。

其次,GroupAdmin 必须手动排除 members 字段。Django 在定义关系的模型(在本例中为 Group)上显示多对多字段的管理小组件。如果您想使用内联模型来表示多对多关系,则必须告知 Django 的管理小组件显示此小组件 - 否则您最终将在管理页面上看到两个用于管理关系的小组件。

请注意,当使用此技术时,m2m_changed 信号不会被触发。这是因为,就管理小组件而言,through 只是一个具有两个外键字段的模型,而不是多对多关系。

在所有其他方面,InlineModelAdmin 与其他任何模型完全相同。您可以使用任何常规 ModelAdmin 属性自定义外观。

使用多对多中间模型

当您使用 through 参数将中间模型指定给 ManyToManyField 时,管理小组件默认不会显示小组件。这是因为该中间模型的每个实例都需要的信息比单个小组件中显示的信息更多,并且多个小组件所需的布局会根据中间模型而有所不同。

但是,我们仍然希望能够内联编辑该信息。幸运的是,我们可以使用内联管理模型来实现此目的。假设我们有以下模型

from django.db import models


class Person(models.Model):
    name = models.CharField(max_length=128)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through="Membership")


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

在管理小组件中显示此中间模型的第一步是为 Membership 模型定义一个内联类

class MembershipInline(admin.TabularInline):
    model = Membership
    extra = 1

此示例使用 Membership 模式的默认 InlineModelAdmin 值,并将额外的添加表单限制为一个。这可以使用 InlineModelAdmin 类可用的任何选项进行自定义。

现在为 PersonGroup 模式创建管理视图

class PersonAdmin(admin.ModelAdmin):
    inlines = [MembershipInline]


class GroupAdmin(admin.ModelAdmin):
    inlines = [MembershipInline]

最后,使用管理站点注册 PersonGroup 模式

admin.site.register(Person, PersonAdmin)
admin.site.register(Group, GroupAdmin)

现在,您的管理站点已设置为从 PersonGroup 详细信息页面内联编辑 Membership 对象。

将泛型关系用作内联

可以使用内联与泛型相关对象。假设您有以下模式

from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models


class Image(models.Model):
    image = models.ImageField(upload_to="images")
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey("content_type", "object_id")


class Product(models.Model):
    name = models.CharField(max_length=100)

如果您想允许在 Product 的添加/更改视图中编辑和创建 Image 实例,可以使用 GenericTabularInlineGenericStackedInlineGenericInlineModelAdmin 的两个子类),由 admin 提供。它们分别为表示内联对象的表单实现表格和堆叠的可视化布局,就像它们的非泛型对应项一样。它们的行为与任何其他内联一样。在此示例应用程序的 admin.py

from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline

from myapp.models import Image, Product


class ImageInline(GenericTabularInline):
    model = Image


class ProductAdmin(admin.ModelAdmin):
    inlines = [
        ImageInline,
    ]


admin.site.register(Product, ProductAdmin)

有关更具体的信息,请参阅 contenttypes 文档

覆盖管理模板

您可以覆盖管理模块用于生成管理站点各个页面的许多模板。您甚至可以为特定应用程序或特定模式覆盖其中一些模板。

设置项目的管理模板目录

管理模板文件位于 django/contrib/admin/templates/admin 目录中。

为了覆盖其中一个或多个,首先在项目的 templates 目录中创建一个 admin 目录。这可以是你 DIRS 选项中指定的 DjangoTemplates 后端的 TEMPLATES 设置中的任何目录。如果你自定义了 'loaders' 选项,请确保 'django.template.loaders.filesystem.Loader' 出现在 'django.template.loaders.app_directories.Loader' 之前,以便模板加载系统在 django.contrib.admin 中包含的模板之前找到你的自定义模板。

在此 admin 目录中,创建以你的应用命名的子目录。在这些应用子目录中,创建以你的模型命名的子目录。请注意,admin 应用在查找目录时会将模型名称小写,因此如果你要在区分大小写的文件系统上运行你的应用,请确保将目录名称全部小写。

要覆盖特定应用的管理模板,请从 django/contrib/admin/templates/admin 目录复制并编辑模板,然后将其保存到你刚创建的其中一个目录中。

例如,如果我们想为名为 my_app 的应用中的所有模型添加一个工具到更改列表视图,我们将 contrib/admin/templates/admin/change_list.html 复制到我们项目的 templates/admin/my_app/ 目录,并进行任何必要的更改。

如果我们想为仅名为“页面”的特定模型添加一个工具到更改列表视图,我们将把该文件复制到我们项目的 templates/admin/my_app/page 目录。

重写与替换管理模板

由于管理模板的模块化设计,通常既没有必要也没有建议替换整个模板。几乎总是最好只重写模板中需要更改的部分。

继续上面的示例,我们希望在 Page 模型的 History 工具旁边添加一个新链接。在查看 change_form.html 后,我们确定只需要重写 object-tools-items 块。因此,以下是我们新的 change_form.html

{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}
{% block object-tools-items %}
    <li>
        <a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% translate "History" %}</a>
    </li>
    <li>
        <a href="mylink/" class="historylink">My Link</a>
    </li>
    {% if has_absolute_url %}
        <li>
            <a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% translate "View on site" %}</a>
        </li>
    {% endif %}
{% endblock %}

就是这样!如果我们将此文件放在 templates/admin/my_app 目录中,我们的链接将出现在 my_app 中所有模型的更改表单上。

可以按应用或模型重写的模板

并非 contrib/admin/templates/admin 中的每个模板都可以按应用或按模型重写。以下可以

  • actions.html
  • app_index.html
  • change_form.html
  • change_form_object_tools.html
  • change_list.html
  • change_list_object_tools.html
  • change_list_results.html
  • date_hierarchy.html
  • delete_confirmation.html
  • object_history.html
  • pagination.html
  • popup_response.html
  • prepopulated_fields_js.html
  • search_form.html
  • submit_line.html

对于无法以这种方式重写的那些模板,你仍然可以通过将新版本放在 templates/admin 目录中,为整个项目重写它们。这对于创建自定义 404 和 500 页面特别有用。

注意

一些管理模板,例如 change_list_results.html,用于呈现自定义包含标签。这些可以被重写,但在这种情况下,你最好创建自己的版本,并给它一个不同的名称。这样你就可以有选择地使用它。

根和登录模板

如果您希望更改索引、登录或注销模板,最好创建自己的 AdminSite 实例(见下文),并更改 AdminSite.index_templateAdminSite.login_templateAdminSite.logout_template 属性。

主题支持

管理员使用 CSS 变量定义颜色和字体。这样可以更改主题,而无需覆盖许多单独的 CSS 规则。例如,如果您更喜欢紫色而不是蓝色,可以向项目添加 admin/base.html 模板覆盖

{% extends 'admin/base.html' %}

{% block extrastyle %}{{ block.super }}
<style>
html[data-theme="light"], :root {
  --primary: #9774d5;
  --secondary: #785cab;
  --link-fg: #7c449b;
  --link-selected-fg: #8f5bb2;
}
</style>
{% endblock %}

CSS 变量列表定义在 django/contrib/admin/static/admin/css/base.css

尊重 prefers-color-scheme 媒体查询的暗模式变量定义在 django/contrib/admin/static/admin/css/dark_mode.css。这链接到 {% block dark-mode-vars %} 中的文档。

AdminSite 对象

class AdminSite(name='admin')

Django 管理站点由 django.contrib.admin.sites.AdminSite 的实例表示;默认情况下,此类的实例创建为 django.contrib.admin.site,您可以在其中注册您的模型和 ModelAdmin 实例。

如果您想自定义默认管理站点,可以 覆盖它

在构建 AdminSite 实例时,可以使用 name 参数向构造函数提供一个唯一的实例名称。此实例名称用于标识实例,尤其是在 反转管理 URL 时。如果没有提供实例名称,将使用默认实例名称 admin。有关自定义 AdminSite 类 的示例,请参阅 AdminSite 类的自定义。

django.contrib.admin.sites.all_sites

一个 WeakSet 包含所有管理站点实例。

AdminSite 属性

模板可以覆盖或扩展基本管理模板,如 覆盖管理模板 中所述。

AdminSite.site_header

作为 <div>(一个字符串)放置在每个管理页面顶部的文本。默认情况下,这是“Django 管理”。

在 Django 5.0 中更改

在较早的版本中,site_header 使用 <h1> 标记。

AdminSite.site_title

放置在每个管理页面的 <title> 末尾的文本(一个字符串)。默认情况下,这是“Django 站点管理”。

AdminSite.site_url

每个管理页面顶部“查看站点”链接的 URL。默认情况下,site_url/。将其设置为 None 以删除链接。

对于在子路径上运行的站点,each_context() 方法检查当前请求是否设置了 request.META['SCRIPT_NAME'],如果 site_url 未设置为除 / 之外的其他值,则使用该值。

AdminSite.index_title

放置在管理索引页面顶部的文本(一个字符串)。默认情况下,这是“站点管理”。

AdminSite.index_template

将由管理站点主索引视图使用的自定义模板的路径。

AdminSite.app_index_template

将由管理站点应用索引视图使用的自定义模板的路径。

AdminSite.empty_value_display

用于在管理站点的更改列表中显示空值的字符串。默认为破折号。还可以在每个 ModelAdmin 的基础上以及 ModelAdmin 中的自定义字段中覆盖该值,方法是在字段上设置 empty_value_display 属性。有关示例,请参见 ModelAdmin.empty_value_display

AdminSite.enable_nav_sidebar

一个布尔值,用于确定在较大的屏幕上是否显示导航侧边栏。默认情况下,它设置为 True

AdminSite.final_catch_all_view

一个布尔值,用于确定是否向管理员添加最终的 catch-all 视图,该视图将未经身份验证的用户重定向到登录页面。默认情况下,它设置为 True

警告

不建议将此值设置为 False,因为该视图可以防止潜在的模型枚举隐私问题。

AdminSite.login_template

自定义模板的路径,该模板将由管理员网站登录视图使用。

AdminSite.login_form

AuthenticationForm 的子类,该子类将由管理员网站登录视图使用。

AdminSite.logout_template

自定义模板的路径,该模板将由管理员网站注销视图使用。

AdminSite.password_change_template

自定义模板的路径,该模板将由管理员网站密码更改视图使用。

AdminSite.password_change_done_template

自定义模板的路径,该模板将由管理员网站密码更改完成视图使用。

AdminSite 方法

AdminSite.each_context(request)

返回一个变量字典,用于放置在管理站点中每个页面的模板上下文中。

默认情况下包括以下变量和值

  • site_header: AdminSite.site_header

  • site_title: AdminSite.site_title

  • site_url: AdminSite.site_url

  • has_permission: AdminSite.has_permission()

  • available_apps: 应用程序注册表 中的应用程序列表,可供当前用户使用。列表中的每个条目都是一个表示应用程序的字典,其中包含以下键

    • app_label: 应用程序标签
    • app_url: 管理中应用程序索引的 URL
    • has_module_perms: 一个布尔值,表示当前用户是否允许显示和访问模块的索引页
    • models: 应用程序中可用的模型列表

    每个模型都是一个具有以下键的字典

    • model:模型类
    • object_name:模型的类名
    • name:模型的复数名称
    • perms:一个 dict,用于跟踪 addchangedeleteview 权限
    • admin_url:模型的管理变更列表 URL
    • add_url:添加新模型实例的管理 URL
  • is_popup:当前页面是否显示在弹出窗口中

  • is_nav_sidebar_enabledAdminSite.enable_nav_sidebar

  • log_entriesAdminSite.get_log_entries()

AdminSite.get_app_list(request, app_label=None)

应用程序注册表 返回当前用户可用的应用程序列表。你可以选择传递一个 app_label 参数来获取单个应用程序的详细信息。列表中的每个条目都是一个表示应用程序的字典,具有以下键

  • app_label: 应用程序标签
  • app_url: 管理中应用程序索引的 URL
  • has_module_perms: 一个布尔值,表示当前用户是否允许显示和访问模块的索引页
  • models: 应用程序中可用的模型列表
  • name:应用程序的名称

每个模型都是一个具有以下键的字典

  • model:模型类
  • object_name:模型的类名
  • name:模型的复数名称
  • perms:一个 dict,用于跟踪 addchangedeleteview 权限
  • admin_url:模型的管理变更列表 URL
  • add_url:添加新模型实例的管理 URL

应用程序和模型的列表按名称按字母顺序排列。你可以在管理索引页面上覆盖此方法以更改默认顺序。

AdminSite.has_permission(request)

如果给定 HttpRequest 的用户有权查看管理网站中的至少一页,则返回 True。默认情况下,要求 User.is_activeUser.is_staff 均为 True

AdminSite.register(model_or_iterable, admin_class=None, **options)

使用给定的 admin_class 注册给定的模型类(或类的可迭代对象)。 admin_class 默认为 ModelAdmin(默认的管理选项)。如果给出了关键字参数(例如 list_display),它们将作为选项应用于管理类。

如果模型是抽象的,则引发 ImproperlyConfigured;如果模型已注册,则引发 django.contrib.admin.exceptions.AlreadyRegistered

AdminSite.unregister(model_or_iterable)

取消注册给定的模型类(或类的可迭代对象)。

如果模型尚未注册,则引发 django.contrib.admin.exceptions.NotRegistered

AdminSite.get_model_admin(model)
Django 5.0 中的新增功能。

返回给定模型类的管理类。如果模型未注册,则引发 django.contrib.admin.exceptions.NotRegistered

AdminSite.get_log_entries(request)
Django 5.0 中的新增功能。

返回一个查询集,用于在网站索引页面上显示相关的 LogEntry 实例。此方法可以被覆盖,以按其他条件过滤日志条目。

AdminSite 实例挂接到你的 URLconf

设置 Django 管理的最后一步是将你的 AdminSite 实例挂接到你的 URLconf。通过将给定的 URL 指向 AdminSite.urls 方法来实现。没有必要使用 include()

在此示例中,我们在 URL /admin/ 处注册了默认 AdminSite 实例 django.contrib.admin.site

# urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path("admin/", admin.site.urls),
]

自定义 AdminSite

如果你希望使用自定义行为设置自己的管理网站,你可以随意地对 AdminSite 进行子类化,并覆盖或添加任何你喜欢的功能。然后,创建一个 AdminSite 子类的实例(以你实例化任何其他 Python 类的相同方式),并使用它来注册你的模型和 ModelAdmin 子类,而不是使用默认网站。最后,更新 myproject/urls.py 以引用你的 AdminSite 子类。

myapp/admin.py
from django.contrib import admin

from .models import MyModel


class MyAdminSite(admin.AdminSite):
    site_header = "Monty Python administration"


admin_site = MyAdminSite(name="myadmin")
admin_site.register(MyModel)
myproject/urls.py
from django.urls import path

from myapp.admin import admin_site

urlpatterns = [
    path("myadmin/", admin_site.urls),
]

请注意,在使用您自己的 AdminSite 实例时,您可能不希望自动发现 admin 模块,因为您很可能在 myproject.admin 模块中导入所有按应用划分的 admin 模块。这意味着您需要在 INSTALLED_APPS 设置中将 'django.contrib.admin.apps.SimpleAdminConfig' 放在 'django.contrib.admin' 的位置。

覆盖默认管理站点

您可以通过将自定义 AppConfigdefault_site 属性设置为 AdminSite 子类的点导入路径或返回站点实例的可调用对象,来覆盖默认的 django.contrib.admin.site

myproject/admin.py
from django.contrib import admin


class MyAdminSite(admin.AdminSite): ...
myproject/apps.py
from django.contrib.admin.apps import AdminConfig


class MyAdminConfig(AdminConfig):
    default_site = "myproject.admin.MyAdminSite"
myproject/settings.py
INSTALLED_APPS = [
    # ...
    "myproject.apps.MyAdminConfig",  # replaces 'django.contrib.admin'
    # ...
]

在同一 URLconf 中有多个管理站点

您可以在同一 Django 支持的网站上创建管理站点的多个实例。创建 AdminSite 的多个实例,并将每个实例放在不同的 URL 中。

在此示例中,URL /basic-admin//advanced-admin/ 分别使用 AdminSite 实例 myproject.admin.basic_sitemyproject.admin.advanced_site 提供管理站点的不同版本。

# urls.py
from django.urls import path
from myproject.admin import advanced_site, basic_site

urlpatterns = [
    path("basic-admin/", basic_site.urls),
    path("advanced-admin/", advanced_site.urls),
]

AdminSite 实例采用其构造函数的单个参数,即其名称,可以是您喜欢的任何名称。此参数将成为 URL 名称的前缀,以便 反转它们。仅当您使用多个 AdminSite 时才需要这样做。

向管理站点添加视图

就像 ModelAdmin 一样,AdminSite 提供了一个 get_urls() 方法,可以覆盖该方法以定义站点的其他视图。要向你的管理站点添加一个新视图,请扩展基本 get_urls() 方法,以包含新视图的模式。

注意

你呈现的任何使用管理模板或扩展基本管理模板的视图,都应在呈现模板之前设置 request.current_app。如果你的视图位于 AdminSite 上,则应将其设置为 self.name,如果你的视图位于 ModelAdmin 上,则应将其设置为 self.admin_site.name

添加密码重置功能

你可以通过向你的 URLconf 添加几行来向管理站点添加密码重置功能。具体来说,添加这四个模式

from django.contrib import admin
from django.contrib.auth import views as auth_views

path(
    "admin/password_reset/",
    auth_views.PasswordResetView.as_view(
        extra_context={"site_header": admin.site.site_header}
    ),
    name="admin_password_reset",
),
path(
    "admin/password_reset/done/",
    auth_views.PasswordResetDoneView.as_view(
        extra_context={"site_header": admin.site.site_header}
    ),
    name="password_reset_done",
),
path(
    "reset/<uidb64>/<token>/",
    auth_views.PasswordResetConfirmView.as_view(
        extra_context={"site_header": admin.site.site_header}
    ),
    name="password_reset_confirm",
),
path(
    "reset/done/",
    auth_views.PasswordResetCompleteView.as_view(
        extra_context={"site_header": admin.site.site_header}
    ),
    name="password_reset_complete",
),

(这假设你在 admin/ 中添加了管理,并且要求你在包含管理应用程序本身的那一行之前放置以 ^admin/ 开头的 URL)。

命名 URL admin_password_reset 的存在将导致在密码框下方的默认管理登录页面上出现“忘记密码?”链接。

LogEntry 对象

class models.LogEntry

LogEntry 类跟踪通过管理界面完成的对象的添加、更改和删除。

LogEntry 属性

LogEntry.action_time

操作的日期和时间。

LogEntry.user

执行操作的用户(AUTH_USER_MODEL 实例)。

LogEntry.content_type

修改对象的 ContentType

LogEntry.object_id

修改对象的文本表示形式的主键。

LogEntry.object_repr

修改后的对象的 repr()

LogEntry.action_flag

记录的操作类型:ADDITIONCHANGEDELETION

例如,要获取通过管理员完成的所有添加操作的列表

from django.contrib.admin.models import ADDITION, LogEntry

LogEntry.objects.filter(action_flag=ADDITION)
LogEntry.change_message

修改的详细说明。例如,在编辑的情况下,消息包含已编辑字段的列表。Django 管理员站点将此内容格式化为 JSON 结构,以便 get_change_message() 可以重新组合以当前用户语言翻译的消息。不过,自定义代码可能会将其设置为纯字符串。建议您使用 get_change_message() 方法来检索此值,而不是直接访问它。

LogEntry 方法

LogEntry.get_edited_object()

返回引用的对象的快捷方式。

LogEntry.get_change_message()

格式化并翻译 change_message 为当前用户语言。在 Django 1.10 之前创建的消息将始终以记录时的语言显示。

反转管理 URL

部署 AdminSite 时,可以使用 Django 的 URL 反转系统 访问该站点提供的视图。

AdminSite 提供以下命名 URL 模式

页面 URL 名称 参数
索引 index  
登录 login  
注销 logout  
更改密码 password_change  
密码更改完成 password_change_done  
i18n JavaScript jsi18n  
应用程序索引页面 app_list app_label
重定向到对象的页面 view_on_site content_type_id, object_id

每个 ModelAdmin 实例提供一组额外的命名 URL

页面 URL 名称 参数
变更列表 {{ app_label }}_{{ model_name }}_changelist  
添加 {{ app_label }}_{{ model_name }}_add  
历史记录 {{ app_label }}_{{ model_name }}_history object_id
删除 {{ app_label }}_{{ model_name }}_delete object_id
更改 {{ app_label }}_{{ model_name }}_change object_id

UserAdmin 提供一个命名 URL

页面 URL 名称 参数
更改密码 auth_user_password_change user_id

这些命名 URL 已在应用程序命名空间 admin 中注册,并在与站点实例名称相对应的实例命名空间中注册。

因此 - 如果你想获取默认管理中的特定 Choice 对象(来自 polls 应用程序)的更改视图的引用,则可以调用

>>> from django.urls import reverse
>>> c = Choice.objects.get(...)
>>> change_url = reverse("admin:polls_choice_change", args=(c.id,))

这将找到管理应用程序的第一个已注册实例(无论实例名称是什么),并解析为在该实例中更改 poll.Choice 实例的视图。

如果你想在特定管理实例中找到 URL,请将该实例的名称作为 current_app 提示提供给反向调用。例如,如果你特别想要名为 custom 的管理实例中的管理视图,则需要调用

>>> change_url = reverse("admin:polls_choice_change", args=(c.id,), current_app="custom")

有关更多详细信息,请参阅 反转命名空间 URL 中的文档。

为了更容易地反转模板中的管理 URL,Django 提供了一个 admin_urlname 过滤器,它将操作作为参数

{% load admin_urls %}
<a href="{% url opts|admin_urlname:'add' %}">Add user</a>
<a href="{% url opts|admin_urlname:'delete' user.pk %}">Delete this user</a>

上面示例中的操作与上面描述的 ModelAdmin 实例的 URL 名称的最后部分匹配。 opts 变量可以是具有 app_labelmodel_name 属性的任何对象,并且通常由当前模型的管理视图提供。

display 装饰器

display(*, boolean=None, ordering=None, description=None, empty_value=None)

此装饰器可用于为自定义显示函数设置特定属性,这些属性可与 list_displayreadonly_fields 一起使用

@admin.display(
    boolean=True,
    ordering="-publish_date",
    description="Is Published?",
)
def is_published(self, obj):
    return obj.publish_date is not None

这等同于直接在函数上设置某些属性(使用原始的、较长的名称)

def is_published(self, obj):
    return obj.publish_date is not None


is_published.boolean = True
is_published.admin_order_field = "-publish_date"
is_published.short_description = "Is Published?"

另请注意,empty_value 装饰器参数映射到直接分配给函数的 empty_value_display 属性。它不能与 boolean 结合使用,它们是互斥的。

使用此装饰器并不是创建显示函数的强制要求,但将其用作源代码中的标记来标识函数的目的可能很有用,而无需参数

@admin.display
def published_year(self, obj):
    return obj.publish_date.year

在这种情况下,它不会向函数添加任何属性。

staff_member_required 装饰器

staff_member_required(redirect_field_name='next', login_url='admin:login')

此装饰器用于需要授权的管理视图。使用此函数装饰的视图将具有以下行为

  • 如果用户已登录,是工作人员(User.is_staff=True),并且处于活动状态(User.is_active=True),则正常执行视图。
  • 否则,请求将被重定向到由 login_url 参数指定的 URL,其中原始请求的路径在由 redirect_field_name 指定的查询字符串变量中。例如:/admin/login/?next=/admin/polls/question/3/

示例用法

from django.contrib.admin.views.decorators import staff_member_required


@staff_member_required
def my_view(request): ...
返回顶部