Django 管理站点 — Django 文档
Django 管理站点
Django 最强大的部分之一是自动管理界面。 它从您的模型中读取元数据,以提供一个快速、以模型为中心的界面,受信任的用户可以在其中管理您网站上的内容。 管理员推荐的用途仅限于组织的内部管理工具。 它不是用来构建整个前端的。
管理员有许多用于自定义的钩子,但要小心尝试专门使用这些钩子。 如果您需要提供一个更加以流程为中心的接口来抽象出数据库表和字段的实现细节,那么可能是时候编写您自己的视图了。
在本文档中,我们将讨论如何激活、使用和自定义 Django 的管理界面。
概述
admin 在 :djadmin:`startproject` 使用的默认项目模板中启用。
如果您不使用默认项目模板,则有以下要求:
添加
'django.contrib.admin'
及其依赖项 - django.contrib.auth、django.contrib.contenttypes、django.contrib.messages 和 ]django.contrib.sessions - 到你的 :setting:`INSTALLED_APPS` 设置。配置一个 Django模板在你的后端 :设置:`模板` 设置与
django.template.context_processors.request
,django.contrib.auth.context_processors.auth
, 和django.contrib.messages.context_processors.messages
在里面'context_processors'
选项 :设置:`选项 ` .3.1 版更改:
django.template.context_processors.request
作为要求添加到'context_processors'
选项中,以支持新的 AdminSite.enable_nav_sidebar。如果您自定义了 :setting:`MIDDLEWARE` 设置,则 django.contrib.auth.middleware.AuthenticationMiddleware 和 django.contrib.messages.middleware.MessageMiddleware ] 必须包括在内。
完成这些步骤后,您将能够通过访问将其连接到的 URL 来使用管理站点(默认情况下为 /admin/
)。
如果您需要创建一个用户登录,请使用 :djadmin:`createsuperuser` 命令。 默认情况下,登录管理员要求用户将 is_staff 属性设置为 True
。
最后,确定您的应用程序的哪些模型应该在管理界面中是可编辑的。 对于这些模型中的每一个,按照 ModelAdmin 中的描述向管理员注册它们。
其他主题
ModelAdmin 物体
- class ModelAdmin
ModelAdmin
类是管理界面中模型的表示。 通常,它们存储在应用程序中名为admin.py
的文件中。 我们来看一个ModelAdmin
的例子:from django.contrib import admin from myapp.models import Author class AuthorAdmin(admin.ModelAdmin): pass admin.site.register(Author, AuthorAdmin)
你需要一个
ModelAdmin
对象吗?在前面的示例中,
ModelAdmin
类尚未定义任何自定义值。 因此,将提供默认的管理界面。 如果您对默认的管理界面感到满意,则根本不需要定义ModelAdmin
对象——您可以在不提供ModelAdmin
描述的情况下注册模型类。 前面的例子可以简化为:from django.contrib import admin from myapp.models import Author admin.site.register(Author)
register 装饰器
- register(*models, site=django.contrib.admin.sites.site)
还有一个用于注册
ModelAdmin
类的装饰器:from django.contrib import admin from .models import Author @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): pass
它被赋予一个或多个模型类以注册到
ModelAdmin
。 如果您使用自定义 AdminSite,请使用site
关键字参数传递它:from django.contrib import admin from .models import Author, Editor, Reader from myproject.admin_site import custom_admin_site @admin.register(Author, Reader, Editor, site=custom_admin_site) class PersonAdmin(admin.ModelAdmin): pass
如果您必须在其
__init__()
方法中引用模型管理类,则不能使用此装饰器,例如super(PersonAdmin, self).__init__(*args, **kwargs)
。 您可以使用super().__init__(*args, **kwargs)
。
发现管理文件
当您将 'django.contrib.admin'
放入 :setting:`INSTALLED_APPS` 设置中时,Django 会自动在每个应用程序中查找 admin
模块并将其导入。
- class apps.AdminConfig
- 这是管理员的默认 AppConfig 类。 它在 Django 启动时调用 autodiscover()。
- class apps.SimpleAdminConfig
- 此类的工作方式类似于 AdminConfig,只是它不调用 autodiscover()。
- default_site
- 默认管理站点类或返回站点实例的可调用对象的带点导入路径。 默认为
'django.contrib.admin.sites.AdminSite'
。 有关用法,请参阅 覆盖默认管理站点 。
- autodiscover()
此函数尝试在每个已安装的应用程序中导入
admin
模块。 此类模块应向管理员注册模型。通常你不需要直接调用这个函数,因为 AdminConfig 在 Django 启动时调用它。
如果您使用自定义 AdminSite
,通常将所有 ModelAdmin
子类导入您的代码并将它们注册到自定义 AdminSite
。 在这种情况下,为了禁用自动发现,您应该在 :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
设置为模型中DateField
或DateTimeField
的名称,更改列表页面将包含该字段的基于日期的向下钻取导航。例子:
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') @admin.display(empty_value='???') def view_birth_date(self, obj): return obj.birth_date
3.2 版本更改: display() 装饰器的
empty_value
参数相当于在前面直接设置显示函数上的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)
如果您想要一个仅包含
name
和title
字段的Author
模型的表单,您可以指定fields
或exclude
,如这个:from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): fields = ('name', 'title') class AuthorAdmin(admin.ModelAdmin): exclude = ('birth_date',)
由于 Author 模型只有三个字段,
name
、title
和birth_date
,上述声明产生的表单将包含完全相同的字段。
- ModelAdmin.fields
使用
fields
选项在“添加”和“更改”页面上的表单中进行简单的布局更改,例如仅显示可用字段的子集、修改其顺序或将它们分组到行中。 例如,您可以为 django.contrib.flatpages.models.FlatPage 模型定义一个更简单的管理表单版本,如下所示:class FlatPageAdmin(admin.ModelAdmin): fields = ('url', 'title', 'content')
在上面的例子中,只有字段
url
、title
和content
将按顺序显示在表单中。fields
可以包含在 ModelAdmin.readonly_fields 中定义的值以显示为只读。对于更复杂的布局需求,请参阅 fieldsets 选项。
fields
选项接受与 list_display 相同类型的值,但不接受可调用对象。 模型和模型管理方法的名称只有在 readonly_fields 中列出时才会使用。要在同一行显示多个字段,请将这些字段包装在它们自己的元组中。 在此示例中,
url
和title
字段将显示在同一行中,而content
字段将显示在它们下方的一行中:class FlatPageAdmin(admin.ModelAdmin): fields = (('url', 'title'), 'content')
注意
此
fields
选项不应与 fieldsets 选项内的fields
字典键混淆,如下一节所述。如果
fields
和 fieldsets 选项都不存在,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]]
如果
fieldsets
和 fields 选项都不存在,Django 将默认显示每个不是AutoField
并且有editable=True
的字段,在一个单一的字段集,其顺序与模型中定义的字段顺序相同。field_options
字典可以有以下键:fields
要在此字段集中显示的字段名称元组。 此密钥是必需的。
例子:
{ 'fields': ('first_name', 'last_name', 'address', 'city', 'state'), }
与 fields 选项一样,要在同一行显示多个字段,请将这些字段包装在它们自己的元组中。 在本例中,
first_name
和last_name
字段将显示在同一行:{ 'fields': (('first_name', 'last_name'), 'address', 'city', 'state'), }
fields
可以包含在 readonly_fields 中定义的值以显示为只读。如果将可调用的名称添加到
fields
,则适用与 fields 选项相同的规则:可调用必须列在 readonly_fields 中。
classes
包含要应用于字段集的额外 CSS 类的列表或元组。
例子:
{ 'classes': ('wide', 'extrapretty'), }
默认管理站点样式表定义的两个有用的类是
collapse
和wide
。 具有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
的验证。注意
如果您的
ModelForm
和ModelAdmin
都定义了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。警告
如果您想使用带有关系字段的自定义小部件(即 ForeignKey 或 ManyToManyField),请确保您没有在
raw_id_fields
、radio_fields
或autocomplete_fields
中包含该字段的名称。formfield_overrides
不会让您更改已设置raw_id_fields
、radio_fields
或autocomplete_fields
的关系字段上的小部件。 这是因为raw_id_fields
、radio_fields
和autocomplete_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
中可以使用四种类型的值。 除了最简单的之外,所有的都可以使用 display() 装饰器来自定义字段的显示方式:模型字段的名称。 例如:
class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name')
一个接受一个参数的可调用对象,模型实例。 例如:
@admin.display(description='Name') def upper_case_name(obj): return ("%s %s" % (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 ("%s %s" % (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): return '%d’s' % (self.birthday.year // 10 * 10) class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'decade_born_in')
关于
list_display
的几个特殊情况需要注意:如果字段是
ForeignKey
,Django 将显示相关对象的__str__()
。不支持
ManyToManyField
字段,因为这需要为表中的每一行执行单独的 SQL 语句。 尽管如此,如果您想这样做,请为您的模型提供一个自定义方法,并将该方法的名称添加到list_display
。 (有关list_display
中自定义方法的更多信息,请参见下文。)如果字段是
BooleanField
,Django 将显示一个漂亮的“是”、“否”或“未知”图标,而不是True
、False
或None
。如果给定的字符串是模型的方法,
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
参数。3.2 版本更改: display() 装饰器的
description
参数相当于在前面直接设置显示函数上的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') @admin.display(empty_value='unknown') def birth_date_view(self, obj): return obj.birth_date
3.2 版本更改: display() 装饰器的
empty_value
参数相当于在前面直接设置显示函数上的empty_value_display
属性版本。 为了向后兼容,仍然支持直接设置属性。如果给定的字符串是模型的方法,
ModelAdmin
或返回True
、False
或None
的可调用对象,Django 将显示一个漂亮的“如果您使用 display() 装饰器传递boolean
参数并将值设置为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() @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')
3.2 版本更改: display() 装饰器的
boolean
参数相当于在前面直接设置显示函数上的boolean
属性版本。 为了向后兼容,仍然支持直接设置属性。__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')
当尝试在管理中按
colored_first_name
排序时,上面将告诉 Django 按first_name
字段排序。要使用
ordering
参数表示降序,您可以在字段名称上使用连字符前缀。 使用上面的例子,这看起来像:@admin.display(ordering='-first_name')
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
3.2 版本更改: display() 装饰器的
ordering
参数相当于在前面直接设置显示函数上的admin_order_field
属性版本。 为了向后兼容,仍然支持直接设置属性。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', ) def full_name(self): return self.first_name + ' ' + self.last_name class PersonAdmin(admin.ModelAdmin): list_display = ('full_name',)
请注意,
@property
必须高于@display
。 如果您使用旧方法——直接设置与显示相关的属性而不是使用 display() 装饰器——请注意property()
函数和 not必须使用@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' full_name = property(my_property)
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_name
和last_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_editable
和list_display_links
中不能同时列出同一个字段——一个字段不能既是表单又是链接。
如果这些规则中的任何一个被破坏,您都会收到验证错误。
- ModelAdmin.list_filter
设置
list_filter
以激活管理员更改列表页面右侧边栏中的过滤器,如下图所示:[[../File:../../../_images/list_filter|../../../_images/list_filter.png]]
list_filter
应该是元素的列表或元组,其中每个元素应该是以下类型之一:字段名称,其中指定字段应为
BooleanField
、CharField
、DateField
、DateTimeField
、IntegerField
、ForeignKey
或ManyToManyField
,例如:class PersonAdmin(admin.ModelAdmin): list_filter = ('is_staff', 'company')
list_filter
中的字段名称也可以使用__
查找跨越关系,例如:class PersonAdmin(admin.UserAdmin): list_filter = ('company__name',)
一个继承自
django.contrib.admin.SimpleListFilter
的类,您需要提供title
和parameter_name
属性并覆盖lookups
和queryset
方法,例如: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
对象传递给lookups
和queryset
方法,例如: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), )
假设
author
是ForeignKey
到User
模型,这会将list_filter
选择限制为写过一本书的用户,而不是列出所有用户。您可以使用
EmptyFieldListFilter
过滤空值,它可以过滤空字符串和空值,具体取决于字段允许存储的内容:class BookAdmin(admin.ModelAdmin): list_filter = ( ('title', admin.EmptyFieldListFilter), )
注意
FieldListFilter
API 被认为是内部的,可能会更改。注意
不支持 GenericForeignKey 字段。
3.1 新功能: 添加了
EmptyFieldListFilter
类。
列表过滤器通常仅在过滤器有多个选择时才会出现。 过滤器的
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
字段排序,则更改列表按name
和pk
排序。 如果您有很多行并且在name
和pk
上没有索引,这可能会表现不佳。
- ModelAdmin.paginator
- 用于分页的分页器类。 默认情况下,使用 django.core.paginator.Paginator。 如果自定义分页器类没有与 django.core.paginator.Paginator 相同的构造函数接口,您还需要提供 ModelAdmin.get_paginator() 的实现。
- ModelAdmin.prepopulated_fields
将
prepopulated_fields
设置为字典,将字段名称映射到应该从中预填充的字段:class ArticleAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("title",)}
设置后,给定的字段将使用一些 JavaScript 从分配的字段中填充。 此功能的主要用途是从一个或多个其他字段自动生成
SlugField
字段的值。 生成的值是通过连接源字段的值产生的,然后将该结果转换为有效的 slug(例如 用破折号代替空格和小写 ASCII 字母)。保存值后,JavaScript 不会修改预填充字段。 通常不希望 slug 发生变化(如果在其中使用了 slug,这将导致对象的 URL 发生变化)。
prepopulated_fields
不接受DateTimeField
、ForeignKey
、OneToOneField
和ManyToManyField
字段。3.2 版本变化: 在旧版本中,从生成的值中删除了各种英文停用词。
- ModelAdmin.preserve_filters
- 默认情况下,在创建、编辑或删除对象后,应用的过滤器会保留在列表视图中。 您可以通过将此属性设置为
False
来清除过滤器。
- ModelAdmin.radio_fields
默认情况下,Django 的管理员使用选择框界面( ) 对于字段
ForeignKey
或有choices
放。 如果radio_fields
中存在字段,Django 将使用单选按钮界面。 假设group
是Person
型号上的ForeignKey
:class PersonAdmin(admin.ModelAdmin): radio_fields = {"group": admin.VERTICAL}
您可以选择使用
django.contrib.admin
模块中的HORIZONTAL
或VERTICAL
。不要在
radio_fields
中包含字段,除非它是ForeignKey
或设置了choices
。
- ModelAdmin.autocomplete_fields
autocomplete_fields
是您想要更改为 Select2 自动完成输入的ForeignKey
和/或ManyToManyField
字段的列表。默认情况下,管理员对这些字段使用选择框界面 (
<select>
)。 有时您不想承担选择所有相关实例以显示在下拉列表中的开销。Select2 输入看起来类似于默认输入,但带有异步加载选项的搜索功能。 如果相关模型有很多实例,这会更快、更用户友好。
您必须在相关对象的
ModelAdmin
上定义 search_fields,因为自动完成搜索使用它。为避免未经授权的数据泄露,用户必须拥有相关对象的
view
或change
权限才能使用自动完成功能。结果的排序和分页由相关的
ModelAdmin
的 get_ordering() 和 get_paginator() 方法控制。在以下示例中,
ChoiceAdmin
有一个自动完成字段,用于ForeignKey
到Question
。 结果按question_text
字段过滤并按date_created
字段排序:class QuestionAdmin(admin.ModelAdmin): ordering = ['date_created'] search_fields = ['question_text'] class ChoiceAdmin(admin.ModelAdmin): autocomplete_fields = ['question']
大型数据集的性能注意事项
使用 ModelAdmin.ordering 排序可能会导致性能问题,因为对大型查询集进行排序会很慢。
此外,如果您的搜索字段包含未被数据库索引的字段,您可能会遇到在超大表上性能不佳的情况。
对于这些情况,最好使用全文索引搜索编写自己的 ModelAdmin.get_search_results() 实现。
您可能还想在非常大的表上更改
Paginator
,因为默认分页器始终执行count()
查询。 例如,您可以覆盖Paginator.count
属性的默认实现。
- ModelAdmin.raw_id_fields
默认情况下,Django 的管理员使用选择框界面( ) 对于字段
ForeignKey
. 有时您不想承担必须选择所有相关实例以显示在下拉列表中的开销。raw_id_fields
是您想要更改为ForeignKey
或ManyToManyField
的Input
小部件的字段列表: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
默认情况下,管理员将所有字段显示为可编辑。 此选项中的任何字段(应为
list
或tuple
)将按原样显示其数据且不可编辑; 它们也被排除在用于创建和编辑的 ModelForm 之外。 请注意,在指定 ModelAdmin.fields 或 ModelAdmin.fieldsets 时,必须存在只读字段才能显示(否则将被忽略)。如果使用
readonly_fields
而没有通过 ModelAdmin.fields 或 ModelAdmin.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',) # 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
以在管理更改表单上启用“另存为新”功能。通常,对象具有三个保存选项:“保存”、“保存并继续编辑”和“保存并添加另一个”。 如果
save_as
是True
,“保存并添加另一个”将替换为“另存为新”按钮,该按钮创建一个新对象(具有新 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
以启用管理员更改列表页面上的搜索框。 这应该设置为一个字段名称列表,只要有人在该文本框中提交搜索查询,就会搜索该列表。这些字段应该是某种文本字段,例如
CharField
或TextField
。 您还可以使用查找 API “跟随”符号对ForeignKey
或ManyToManyField
执行相关查找:search_fields = ['foreign_key__related_fieldname']
例如,如果您有一个包含作者的博客条目,则以下定义将启用按作者的电子邮件地址搜索博客条目:
search_fields = ['user__email']
当有人在管理搜索框中进行搜索时,Django 将搜索查询拆分为单词并返回包含每个单词的所有对象,不区分大小写(使用 :lookup:`icontains` 查找),其中每个单词必须至少位于
search_fields
之一中。 例如,如果search_fields
设置为['first_name', 'last_name']
并且用户搜索john lennon
,Django 将执行此 SQLWHERE
子句的等效操作: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 将执行与此 SQLWHERE
子句等效的操作:WHERE (first_name ILIKE '%john winston%' OR last_name ILIKE '%john winston%')
如果您不想使用
icontains
作为查找,您可以通过将其附加到字段来使用任何查找。 例如,您可以通过将search_fields
设置为['first_name__exact']
来使用 :lookup:`exact`。一些(较旧的)用于指定字段查找的快捷方式也可用。 您可以在
search_fields
中的字段前添加以下字符,相当于在该字段中添加__<lookup>
:前缀
查找
^
=
@
无
如果您需要自定义搜索,您可以使用 ModelAdmin.get_search_results() 来提供额外或替代的搜索行为。
3.2 版更改: 支持搜索带空格的引用短语。
- 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
、tuple
或set
) 您想要排序的 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)
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
作为参数,并预期返回list
或tuple
以进行类似于 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
。例如,要按
name
和age
进行搜索,您可以使用: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')
。
- ModelAdmin.save_related(request, form, formsets, change)
save_related
方法被赋予HttpRequest
、父对象ModelForm
实例、内联表单集列表和基于父对象是否被添加或更改的布尔值。 在这里,您可以对与父项相关的对象执行任何保存前或保存后操作。 请注意,此时父对象及其表单已经保存。
- ModelAdmin.get_autocomplete_fields(request)
get_autocomplete_fields()
方法被赋予HttpRequest
并预期返回字段名称的list
或tuple
,这些字段名称将与上述自动完成小部件一起显示在 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
并预期返回字段名称的list
或tuple
,这些字段名称将显示在如上所述的更改列表视图中在 ModelAdmin.list_display 部分。
- ModelAdmin.get_list_display_links(request, list_display)
get_list_display_links
方法由 ModelAdmin.get_list_display() 返回HttpRequest
和list
或tuple
。 如 ModelAdmin 中所述,预计将返回更改列表中将链接到更改视图的字段名称的None
或list
或tuple
.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
),并预期返回两个列表 -元组,其中每个二元组代表管理表单页面上的<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)
get_sortable_by()
方法传递了HttpRequest
并期望返回一个集合(例如list
、tuple
或set
)的字段名称将在更改列表页面中排序。如果设置了它,它的默认实现返回 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_inlines(request, obj)
get_inlines
方法被赋予正在编辑的HttpRequest
和obj
(或添加表单上的None
),并且预计会返回一个可迭代的内联。 您可以覆盖此方法以根据请求或模型实例动态添加内联,而不是在 ModelAdmin.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,由 fields 和 exclude 等属性修改。 因此,例如,如果您想为超级用户提供额外的字段,您可以像这样交换不同的基本表单:
class MyModelAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): if request.user.is_superuser: kwargs['form'] = MySuperuserForm return super().get_form(request, obj, **kwargs)
你也可以直接返回一个自定义的 ModelForm 类。
- ModelAdmin.get_formsets_with_inlines(request, obj=None)
用于管理添加和更改视图的产量 (
FormSet
, InlineModelAdmin) 对。例如,如果您只想在更改视图中显示特定的内联,您可以按如下方式覆盖
get_formsets_with_inlines
:class MyModelAdmin(admin.ModelAdmin): inlines = [MyInline, SomeOtherInline] def get_formsets_with_inlines(self, request, obj=None): for inline in self.get_inline_instances(request, obj): # hide MyInline in the add view if not isinstance(inline, MyInline) or obj is not None: yield inline.get_formset(request, obj), inline
- ModelAdmin.formfield_for_foreignkey(db_field, request, **kwargs)
ModelAdmin
上的formfield_for_foreignkey
方法允许您覆盖外键字段的默认表单域。 例如,要根据用户返回此外键字段的对象子集:class MyModelAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "car": kwargs["queryset"] = Car.objects.filter(owner=request.user) return super().formfield_for_foreignkey(db_field, request, **kwargs)
这使用
HttpRequest
实例过滤Car
外键字段以仅显示User
实例拥有的汽车。对于更复杂的过滤器,您可以使用
ModelForm.__init__()
方法根据模型的instance
进行过滤(请参阅 处理关系的字段 )。 例如:class CountryAdminForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['capital'].queryset = self.instance.cities.all() class CountryAdmin(admin.ModelAdmin): form = CountryAdminForm
- ModelAdmin.formfield_for_manytomany(db_field, request, **kwargs)
与
formfield_for_foreignkey
方法一样,可以覆盖formfield_for_manytomany
方法以更改多对多字段的默认表单字段。 例如,如果一个车主可以拥有多辆汽车,而汽车可以属于多个车主——多对多关系——您可以过滤Car
外键字段以仅显示User
拥有的汽车]:class MyModelAdmin(admin.ModelAdmin): def formfield_for_manytomany(self, db_field, request, **kwargs): if db_field.name == "cars": kwargs["queryset"] = Car.objects.filter(owner=request.user) return super().formfield_for_manytomany(db_field, request, **kwargs)
- ModelAdmin.formfield_for_choice_field(db_field, request, **kwargs)
与
formfield_for_foreignkey
和formfield_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
,则会引发DisallowedModelAdminLookup
(SuspiciousOperation 的子类)。默认情况下,
lookup_allowed()
允许访问模型的本地字段、list_filter 中使用的字段路径(但不是来自 get_list_filter() 的路径)以及 所需的查找]limit_choices_to 在 raw_id_fields 中正常运行。覆盖此方法以自定义您的 ModelAdmin 子类允许的查找。
- ModelAdmin.has_view_permission(request, obj=None)
如果允许查看
obj
,则应返回True
,否则返回False
。 如果 obj 是None
,则应返回True
或False
以指示通常是否允许查看此类型的对象(例如,False
将被解释)表示不允许当前用户查看任何此类对象)。如果用户具有“更改”或“查看”权限,则默认实现返回
True
。
- ModelAdmin.has_add_permission(request)
- 如果允许添加对象,则应返回
True
,否则返回False
。
- ModelAdmin.has_change_permission(request, obj=None)
- 如果允许编辑
obj
,则应返回True
,否则返回False
。 如果obj
为None
,则应返回True
或False
以指示通常是否允许编辑此类型的对象(例如,[ X151X] 将被解释为表示不允许当前用户编辑任何此类对象)。
- ModelAdmin.has_delete_permission(request, obj=None)
- 如果允许删除
obj
,则应返回True
,否则返回False
。 如果obj
为None
,则应返回True
或False
以指示通常是否允许删除此类型的对象(例如,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)
用于自定义 delete_view() 和“删除选定”action 的删除过程的钩子。
objs
参数是要删除的对象(QuerySet
或模型实例列表)的同构迭代,request
是 HttpRequest。此方法必须返回
(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.5.1)命名空间为 django.jQuery
。 如果您想在您自己的管理 JavaScript 中使用 jQuery 而无需包含第二个副本,您可以在更改列表中使用 django.jQuery
对象并添加/编辑视图。 此外,当 声明表单媒体资产 时,您自己的管理表单或小部件取决于 django.jQuery
必须指定 js=['admin/js/jquery.init.js', …]
。
3.1 版本变化:jQuery 从 3.4.1 升级到 3.5.1。
ModelAdmin 类默认需要 jQuery,因此除非您有特定需求,否则无需将 jQuery 添加到 ModelAdmin
的媒体资源列表中。 例如,如果您需要 jQuery 库位于全局命名空间中(例如,当使用第三方 jQuery 插件时)或者如果您需要更新版本的 jQuery,您将必须包含您自己的副本。
Django 提供未压缩和“缩小”版本的 jQuery,分别为 jquery.js
和 jquery.min.js
。
ModelAdmin 和 InlineModelAdmin 有一个 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 选项
InlineModelAdmin
与 ModelAdmin
共享许多相同的特性,并添加了一些自己的特性(共享特性实际上是在 BaseModelAdmin
超类中定义的)。 共有的特点是:
form
fieldsets
fields
formfield_overrides
exclude
filter_horizontal
filter_vertical
ordering
prepopulated_fields
get_fieldsets()
get_queryset()
radio_fields
readonly_fields
raw_id_fields
formfield_for_choice_field()
formfield_for_foreignkey()
formfield_for_manytomany()
has_module_permission()
InlineModelAdmin
类添加或自定义:
- InlineModelAdmin.model
- 内联正在使用的模型。 这是必需的。
- InlineModelAdmin.fk_name
- 模型上的外键名称。 在大多数情况下,这会自动处理,但如果同一个父模型有多个外键,则必须明确指定
fk_name
。
- InlineModelAdmin.formset
- 默认为 BaseInlineFormSet。 使用您自己的表单集可以为您提供许多自定义的可能性。 内联是围绕 模型表单集 构建的。
- InlineModelAdmin.form
form
的值默认为ModelForm
。 这是在为此内联创建表单集时传递给 inlineformset_factory() 的内容。
- InlineModelAdmin.classes
- 一个列表或元组,包含额外的 CSS 类,以应用于为内联呈现的字段集。 默认为
None
。 与在 fieldsets 中配置的类一样,带有collapse
类的内联最初将被折叠,它们的标题将有一个小的“显示”链接。
- InlineModelAdmin.extra
这控制除了初始表单之外表单集将显示的额外表单的数量。 默认为 3。 有关更多信息,请参阅 表单集文档 。
对于使用支持 JavaScript 的浏览器的用户,提供了一个“添加另一个”链接,以允许添加任意数量的额外内联,除了作为
extra
参数的结果提供的内联。如果当前显示的表单数量超过
max_num
,或者用户没有启用 JavaScript,则不会出现动态链接。InlineModelAdmin.get_extra() 还允许您自定义额外表单的数量。
- InlineModelAdmin.max_num
这控制了内联中显示的最大表单数。 这与对象的数量没有直接关系,但如果该值足够小,则可以。 有关详细信息,请参阅 限制可编辑对象的数量 。
InlineModelAdmin.get_max_num() 还允许您自定义额外表单的最大数量。
- InlineModelAdmin.min_num
这控制了内联显示的最小表单数。 有关更多信息,请参阅 modelformset_factory()。
InlineModelAdmin.get_min_num() 还允许您自定义显示表单的最小数量。
- InlineModelAdmin.raw_id_fields
默认情况下,Django 的管理员使用选择框界面( ) 对于字段
ForeignKey
. 有时您不想承担必须选择所有相关实例以显示在下拉列表中的开销。raw_id_fields
是您想要更改为ForeignKey
或ManyToManyField
的Input
小部件的字段列表: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
,否则返回False
。obj
是正在编辑的父对象或添加新父对象时的None
。
- InlineModelAdmin.has_change_permission(request, obj=None)
- 如果允许编辑内联对象,则应返回
True
,否则返回False
。obj
是正在编辑的父对象。
- InlineModelAdmin.has_delete_permission(request, obj=None)
- 如果允许删除内联对象,则应返回
True
,否则返回False
。obj
是正在编辑的父对象。
注意
传递给 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.through
。 through
属性是对管理多对多关系的模型的引用。 当您定义多对多字段时,该模型由 Django 自动创建。
其次,GroupAdmin
必须手动排除members
字段。 Django 为模型上的多对多字段显示一个管理小部件,用于定义关系(在本例中为 Group
)。 如果你想使用内联模型来表示多对多关系,你必须告诉 Django 的管理员 不 显示这个小部件 - 否则你最终会在你的管理页面上有两个小部件来管理关系。
请注意,使用此技术时,不会触发 m2m_changed 信号。 这是因为就管理员而言,through
只是一个具有两个外键字段的模型,而不是多对多关系。
在所有其他方面,InlineModelAdmin
与任何其他产品完全相同。 您可以使用任何正常的 ModelAdmin
属性自定义外观。
使用多对多中介模型
当您使用 ManyToManyField 的 through
参数指定中间模型时,默认情况下管理员不会显示小部件。 这是因为该中间模型的每个实例需要的信息多于单个小部件所能显示的信息,并且多个小部件所需的布局将因中间模型而异。
但是,我们仍然希望能够在线编辑该信息。 幸运的是,我们可以使用内联管理模型来做到这一点。 假设我们有以下模型:
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
类可用的任何选项进行自定义。
现在为 Person
和 Group
模型创建管理视图:
class PersonAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
最后,在管理站点注册您的 Person
和 Group
模型:
admin.site.register(Person, PersonAdmin)
admin.site.register(Group, GroupAdmin)
现在,您的管理站点已设置为从 Person
或 Group
详细信息页面内联编辑 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
实例,添加/更改视图,您可以使用 GenericTabularInline 或 GenericStackedInline(两个子类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 文档 。
覆盖管理模板
您可以覆盖管理模块用于生成管理站点的各种页面的许多模板。 您甚至可以为特定应用程序或特定模型覆盖其中的一些模板。
设置您的项目管理模板目录
管理模板文件位于 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">{% 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_template 、 AdminSite。 login_template 或 AdminSite.logout_template 属性。
主题支持
3.2 版中的新功能。
管理员使用 CSS 变量来定义颜色。 这允许更改颜色而不必覆盖许多单独的 CSS 规则。 例如,如果您更喜欢紫色而不是蓝色,则可以向项目添加 admin/base.html
模板覆盖:
{% extends 'admin/base.html' %}
{% block extrastyle %}{{ block.super }}
<style>
:root {
--primary: #9774d5;
--secondary: #785cab;
--link-fg: #7c449b;
--link-selected-fg: #8f5bb2;
}
</style>
{% endblock %}
定义了一个深色主题,并根据 prefers-color-scheme 媒体查询进行应用。
CSS 变量列表在 django/contrib/admin/static/admin/css/base.css
中定义。
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
属性,还可以基于每个ModelAdmin
和ModelAdmin
内的自定义字段覆盖该值。 有关示例,请参阅 ModelAdmin.empty_value_display。
- AdminSite.enable_nav_sidebar
3.1 版中的新功能。
一个布尔值,用于确定是否在较大屏幕上显示导航侧边栏。 默认设置为
True
。
- AdminSite.final_catch_all_view
3.2 版中的新功能。
一个布尔值,用于确定是否向管理员添加最终的全能视图,将未经身份验证的用户重定向到登录页面。 默认设置为
True
。警告
不建议将此设置为
False
,因为该视图可防止潜在的模型枚举隐私问题。
- AdminSite.login_template
- 管理站点登录视图将使用的自定义模板的路径。
- AdminSite.login_form
- 管理站点登录视图将使用的 AuthenticationForm 的子类。
- AdminSite.logout_template
- 管理站点注销视图将使用的自定义模板的路径。
- AdminSite.password_change_template
- 管理站点密码更改视图将使用的自定义模板的路径。
- AdminSite.password_change_done_template
- 管理站点密码更改完成视图将使用的自定义模板的路径。
AdminSite 方法
- AdminSite.each_context(request)
返回一个变量字典,以放置在管理站点中每个页面的模板上下文中。
默认情况下包括以下变量和值:
site_header
:AdminSite.site_headersite_title
:AdminSite.site_titlesite_url
:AdminSite.site_urlhas_permission
:AdminSite.has_permission()available_apps
:当前用户可用的 应用程序注册表 中的应用程序列表。 列表中的每个条目都是一个字典,代表具有以下键的应用程序:app_label
:应用标签app_url
:admin中应用索引的URLhas_module_perms
:一个布尔值,指示当前用户是否允许显示和访问模块的索引页面models
:应用程序中可用的模型列表
每个模型都是一个具有以下键的字典:
object_name
:模型的类名name
:型号的复数名称perms
:dict
跟踪add
、change
、delete
和view
权限admin_url
:模型的管理员更改列表 URLadd_url
:添加新模型实例的管理 URL
- AdminSite.has_permission(request)
- 如果给定
HttpRequest
的用户有权查看管理站点中的至少一个页面,则返回True
。 默认要求 User.is_active 和 User.is_staff 为True
。
- AdminSite.register(model_or_iterable, admin_class=None, **options)
使用给定的
admin_class
注册给定的模型类(或可迭代的类)。admin_class
默认为 ModelAdmin(默认管理选项)。 如果给出关键字参数——例如list_display
– 它们将作为选项应用于管理类。如果模型是抽象的,则引发 ImproperlyConfigured。 和
django.contrib.admin.sites.AlreadyRegistered
如果模型已经注册。
- AdminSite.unregister(model_or_iterable)
取消注册给定的模型类(或可迭代的类)。
如果模型尚未注册,则引发
django.contrib.admin.sites.NotRegistered
。
将 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 子类。
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)
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'
。
覆盖默认管理站点
您可以通过将自定义 AppConfig
的 default_site 属性设置为 AdminSite
子类或可调用的带点导入路径来覆盖默认 django.contrib.admin.site
返回一个站点实例。
from django.contrib import admin
class MyAdminSite(admin.AdminSite):
...
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):
default_site = 'myproject.admin.MyAdminSite'
INSTALLED_APPS = [
...
'myproject.apps.MyAdminConfig', # replaces 'django.contrib.admin'
...
]
同一 URLconf 中的多个管理站点
您可以在同一个由 Django 驱动的网站上创建管理站点的多个实例。 创建 AdminSite
的多个实例并将每个实例放置在不同的 URL 中。
在此示例中,URL /basic-admin/
和 /advanced-admin/
具有管理站点的不同版本 – 使用 AdminSite
实例 myproject.admin.basic_site
和 myproject.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
记录的操作类型:
ADDITION
、CHANGE
、DELETION
。例如,要获取通过管理员完成的所有添加的列表:
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 模式:
页面 | 网址名称 | 参数 |
---|---|---|
索引 | index
|
|
登录 | login
|
|
登出 | logout
|
|
密码更改 | password_change
|
|
密码修改完成 | password_change_done
|
|
i18n JavaScript | jsi18n
|
|
申请索引页面 | app_list
|
app_label
|
重定向到对象的页面 | view_on_site
|
content_type_id 、object_id
|
每个 ModelAdmin 实例提供一组额外的命名 URL:
页面 | 网址名称 | 参数 |
---|---|---|
更改列表 | 模板: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:
页面 | 网址名称 | 参数 |
---|---|---|
密码更改 | 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_label
和 model_name
属性的对象,通常由当前模型的管理视图提供。
display 装饰器
- display(*, boolean=None, ordering=None, description=None, empty_value=None)
3.2 版中的新功能。
此装饰器可用于设置可与 list_display 或 readonly_fields 一起使用的自定义显示函数的特定属性:
@admin.display( boolean=True, ordering='-publish_date', description='Is Published?', ) def is_published(self, obj): return obj.publish_date is not None
这相当于直接在函数上设置一些属性(使用原始的、更长的名称):
def is_published(self, obj): return obj.publish_date is not None is_published.boolean = True is_published.admin_order_field = '-publish_date' is_published.short_description = 'Is Published?'
另请注意,
empty_value
装饰器参数映射到直接分配给函数的empty_value_display
属性。 它不能与boolean
结合使用——它们是互斥的。使用此装饰器不是强制创建显示函数,但使用它而不带参数作为源代码中的标记来标识函数的用途会很有用:
@admin.display def published_year(self, obj): return obj.publish_date.year
在这种情况下,它不会向函数添加任何属性。
staff_member_required 装饰器
- staff_member_required(redirect_field_name='next', login_url='admin:login')
此装饰器用于需要授权的管理视图。 使用此函数修饰的视图将具有以下行为:
如果用户已登录、是工作人员 (
User.is_staff=True
) 且处于活动状态 (User.is_active=True
),则正常执行视图。否则,请求将被重定向到
login_url
参数指定的 URL,原始请求的路径位于redirect_field_name
指定的查询字符串变量中。 例如:/admin/login/?next=/admin/polls/question/3/
。
用法示例:
from django.contrib.admin.views.decorators import staff_member_required @staff_member_required def my_view(request): ...