Django 管理站点 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/ref/contrib/admin/index
跳转至:导航、​搜索

Django 管理站点

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

管理员有许多用于自定义的钩子,但要小心尝试专门使用这些钩子。 如果您需要提供一个更加以流程为中心的接口来抽象出数据库表和字段的实现细节,那么可能是时候编写您自己的视图了。

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

概况

admin 在 :djadmin:`startproject` 使用的默认项目模板中启用。

如果您不使用默认项目模板,则有以下要求:

  1. 添加 'django.contrib.admin' 及其依赖项 - django.contrib.authdjango.contrib.contenttypesdjango.contrib.messages]django.contrib.sessions - 到你的 :setting:`INSTALLED_APPS` 设置。
  2. 配置一个 Django模板在你的后端 :设置:`模板` 设置与django.contrib.auth.context_processors.authdjango.contrib.messages.context_processors.messages在里面'context_processors'选项 :设置:`选项 ` .
  3. 如果您自定义了 :setting:`MIDDLEWARE` 设置,则 django.contrib.auth.middleware.AuthenticationMiddlewaredjango.contrib.messages.middleware.MessageMiddleware ] 必须包括在内。
  1. 将管理员的 URL 连接到您的 URLconf

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

如果您需要创建一个用户登录,请使用 :djadmin:`createsuperuser` 命令。 默认情况下,登录管理员要求用户将 is_superuseris_staff 属性设置为 True

最后,确定您的应用程序的哪些模型应该在管理界面中是可编辑的。 对于这些模型中的每一个,按照 ModelAdmin 中的描述向管理员注册它们。

其他主题

也可以看看

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

有问题? 尝试 常见问题解答:管理员


ModelAdmin 物体

class ModelAdmin

ModelAdmin 类是管理界面中模型的表示。 通常,它们存储在应用程序中名为 admin.py 的文件中。 我们来看一个非常简单的ModelAdmin的例子:

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

class AuthorAdmin(admin.ModelAdmin):
    pass
admin.site.register(Author, AuthorAdmin)

你需要一个 ModelAdmin 对象吗?

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

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

admin.site.register(Author)

register 装饰器

register(*models, site=django.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)


发现管理文件

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

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

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

default_site

2.1 版中的新功能。

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

autodiscover()

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

通常你不需要直接调用这个函数,因为 AdminConfig 在 Django 启动时调用它。

如果您使用自定义 AdminSite,通常将所有 ModelAdmin 子类导入您的代码并将它们注册到自定义 AdminSite。 在这种情况下,为了禁用自动发现,您应该在 :setting:`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):
    fields = ('name', 'title', 'view_birth_date')

    def view_birth_date(self, obj):
        return obj.birth_date

    view_birth_date.empty_value_display = '???'
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')

注解

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

如果 fieldsfieldsets 选项都不存在,Django 将默认显示每个不是 AutoField 并且有 editable=True 的字段,在一个单一的字段集,其顺序与模型中定义的字段顺序相同。

ModelAdmin.fieldsets

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

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

二元组的格式为 (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'),
        }),
    )

这样一来,管理页面就变成了这样:

[[../File:../../../_images/fieldsets|../../../_images/fieldsets.png]]

如果 fieldsetsfields 选项都不存在,Django 将默认显示每个不是 AutoField 并且有 editable=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() 方法自定义默认表单,而不是指定一个全新的表单。

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

注解

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

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

注解

如果您的 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

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

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},
    }

注意字典中的key是实际的字段类,不是一个字符串。 该值是另一个字典; 这些参数将传递给表单域的 __init__() 方法。 有关详细信息,请参阅 Forms 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来控制在admin的更改列表页面上显示哪些字段。

例子:

list_display = ('first_name', 'last_name')

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

list_display 中可以使用四种类型的值:

  • 模型字段的名称。 例如:

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

    def upper_case_name(obj):
        return ("%s %s" % (obj.first_name, obj.last_name)).upper()
    upper_case_name.short_description = 'Name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = (upper_case_name,)
  • 表示 ModelAdmin 方法的字符串,该方法接受一个参数,即模型实例。 例如:

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('upper_case_name',)
    
        def upper_case_name(self, obj):
            return ("%s %s" % (obj.first_name, obj.last_name)).upper()
        upper_case_name.short_description = 'Name'
  • 表示模型属性或方法的字符串(没有任何必需的参数)。 例如:

    from django.contrib import admin
    from django.db import models
    
    class Person(models.Model):
        name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        def decade_born_in(self):
            return self.birthday.strftime('%Y')[:3] + "0's"
        decade_born_in.short_description = 'Birth decade'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'decade_born_in')

关于list_display的几个特殊情况需要注意:

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

  • 不支持 ManyToManyField 字段,因为这需要为表中的每一行执行单独的 SQL 语句。 尽管如此,如果您想这样做,请为您的模型提供一个自定义方法,并将该方法的名称添加到 list_display。 (有关 list_display 中自定义方法的更多信息,请参见下文。)

  • 如果字段是 BooleanField,Django 将显示一个漂亮的“开”或“关”图标,而不是 TrueFalse

  • 如果给定的字符串是模型的方法,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)
    
        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 方法时,您可以通过向可调用添加 short_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')
    
        def birth_date_view(self, obj):
             return obj.birth_date
    
        birth_date_view.empty_value_display = 'unknown'
  • 如果给定的字符串是模型的方法,则 ModelAdmin 或返回 True 或 False 的可调用对象如果给方法一个 boolean,Django 将显示一个漂亮的“开”或“关”图标值为 True 的属性。

    这是一个完整的示例模型:

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

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

    但是,如果 list_display 的元素代表某个数据库字段,则可以通过设置该项目的 admin_order_field 属性来表明这一事实。

    例如:

    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)
    
        def colored_first_name(self):
            return format_html(
                '<span style="color: #{};">{}</span>',
                self.color_code,
                self.first_name,
            )
    
        colored_first_name.admin_order_field = 'first_name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'colored_first_name')

    当尝试在管理中按 colored_first_name 排序时,上面将告诉 Django 按 first_name 字段排序。

    要使用 admin_order_field 表示降序,您可以在字段名称上使用连字符前缀。 使用上面的例子,这看起来像:

    colored_first_name.admin_order_field = '-first_name'

    admin_order_field 支持查询查找以按相关模型上的值排序。 此示例在列表显示中包含一个“作者名字”列,并允许按名字对其进行排序:

    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')
    
        def author_first_name(self, obj):
            return obj.author.first_name
    
        author_first_name.admin_order_field = 'author__first_name'

    查询表达式可用于admin_order_field。 例如:

    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)
    
        def full_name(self):
            return self.first_name + ' ' + self.last_name
        full_name.admin_order_field = Concat('first_name', Value(' '), 'last_name')

    2.1 新功能: 增加了对 admin_order_field 中表达式的支持。

  • list_display 的元素也可以是属性。 但是请注意,由于属性在 Python 中的工作方式,只有在使用 property() 函数并且 @property 装饰器。

    例如:

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
    
        def my_property(self):
            return self.first_name + ' ' + self.last_name
        my_property.short_description = "Full name of the person"
    
        full_name = property(my_property)
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('full_name',)
  • list_display 中的字段名称也将作为 CSS 类出现在 HTML 输出中,在每个 <th> 元素上以 column-<field_name> 的形式出现。 例如,这可用于在 CSS 文件中设置列宽。

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

    • 一个模型的字段。

    • 一个可调用对象。

    • 表示 ModelAdmin 属性的字符串。

    • 一个代表模型属性的字符串。

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

ModelAdmin.list_display_links

使用 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 以激活管理员更改列表页面右侧边栏中的过滤器,如下图所示:

[[../File:../../../_images/list_filter|../../../_images/list_filter.png]]

list_filter 应该是元素的列表或元组,其中每个元素应该是以下类型之一:

  • 字段名称,其中指定字段应为 BooleanFieldCharFieldDateFieldDateTimeFieldIntegerFieldForeignKeyManyToManyField,例如:

    class PersonAdmin(admin.ModelAdmin):
        list_filter = ('is_staff', 'company')

    list_filter 中的字段名称也可以使用 __ 查找跨越关系,例如:

    class PersonAdmin(admin.UserAdmin):
        list_filter = ('company__name',)
  • 一个继承自 django.contrib.admin.SimpleListFilter 的类,您需要提供 titleparameter_name 属性并覆盖 lookupsqueryset 方法,例如:

    from datetime import date
    
    from django.contrib import admin
    from django.utils.translation import gettext_lazy as _
    
    class DecadeBornListFilter(admin.SimpleListFilter):
        # Human-readable title which will be displayed in the
        # right admin sidebar just above the filter options.
        title = _('decade born')
    
        # Parameter for the filter that will be used in the URL query.
        parameter_name = 'decade'
    
        def lookups(self, request, model_admin):
            """
            Returns a list of tuples. The first element in each
            tuple is the coded value for the option that will
            appear in the URL query. The second element is the
            human-readable name for the option that will appear
            in the right sidebar.
            """
            return (
                ('80s', _('in the eighties')),
                ('90s', _('in the nineties')),
            )
    
        def queryset(self, request, queryset):
            """
            Returns the filtered queryset based on the value
            provided in the query string and retrievable via
            `self.value()`.
            """
            # Compare the requested value (either '80s' or '90s')
            # to decide how to filter the queryset.
            if self.value() == '80s':
                return queryset.filter(birthday__gte=date(1980, 1, 1),
                                        birthday__lte=date(1989, 12, 31))
            if self.value() == '90s':
                return queryset.filter(birthday__gte=date(1990, 1, 1),
                                        birthday__lte=date(1999, 12, 31))
    
    class PersonAdmin(admin.ModelAdmin):
        list_filter = (DecadeBornListFilter,)

    注解

    为方便起见,将 HttpRequest 对象传递给 lookupsqueryset 方法,例如:

    class AuthDecadeBornListFilter(DecadeBornListFilter):
    
        def lookups(self, request, model_admin):
            if request.user.is_superuser:
                return super().lookups(request, model_admin)
    
        def queryset(self, request, queryset):
            if request.user.is_superuser:
                return super().queryset(request, queryset)

    同样为方便起见,ModelAdmin 对象被传递给 lookups 方法,例如,如果您想根据可用数据进行查找:

    class AdvancedDecadeBornListFilter(DecadeBornListFilter):
    
        def lookups(self, request, model_admin):
            """
            Only show the lookups if there actually is
            anyone born in the corresponding decades.
            """
            qs = model_admin.get_queryset(request)
            if qs.filter(birthday__gte=date(1980, 1, 1),
                          birthday__lte=date(1989, 12, 31)).exists():
                yield ('80s', _('in the eighties'))
            if qs.filter(birthday__gte=date(1990, 1, 1),
                          birthday__lte=date(1999, 12, 31)).exists():
                yield ('90s', _('in the nineties'))
  • 元组,其中第一个元素是字段名称,第二个元素是继承自 django.contrib.admin.FieldListFilter 的类,例如:

    class PersonAdmin(admin.ModelAdmin):
        list_filter = (
            ('is_staff', admin.BooleanFieldListFilter),
        )

    您可以使用 RelatedOnlyFieldListFilter 将相关模型的选择限制为该关系中涉及的对象:

    class BookAdmin(admin.ModelAdmin):
        list_filter = (
            ('author', admin.RelatedOnlyFieldListFilter),
        )

    假设 authorForeignKeyUser 模型,这会将 list_filter 选择限制为写过一本书的用户,而不是列出所有用户。

    注解

    FieldListFilter API 被认为是内部的,可能会更改。

列表过滤器通常仅在过滤器有多个选择时才会出现。 过滤器的 has_output() 方法控制它是否出现。

可以指定用于呈现列表过滤器的自定义模板:

class FilterWithCustomTemplate(admin.SimpleListFilter):
    template = "custom_template.html"

有关具体示例,请参阅 Django (admin/filter.html) 提供的默认模板。

ModelAdmin.list_max_show_all
设置 list_max_show_all 以控制在“全部显示”管理更改列表页面上可以显示的项目数量。 仅当总结果计数小于或等于此设置时,管理员才会在更改列表上显示“全部显示”链接。 默认情况下,它设置为 200
ModelAdmin.list_per_page
设置 list_per_page 以控制每个分页管理更改列表页面上显示的项目数。 默认情况下,它设置为 100
ModelAdmin.list_select_related

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

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

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

如果您需要更细粒度的控制,请使用元组(或列表)作为 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 字母; 并删除各种英语停用词,如“a”、“an”、“as”等)。

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

prepopulated_fields 不接受 DateTimeFieldForeignKeyOneToOneFieldManyToManyField 字段。

ModelAdmin.preserve_filters
管理员现在在创建、编辑或删除对象后保留列表视图上的过滤器。 您可以通过将此属性设置为 False 来恢复之前清除过滤器的行为。
ModelAdmin.radio_fields

默认情况下,Django 的管理员使用选择框界面( ) 对于字段ForeignKey或有choices放。 如果 radio_fields 中存在字段,Django 将使用单选按钮界面。 假设 groupPerson 型号上的 ForeignKey

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

您可以选择使用 django.contrib.admin 模块中的 HORIZONTALVERTICAL

不要在 radio_fields 中包含字段,除非它是 ForeignKey 或设置了 choices

ModelAdmin.autocomplete_fields

autocomplete_fields 是您想要更改为 Select2 自动完成输入的 ForeignKey 和/或 ManyToManyField 字段的列表。

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

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

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

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

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

在以下示例中,ChoiceAdmin 有一个自动完成字段,用于 ForeignKeyQuestion。 结果按 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. 有时您不想承担必须选择所有相关实例以显示在下拉列表中的开销。

raw_id_fields 是您想要更改为 ForeignKeyManyToManyFieldInput 小部件的字段列表:

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

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

[[../File:../../../_images/raw_id_fields|../../../_images/raw_id_fields.png]]

ModelAdmin.readonly_fields

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

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

只读字段不仅可以显示来自模型字段的数据,还可以显示模型方法或 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',)

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

    # short_description functions like a model field's verbose_name
    address_report.short_description = "Address"
ModelAdmin.save_as

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

通常,对象具有三个保存选项:“保存”、“保存并继续编辑”和“保存并添加另一个”。 如果 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 以在管理员更改表单的顶部添加保存按钮。

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

默认情况下,save_on_top 设置为 False

ModelAdmin.search_fields

设置 search_fields 以启用管理员更改列表页面上的搜索框。 这应该设置为一个字段名称列表,只要有人在该文本框中提交搜索查询,就会搜索该列表。

这些字段应该是某种文本字段,例如 CharFieldTextField。 您还可以使用查找 API “跟随”符号对 ForeignKeyManyToManyField 执行相关查找:

search_fields = ['foreign_key__related_fieldname']

例如,如果您有一个包含作者的博客条目,则以下定义将启用按作者的电子邮件地址搜索博客条目:

search_fields = ['user__email']

当有人在管理搜索框中进行搜索时,Django 将搜索查询拆分为单词并返回包含每个单词的所有对象,不区分大小写(使用 :lookup:`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%')

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

请注意,因为查询词如前所述被拆分和 AND 运算,所以使用 :lookup:`exact` 进行搜索仅适用于单个搜索词,因为除非所有词,否则两个或多个词不能完全匹配是相同的。

2.1 版新功能:添加了指定字段查找的功能。

一些(较旧的)用于指定字段查找的快捷方式也可用。 您可以在 search_fields 中的字段前添加以下字符,相当于在该字段中添加 __<lookup>

前缀

查找

^

:lookup:`startswith`

=

:lookup:`iexact`

@

:lookup:`search`

None

:lookup:`icontains`

如果您需要自定义搜索,您可以使用 ModelAdmin.get_search_results() 来提供额外或替代的搜索行为。

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

2.1 版中的新功能。

默认情况下,更改列表页面允许按 list_display 中指定的所有模型字段(以及具有 admin_order_field 属性的可调用对象)进行排序。

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

如果您需要动态指定此列表,请改为实现 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 和一个模型实例。 覆盖此方法允许执行删除前或删除后操作。 使用 Model.delete() 调用 super().delete_model() 删除对象。
ModelAdmin.delete_queryset(request, queryset)

2.1 版中的新功能。

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 作为参数,并预期返回 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)

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

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

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

例如,要按 nameage 进行搜索,您可以使用:

class PersonAdmin(admin.ModelAdmin):
    list_display = ('name', 'age')
    search_fields = ('name',)

    def get_search_results(self, request, queryset, search_term):
        queryset, use_distinct = 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, use_distinct

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

ModelAdmin.save_related(request, form, formsets, change)
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),并预期返回 [将显示为只读的字段名称的 X137X] 或 tuple,如上文 ModelAdmin.readonly_fields 部分所述。
ModelAdmin.get_prepopulated_fields(request, obj=None)
get_prepopulated_fields 方法被赋予 HttpRequest 和正在编辑的 obj(或添加表单上的 None),并预期返回 [ X137X],如上文 ModelAdmin.prepopulated_fields 部分所述。
ModelAdmin.get_list_display(request)
get_list_display 方法被赋予 HttpRequest 并预期返回字段名称的 listtuple,这些字段名称将显示在如上所述的更改列表视图中在 ModelAdmin.list_display 部分。
ModelAdmin.get_list_display_links(request, list_display)
get_list_display_links 方法由 ModelAdmin.get_list_display() 返回 HttpRequestlisttuple。 如 ModelAdmin 中所述,预计将返回更改列表中将链接到更改视图的字段名称的 Nonelisttuple .list_display_links 部分。
ModelAdmin.get_exclude(request, obj=None)
get_exclude 方法被赋予正在编辑的 HttpRequestobj(或添加表单上的 None),并预期返回字段列表,如 ModelAdmin.exclude 中所述。
ModelAdmin.get_fields(request, obj=None)
get_fields 方法被赋予正在编辑的 HttpRequestobj(或添加表单上的 None),并预期返回字段列表,如上文 ModelAdmin.fields 部分所述。
ModelAdmin.get_fieldsets(request, obj=None)
get_fieldsets 方法被赋予正在编辑的 HttpRequestobj(或添加表单上的 None),并预期返回两个列表 -元组,其中每个二元组代表管理表单页面上的 <fieldset>,如上文 ModelAdmin.fieldsets 部分所述。
ModelAdmin.get_list_filter(request)
get_list_filter 方法被赋予 HttpRequest 并期望返回与 list_filter 属性相同类型的序列类型。
ModelAdmin.get_list_select_related(request)
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)

2.1 版中的新功能。

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),并预期返回 [ InlineModelAdmin 对象的 X137X] 或 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_urls()

ModelAdmin 上的 get_urls 方法以与 URLconf 相同的方式返回要用于该 ModelAdmin 的 URL。 因此,您可以按照 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.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 %}

注解

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

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

然而,上面注册的 self.my_view 函数有两个问题:

  • 它将 执行任何权限检查,因此公众可以访问它。

  • 它将 not 提供任何标题详细信息以防止缓存。 这意味着如果页面从数据库中检索数据,并且缓存中间件处于活动状态,则页面可能会显示过时的信息。

由于这通常不是您想要的,Django 提供了一个方便的包装器来检查权限并将视图标记为不可缓存。 这个包装器是 AdminSite.admin_view()(即 ModelAdmin 实例中的 self.admin_site.admin_view); 像这样使用它:

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

注意上面第五行中的包装视图:

path('my_view/', self.admin_site.admin_view(self.my_view))

这种包装将保护 self.my_view 免受未经授权的访问,并将应用 django.views.decorators.cache.never_cache() 装饰器以确保它在缓存中间件处于活动状态时不被缓存。

如果页面可缓存,但您仍希望执行权限检查,则可以将 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 实例拥有的汽车。

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'] += (('ready', 'Ready for deployment'),)
        return super().formfield_for_choice_field(db_field, request, **kwargs)

注解

在表单域上设置的任何 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

注解

如果在 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)

更改列表页面中的对象可以通过从 URL 的查询字符串中查找来过滤。 例如,这就是 list_filter 的工作原理。 查找类似于 QuerySet.filter() 中使用的内容(例如 user__email=user@example.com)。 由于查询字符串中的查找可由用户操纵,因此必须对其进行清理以防止未经授权的数据暴露。

lookup_allowed() 方法从查询字符串(例如 'user__email') 和相应的值(例如 'user@example.com'),并返回一个布尔值,指示是否允许使用参数过滤更改列表的 QuerySet。 如果 lookup_allowed() 返回 False,则会引发 DisallowedModelAdminLookupSuspiciousOperation 的子类)。

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

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

ModelAdmin.has_view_permission(request, obj=None)

2.1 版中的新功能。

如果允许查看 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 以指示通常是否允许编辑此类型的对象(例如,[ X151X] 将被解释为表示不允许当前用户编辑任何此类对象)。
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() 和 [ X183X]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)

使用 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)

确定 add_view() 阶段的 HttpResponse

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

ModelAdmin.response_change(request, obj)

确定 change_view() 阶段的 HttpResponse

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

ModelAdmin.response_delete(request, obj_display, obj_id)

确定 delete_view() 阶段的 HttpResponse

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

obj_display 是一个带有被删除对象名称的字符串。

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

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)

2.1 版中的新功能。

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

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

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

deleted_objects 是一个字符串列表,表示将被删除的所有对象。 如果有任何要删除的相关对象,则该列表是嵌套的并包括那些相关对象。 该列表在模板中使用 :tfilter:`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 app 前置 :setting:`STATIC_URL`(或 :setting:`MEDIA_URL` 如果 :setting:`STATIC_URL`None) 到任何资产路径。 相同的规则适用于表格 上的 常规资产定义。

jQuery

Django 管理 JavaScript 使用 jQuery 库。

为避免与用户提供的脚本或库发生冲突,Django 的 jQuery(版本 3.3.1)命名空间为 django.jQuery。 如果您想在您自己的管理 JavaScript 中使用 jQuery 而无需包含第二个副本,您可以在更改列表中使用 django.jQuery 对象并添加/编辑视图。

2.1 版本变化:jQuery 从 2.2.3 升级到 3.3.1。


2.2.13 版本变化:jQuery 从 3.3.1 升级到 3.5.1。


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

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

ModelAdminInlineModelAdmin 有一个 media 属性返回一个 Media 对象列表,这些对象存储表单和/或表单集的 JavaScript 文件的路径. 如果 :setting:`DEBUG`True 它将返回各种 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 选项

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

InlineModelAdmin 类添加或自定义:

InlineModelAdmin.model
内联正在使用的模型。 这是必需的。
InlineModelAdmin.fk_name
模型上的外键名称。 在大多数情况下,这会自动处理,但如果同一个父模型有多个外键,则必须明确指定 fk_name
InlineModelAdmin.formset
默认为 BaseInlineFormSet。 使用您自己的表单集可以为您提供许多自定义的可能性。 内联是围绕 模型表单集 构建的。
InlineModelAdmin.form
form 的值默认为 ModelForm。 这是在为此内联创建表单集时传递给 inlineformset_factory() 的内容。

警告

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


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

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

对于使用支持 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. 有时您不想承担必须选择所有相关实例以显示在下拉列表中的开销。

raw_id_fields 是您想要更改为 ForeignKeyManyToManyFieldInput 小部件的字段列表:

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.can_delete
指定是否可以在内联中删除内联对象。 默认为 True
InlineModelAdmin.show_change_link
指定可以在管理员中更改的内联对象是否具有指向更改表单的链接。 默认为 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

2.1 版更改: 添加了 obj 参数。 在弃用期间,如果第三方调用has_add_permission()不提供,也可能是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_admin 小部件表示. 但是,也可以用内联替换这些小部件。

假设我们有以下模型:

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 myproject.myapp.models import Image, Product

class ImageInline(GenericTabularInline):
    model = Image

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

admin.site.register(Product, ProductAdmin)

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


覆盖管理模板

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

设置你的项目管理模板目录

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

为了覆盖其中一个或多个,首先在项目的 templates 目录中创建一个 admin 目录。 这可以是您在 :setting:`目录 ` 的选项DjangoTemplates后端在 :设置:`模板` 环境。 如果您自定义了 'loaders' 选项,请确保 'django.template.loaders.filesystem.Loader' 出现在 'django.template.loaders.app_directories.Loader' 之前,以便您的自定义模板会在 [X199X 包含的模板之前被模板加载系统找到]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 目录中。


覆盖 vs. 更换管理模板

由于管理模板的模块化设计,通常不需要也不建议替换整个模板。 仅覆盖模板中需要更改的部分几乎总是更好。

为了继续上面的例子,我们想在 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">{% trans "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">{% trans "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

2.1 版更改:能够覆盖 actions.htmlchange_form_object_tools.htmlchange_list_object_tools.htmlchange_list_results.htmldate_hierarchy.html添加了 pagination.htmlprepopulated_fields_js.htmlsearch_form.htmlsubmit_line.html 模板。


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

注解

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


根和登录模板

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


AdminSite 物体

class AdminSite(name='admin')

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

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

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

AdminSite 属性

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

AdminSite.site_header
放置在每个管理页面顶部的文本,作为 <h1>(一个字符串)。 默认情况下,这是“Django 管理”。
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
用于在管理站点的更改列表中显示空值的字符串。 默认为破折号。 通过在字段上设置 empty_value_display 属性,还可以基于每个 ModelAdminModelAdmin 内的自定义字段覆盖该值。 有关示例,请参阅 ModelAdmin.empty_value_display
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_headerAdminSite.site_header

  • site_titleAdminSite.site_title

  • site_urlAdminSite.site_url

  • has_permissionAdminSite.has_permission()

  • available_apps:当前用户可用的 应用程序注册表 中的应用程序列表。 列表中的每个条目都是一个字典,代表具有以下键的应用程序:

    • app_label:应用标签

    • app_url:admin中应用索引的URL

    • has_module_perms:一个布尔值,指示当前用户是否允许显示和访问模块的索引页面

    • models:应用程序中可用的模型列表

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

    • object_name:模型的类名

    • name:型号的复数名称

    • permsdict 跟踪 addchangedeleteview 权限

    • admin_url:模型的管理员更改列表 URL

    • add_url:添加新模型实例的管理 URL

AdminSite.has_permission(request)
如果给定 HttpRequest 的用户有权查看管理站点中的至少一个页面,则返回 True。 默认要求 User.is_activeUser.is_staffTrue
AdminSite.register(model_or_iterable, admin_class=None, **options)

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

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


将 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.admin import AdminSite

from .models import MyModel

class MyAdminSite(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 模块,因为您可能会在 [ X182X] 模块。 这意味着您需要在 :setting:`INSTALLED_APPS` 设置中放置 'django.contrib.admin.apps.SimpleAdminConfig' 而不是 'django.contrib.admin'


覆盖默认的管理站点

2.1 版中的新功能。


您可以通过将自定义 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.auth import views as auth_views

path(
    'admin/password_reset/',
    auth_views.PasswordResetView.as_view(),
    name='admin_password_reset',
),
path(
    'admin/password_reset/done/',
    auth_views.PasswordResetDoneView.as_view(),
    name='password_reset_done',
),
path(
    'reset/<uidb64>/<token>/',
    auth_views.PasswordResetConfirmView.as_view(),
    name='password_reset_confirm',
),
path(
    'reset/done/',
    auth_views.PasswordResetCompleteView.as_view(),
    name='password_reset_complete',
),

(这假设您已将管理员添加到 admin/ 并要求您将以 ^admin/ 开头的 URL 放在包含管理员应用程序本身的行之前)。

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


LogEntry 物体

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

LogEntry 属性

LogEntry.action_time
动作的日期和时间。
LogEntry.user
执行操作的用户(:setting:`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_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 注册,并且使用与 Site 实例名称对应的实例命名空间。

因此 - 如果您想在默认管理员中获取对特定 Choice 对象(来自民意调查应用程序)的更改视图的引用,您可以调用:

>>> 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 属性的对象,通常由当前模型的管理视图提供。


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):
    ...