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.sessions.middleware.SessionMiddlewaredjango.contrib.auth.middleware.AuthenticationMiddlewaredjango.contrib.messages.middleware.MessageMiddleware

  4. 将管理员的 URL 挂接到您的 URLconf 中.

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

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

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

其他主题

另请参阅

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

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

ModelAdmin 对象

class ModelAdmin[source]

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)[source]

还有一个用于注册 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)

管理员文件的发现

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

class apps.AdminConfig

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

class apps.SimpleAdminConfig

此类的工作方式类似于 AdminConfig,但它不会调用 autodiscover()

default_site

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

autodiscover()[source]

此函数尝试在每个已安装的应用中导入一个admin模块。这些模块预计会将模型注册到管理员中。

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

如果您正在使用自定义的AdminSite,通常的做法是将所有ModelAdmin子类导入到您的代码中,并将它们注册到自定义的AdminSite。在这种情况下,为了禁用自动发现,您应该将'django.contrib.admin.apps.SimpleAdminConfig'而不是'django.contrib.admin'放在您的INSTALLED_APPS设置中。

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 将默认显示每个不是AutoField并且具有editable=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

如果既没有 fieldsets 也没有 fields 选项,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 类的列表或元组。这可以包括项目中定义的任何自定义 CSS 类,以及 Django 提供的任何 CSS 类。在默认的管理站点 CSS 样式表中,定义了两个特别有用的类:collapsewide

    示例

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

    具有 wide 样式的字段集将在管理界面中获得额外的水平空间。具有名称和 collapse 样式的字段集将最初处于折叠状态,使用可扩展的小部件和一个切换按钮来切换它们的可见性。

    Django 5.1 中的更改

    使用 collapse 类的 fieldsets 现在使用 <details><summary> 元素,前提是它们定义了 name

  • description

    一个可选的额外文本字符串,显示在每个字段集的顶部,位于字段集标题下方。

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

TabularInlinefieldsets 的支持有限

TabularInline 中使用 fieldsets 功能有限。您可以通过在 field_options 字典中定义 fields 来指定将在 TabularInline 布局中显示的字段及其顺序。

所有其他功能都不受支持。这包括使用 name 为一组字段定义标题。

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"]
    
  • 相关字段的名称,使用 __ 符号。例如

    class PersonAdmin(admin.ModelAdmin):
        list_display = ["city__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"]
    
Django 5.1 中的更改

在针对相关字段时,添加了对使用 __ 查找的支持。

关于 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"]
    
  • __str__() 方法在 list_display 中与任何其他模型方法一样有效,因此完全可以这样做。

    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() 方法。

注意

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

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,则会导致对象的 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 的 admin 使用选择框界面(<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 自动完成输入框。

默认情况下,admin 为这些字段使用选择框界面(<select>)。有时您不希望承担显示下拉菜单中所有相关实例的开销。

Select2 输入框看起来类似于默认输入框,但它带有一个异步加载选项的搜索功能。如果相关模型有很多实例,这会更快且更友好。

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

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

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

在以下示例中,ChoiceAdmin 具有一个指向 QuestionForeignKey 的自动完成字段。结果将根据 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 的 admin 使用选择框界面(<select>)来显示那些是 ForeignKey 的字段。有时您不希望承担必须选择所有相关实例以在下拉菜单中显示的开销。

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

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

如果使用 readonly_fields 且没有通过 ModelAdmin.fieldsModelAdmin.fieldsets 定义显式排序,则它们将被添加到所有可编辑字段之后。

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

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 以在 admin 修改表单上启用“另存为新文件”功能。

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

默认情况下,save_as 设置为 False

ModelAdmin.save_as_continue

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

默认情况下,save_as_continue 设置为 True

ModelAdmin.save_on_top

设置 save_on_top 以在 admin 修改表单的顶部添加保存按钮。

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

默认情况下,save_on_top 设置为 False

ModelAdmin.search_fields

设置 search_fields 以在 admin 更改列表页面上启用搜索框。这应设置为一个字段名称列表,每当有人在该文本框中提交搜索查询时,将搜索这些字段。

这些字段应为某种类型的文本字段,例如 CharFieldTextField。您还可以使用查找 API 的“跟随”表示法对 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

默认情况下,更改列表页面允许按所有模型字段(以及使用 display() 装饰器的 ordering 参数或具有 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)[source]

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)[source]

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

ModelAdmin.delete_queryset(request, queryset)[source]

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

ModelAdmin.save_formset(request, form, formset, change)[source]

save_formset 方法接收 HttpRequest、父 ModelForm 实例和一个布尔值(表示是添加还是修改父对象)。

例如,将 request.user 附加到每个已更改的 formset 模型实例上

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()

另见 在 formset 中保存对象

警告

所有返回 ModelAdmin 属性的钩子都返回属性本身,而不是其值的副本。动态修改值可能会导致意外的结果。

ModelAdmin.get_readonly_fields() 为例

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

    def get_readonly_fields(self, request, obj=None):
        readonly = super().get_readonly_fields(request, obj)
        if not request.user.is_superuser:
            readonly.append("age")  # Edits the class attribute.
        return readonly

这会导致 readonly_fields 变成 ["name", "age", "age", ...],即使对于超级用户也是如此,因为每次非超级用户访问页面时都会添加 "age"

ModelAdmin.get_ordering(request)

get_ordering 方法以 request 作为参数,并期望返回一个用于排序的 listtuple,类似于 ordering 属性。例如

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)[source]

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') 更有效,后者会导致对数字字段进行字符串比较,例如在 PostgreSQL 上为 ... OR UPPER("polls_choice"."votes"::text) = UPPER('4')

save_related 方法接收 HttpRequest、父 ModelForm 实例、内联 formset 列表和一个布尔值(表示父对象是正在添加还是修改)。在这里,你可以对与父对象相关的对象执行任何保存前或保存后的操作。请注意,此时父对象及其表单已保存。

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)[source]

get_list_display 方法接收 HttpRequest,并期望返回一个 listtuple,其中包含将在更改列表视图中显示的字段名称,如上文 ModelAdmin.list_display 部分所述。

get_list_display_links 方法接收 HttpRequestModelAdmin.get_list_display() 返回的 listtuple。它期望返回 None 或一个 listtuple,其中包含更改列表中将链接到更改视图的字段名称,如 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)[source]

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

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

ModelAdmin.get_search_fields(request)[source]

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)[source]

给定的 get_inline_instances 方法会接收 HttpRequest 和正在编辑的 obj(或在添加表单上为 None),并期望返回一个 listtuple,其中包含 InlineModelAdmin 对象,如下面的 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()[source]

ModelAdmin 上的 get_urls 方法返回要用于该 ModelAdmin 的 URL,方式与 URLconf 相同。因此,您可以按照 URL 分发器 中所述扩展它们,在您的视图上使用 AdminSite.admin_view() 包装器。

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)[source]

返回一个用于管理员添加和更改视图的 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)[source]

为管理员添加和更改视图生成 (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属性都将仅限于该表单字段。如果模型上的对应字段设置了choices,则提供给表单的choices必须是这些choices的有效子集,否则表单提交将失败,并在保存前模型本身进行验证时引发ValidationError

ModelAdmin.get_changelist(request, **kwargs)[source]

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

ModelAdmin.get_changelist_form(request, **kwargs)[source]

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

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)[source]

如果使用了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()中使用的查找(例如user__email=user@example.com)。由于查询字符串中的查找可以由用户操纵,因此必须对其进行清理以防止未经授权的数据泄露。

给定lookup_allowed()方法来自查询字符串的查找路径(例如'user__email'),相应的value(例如'user@example.com')以及请求,并返回一个布尔值,指示是否允许使用这些参数过滤更改列表的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。如果objNone,则应返回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)

ModelAdmin上的get_queryset方法返回一个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)[source]

使用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)[source]

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

ModelAdmin.response_add(request, obj, post_url_continue=None)[source]

确定HttpResponseadd_view()阶段。

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

ModelAdmin.response_change(request, obj)[source]

确定HttpResponsechange_view()阶段。

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

ModelAdmin.response_delete(request, obj_display, obj_id)[source]

确定HttpResponsedelete_view()阶段。

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

obj_display是已删除对象的名称字符串。

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

ModelAdmin.get_formset_kwargs(request, obj, inline, prefix)[source]

用于自定义传递给表单集构造函数的关键字参数的钩子。例如,将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)[source]

管理更改表单的初始数据的钩子。默认情况下,字段从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)[source]

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

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

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

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

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

perms_needed 是一个集合,包含用户没有删除权限的模型的 verbose_name

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

其他方法

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

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

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

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

ModelAdmin.changelist_view(request, extra_context=None)[source]

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

ModelAdmin.delete_view(request, object_id, extra_context=None)[source]

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

ModelAdmin.history_view(request, object_id, extra_context=None)[source]

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

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

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

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(如果 STATIC_URLNone,则为 MEDIA_URL)添加到任何资产路径。适用的规则与 表单上的常规资产定义 相同。

jQuery

Django admin JavaScript 使用 jQuery 库。

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

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)的未压缩版本;否则,它将返回“最小化”版本。

向 admin 添加自定义验证

您还可以向 admin 中添加自定义数据验证。自动 admin 界面重用 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[source]
class StackedInline[source]

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

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 选项

InlineModelAdminModelAdmin 共享许多相同的特性,并添加了一些自己的特性(共享的特性实际上是在 BaseModelAdmin 超类中定义的)。共享的特性包括:

InlineModelAdmin 类添加或自定义了以下内容:

InlineModelAdmin.model

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

InlineModelAdmin.fk_name

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

InlineModelAdmin.formset

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

InlineModelAdmin.form

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

警告

在为 InlineModelAdmin 表单编写自定义验证时,请注意不要编写依赖于父模型特性的验证。如果父模型验证失败,它可能会处于不一致的状态,如 ModelForm 上的验证 中的警告中所述。

InlineModelAdmin.classes

包含要应用于为内联渲染的字段集的额外 CSS 类的列表或元组。默认为 None。与在 fieldsets 中配置的类一样,具有 collapse 类的内联将使用可扩展小部件最初折叠。

Django 5.1 中的更改

使用 collapse 类的 fieldsets 现在使用 <details><summary> 元素,前提是它们定义了 name

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 的 admin 使用选择框界面(<select>)来显示那些是 ForeignKey 的字段。有时您不希望承担必须选择所有相关实例以在下拉菜单中显示的开销。

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 属性自定义外观。

与多对多中间模型一起使用

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

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

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 实例,可以使用 GenericTabularInlineGenericStackedInline(均为 GenericInlineModelAdmin 的子类)由 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 选项中指定的任何目录,该选项位于 TEMPLATES 设置的 DjangoTemplates 后端中。如果你自定义了 'loaders' 选项,请确保 'django.template.loaders.filesystem.Loader' 出现在 'django.template.loaders.app_directories.Loader' 之前,以便模板加载系统在查找包含在 django.contrib.admin 中的模板之前找到你的自定义模板。

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

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

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

如果我们想仅为名为“Page”的特定模型的更改列表视图添加一个工具,我们会将同一文件复制到项目的 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')[source]

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)[source]

返回一个字典,其中包含要放入管理站点中每个页面模板上下文中的变量。

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

  • 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_enabled: AdminSite.enable_nav_sidebar

  • log_entries: AdminSite.get_log_entries()

AdminSite.get_app_list(request, app_label=None)[source]

返回当前用户可用的 应用程序注册表 中的应用程序列表。您可以选择传递一个 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)[source]

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

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

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

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

AdminSite.unregister(model_or_iterable)[source]

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

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

AdminSite.get_model_admin(model)[source]
Django 5.0 中的新功能。

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

AdminSite.get_log_entries(request)[source]
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。它应该设置为self.name(如果您的视图位于AdminSite上)或self.admin_site.name(如果您的视图位于ModelAdmin上)。

添加密码重置功能

您可以通过在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)。

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

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()[source]

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

LogEntry.get_change_message()[source]

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_idobject_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)[source]

此装饰器可用于设置自定义显示函数上的特定属性,这些属性可与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')[source]

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

  • 如果用户已登录,是工作人员(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): ...
返回顶部