Django 管理站点四
其他方法
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
方法不同,这五个方法实际上是被设计成作为 Django 视图从管理应用的 URL 调度处理程序中调用,以渲染处理模型实例 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 实例,允许你在渲染前轻松定制响应数据。
ModelAdmin
静态资源定义
有时你会想在添加/更改视图时添加一点 CSS 和/或 JavaScript。这可以通过在你的 ModelAdmin
上使用 Media
内类来实现:
class ArticleAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ["my_styles.css"],
}
js = ["my_code.js"]
staticfiles app 将 STATIC_URL (如果 STATIC_URL 是 None,则 MEDIA_URL)预先加入任何资产路径。同样的规则适用于:ref:表单上定义的静态资源 <form-asset-paths>。
jQuery
Django 管理 JavaScript 使用了 jQuery 库。
为了避免与用户提供的脚本或库冲突,Django 的 jQuery(版本3.7.1)被命名为 django.jQuery
。如果你想在自己的管理 JavaScript 中使用 jQuery,而不需要包含第二个副本,你可以在 changelist 和 add/edit 视图上使用 django.jQuery
对象。此外,依赖于 django.jQuery
的自己的管理表单或小部件在 声明表单媒体资产 时必须指定
js=['admin/js/jquery.init.js', …]
。
Changed in Django 5.0:
jQuery 已从 3.6.4 升级到 3.7.1。
ModelAdmin 类默认需要 jQuery,所以除非你有特殊需要,否则不需要将 jQuery 添加到你的 ModelAdmin
的媒体资源列表中。例如,如果你要求 jQuery 库在全局命名空间中(例如在使用第三方 jQuery 插件时),或者你需要一个较新版本的 jQuery,你将不得不包含自己的副本。
Django 提供了 jQuery 的非压缩和 “最小化” 版本,分别为 jquery.js
和 jquery.min.js
。
ModelAdmin 和 InlineModelAdmin有一个 media
属性,它返回一个 Media
对象的列表,其中存储了表单和/或表单集的 JavaScript 文件的路径。如果 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
的子类,它们是:
- TabularInline
- StackedInline
这两者之间的区别仅仅是用于呈现它们的模板。
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 表单编写自定义验证时,要谨慎编写依赖于父模型特征的验证。如果父模型未能验证,它可能会处于不一致的状态,如 验证 ModelForm 中的警告所述。
InlineModelAdmin.classes
A list or tuple containing extra CSS classes to apply to the fieldset that is rendered for the inlines. Defaults to None. As with classes configured in fieldsets, inlines with a collapse class will be initially collapsed using an expandable widget.
Changed in Django 5.1:
fieldsets
using the collapse
class now use <details>
and <summary>
elements, provided they define a name
.
InlineModelAdmin.extra
控制表单集在初始表格之外显示的额外表单数量,默认为 3 个。
对于使用支持 JavaScript 的浏览器的用户,提供了一个 “添加另一个” 的链接,以便在 extra
参数提供的内联行之外,再添加任意数量的内联行。
如果当前显示的表单数量超过 max_num
,或者用户没有启用 JavaScript,动态链接将不会出现。
InlineModelAdmin.get_extra() 还允许你自定义额外表格的数量。
InlineModelAdmin.max_num
这控制了内联中显示表单的最大数量。这并不直接与对象的数量相关,但如果该值足够小,则可以。更多信息请参见 model-formets-max-num。
InlineModelAdmin.get_max_num() 还允许你自定义额外表单的最大数量。
InlineModelAdmin.min_num
这控制了内联中要显示的表单的最少数量。
InlineModelAdmin.get_min_num() 还允许你自定义显示的最小表单数量。
InlineModelAdmin.raw_id_fields
默认情况下,Django 的管理员对 ForeignKey
的字段使用选择框接口(<select>)。有时候,你不想产生必须选择所有相关的实例来显示在下拉框中的开销。
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
对 verbose_name_plural 的重写,来自模型内部的 Meta 类。如果这个没有给出,而 InlineModelAdmin.verbose_name 被定义,Django 将使用 InlineModelAdmin.verbose_name + 's'。
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 实例,添加/更改视图,你可以使用由 admin 提供的 GenericTabularInline 或 GenericStackedInline (都是 GenericInlineModelAdmin 的子类)。它们分别为代表内联对象的表单实现了表格式和堆栈式的可视化布局,就像它们的非通用对应物一样。它们的行为就像其他内联一样。在你的 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)
覆盖管理模板
你可以覆盖许多模板,管理模块用于生成管理网站的各种页面。你甚至可以为特定的应用程序或特定的模型覆盖其中的一些模板。
设置你的项目管理模板目录
管理员模板文件位于 django/contrib/admin/templates/admin 目录中。
为了覆盖其中的一个或多个目录,首先在你的项目的 templates
目录下创建一个 admin
目录。这个目录可以是你在 DjangoTemplates
后台的 DIRS 选项中指定的任何一个目录。如果你自定义了 'loaders'
选项,请确保 'django.template.loaders.filesystem.Loader'
出现在 'django.template.loaders.app_directories.Loader'
之前,这样你的自定义模板就会被模板加载系统发现,而不是 django.contrib.admin 所包含的模板。
在这个 admin
目录中,创建以你的应用程序命名的子目录。在这些应用程序子目录中,创建以你的模型命名的子目录。注意,管理员应用程序在查找目录时,会将模型名称小写,所以如果你要在大小写敏感的文件系统上运行应用程序,请确保你将目录命名为所有小写。
要为特定的应用程序覆盖管理员模板,请从 django/contrib/admin/templates/admin 目录中复制并编辑模板,然后将其保存到刚刚创建的目录之一。
例如,如果我们想在名为 my_app
的应用程序中的所有模型的变更列表视图中添加一个工具,我们会将 contrib/admin/templates/admin/change_list.html
复制到我们项目的 templates/admin/my_app/
目录下,并进行任何必要的更改。
如果我们想只为名为 Page
的特定模型在变更列表视图中添加一个工具,我们会将同样的文件复制到项目的 templates/admin/my_app/page
目录下。
覆盖 vs. 替换管理模板
由于管理模板的模块化设计,通常没有必要也不建议更换整个模板。最好是只覆盖你需要更改的模板部分。
继续上面的例子,我们想在 History
工具旁边为 Page
模型添加一个新的链接。在看了 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
是用来呈现自定义包含标签的。这些可以被覆盖,但在这种情况下,你可能最好创建你自己版本的标签,并给它一个不同的名字。这样你就可以有选择地使用它。