Burgesszheng 2020-02-16
该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)
使用装饰器的形式:
from django.contrib import admin from myapp.models import User @admin.register(User, ) class UserAdmin(admin.ModelAdmin): pass
ModelAdmin
是非常灵活的。它具有几个用于定制界面的选项。所有选项都在ModelAdmin
子类上定义:
作用:用于批量操作, django默认只有批量删除操作,如果想要新增使用actions代码如下:
from django.contrib import admin from myapp.models import User class UserAdmin(admin.ModelAdmin): actions = ['make_published'] def make_published(self, request, queryset): queryset.update(status='p') make_published.short_description = "Mark selected stories as published" admin.site.register(User, UserAdmin)
作用:控制操作栏在页面上的显示位置。默认情况下,管理员更改列表在页面的顶部显示操作。
# 顶部显示 class UserAdmin(admin.ModelAdmin): actions_on_top = True actions_on_bottom = False # 底部显示 class UserAdmin(admin.ModelAdmin): actions_on_top = False actions_on_bottom = True
作用: 控制是否在操作下拉菜单旁边显示选择计数器。默认情况下显示它
class UserAdmin(admin.ModelAdmin): actions_selection_counter = False # 不显示
作用: 设置date_hierarchy
为 模型中的DateFiel
或 DateTimeField
名称 。效果如图
date_hierarchy = 'pub_date' # 也可以通过'__'跨表取 date_hierarchy = 'author__pub_date'
作用: 此属性将在现实页面覆盖记录字段为空(None
,空字符串等)的默认显示值。默认值为-
(破折号)
empty_value_display = '?????????'
指定覆盖某个字段
from django.contrib import admin class AuthorAdmin(admin.ModelAdmin): fields = ('name', 'title', 'view_birth_date') def view_birth_date(self, obj): # 自定义的字段,obj为当前model对象 return obj.birth_date view_birth_date.empty_value_display = '???' # 指定覆盖该字段空值
作用: 如果提供此属性,则应为要从表单中排除的字段名称列表。
class AuthorAdmin(admin.ModelAdmin): exclude = ['age'] # 不能和fields同用
作用:详细页面需要显示的字段列表
class AuthorAdmin(admin.ModelAdmin): exclude = ['name']
分组显示,分组中的字段在一行显示
class FlatPageAdmin(admin.ModelAdmin): fields = (('url', 'title'), 'content')
作用: 设置fieldsets
以控制管理员“添加”和“更改”页面的布局 。
fieldsets
是由一个二元的元组组成,每个元组中都有两个参数,((name, field_options), )。其中,name是代表字段集标题的字符串,如果不设置名字则填None, field_options是有关字段集的信息词典,包括要在其中显示的字段的列表。
示例如下:
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'), }), )
该field_options
词典可以具有以下键:
fields
:要在此字段集中显示的字段名称的元组。此键值是必需的。
classes
: 包含要应用于该字段集的额外CSS类的列表或元组。
举例说明:
{ 'classes': ('wide', 'extrapretty'), }
默认管理网站样式表定义的两个有用的类是 collapse
和wide
。
collapse
样式的字段集将首先在管理员中折叠,需要设置一个字段集的标题,并替换为一个小的“单击以展开”链接(Show/Hide)。
wide
样式的字段集将获得额外的水平空间,即字段名和值之间空间会增大。
description
: 一串可选的额外文本,将显示在每个字段集顶部。
作用:只针对于多对多显示样式的修改
默认状态:
应用filter_horizontal之后:
class UserAdmin(admin.ModelAdmin): filter_horizontal = ('roles', ) # roles是多对多字段
作用:只针对于多对多显示样式的修改
应用filter_vertical之后:
class UserAdmin(admin.ModelAdmin): filter_vertical = ('roles', ) # roles是多对多字段
作用:自定义创建form
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和ModelForm均定义exclude,优先ModelAdmin,即name仍会显示
作用: 这个属性比较难以理解,通过一个列子来解释可能会更好一点。设想一下我们自己写了个RichTextEditorWidget
(富文本控件),然后想用它来代替传统的``(文本域控件)用于输入大段文字。我们可以这么做:
from django.db import models from django.contrib import admin # 从对应的目录导入我们先前写好的widget和model from myapp.widgets import RichTextEditorWidget from myapp.models import MyModel class MyModelAdmin(admin.ModelAdmin): formfield_overrides = { models.TextField: {'widget': RichTextEditorWidget}, }
注意在上面的外层字典中的键是一个实际的字段类,而不是字符串,对应的值又是一个字典;这些参数将被传递给form字段的__init__()
方法。
警告:如果你想使用一个带有关系字段的自定义widget。请确保你没有在raw_id_fields
或radio_field
s之中include那些字段的名字。
作用: 控制在管理员的更改列表页面上显示哪些字段 。
class UserAdmin(admin.ModelAdmin): list_display = ('name', 'age', 'roles', )
还可显示自定义的字段信息
class UserAdmin(admin.ModelAdmin): list_display = ('upper_case_name', ) def upper_case_name(self, obj): return ("%s %s" % (obj.first_name, obj.last_name)).upper() # short_description functions like a model field's verbose_name upper_case_name.short_description = 'Name' # 为新字段的显示名字
作用:可点击进入详情页面的字段,None为没有链接。但是直接访问url依然可以进入详情页。
class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'birthday') list_display_links = ('first_name', 'last_name')
作用: 该列表将允许在展示列表页面上进行编辑。
class UserAdmin(admin.ModelAdmin): list_display = ('name', 'age', 'roles', ) list_editable = ('age', ) # list_editable中的字段必须在list_display中
作用: 管理员更改列表页面右侧栏中的过滤器
class PersonAdmin(admin.ModelAdmin): list_filter = ('name', 'age')
list_filter
也可以使用__
查找来跨越关系
class PersonAdmin(admin.ModelAdmin): list_filter = ('name', 'age', 'company__name')
作用: 用于分页的分页器类。默认情况下, 使用 django.core.paginator.Paginator
class UserAdmin(admin.ModelAdmin): paginator = 自定义的分页类 # 自定义分页器,还需要实现一个get_paginator()
作用: 设置一个值,仅当总结果计数小于或等于此设置时,管理员才会在更改列表上显示“ show all ”链接。默认情况下,此设置为200
class PersonAdmin(admin.ModelAdmin): list_max_show_all = 10
作用:每页显示多少条数据。默认100
class PersonAdmin(admin.ModelAdmin): list_per_page = 10
作用: 设置list_select_related
为告诉Django在管理更改列表页面上检索对象列表时使用 select_related()
class ArticleAdmin(admin.ModelAdmin): list_select_related = ('author', 'category')
作用: 在Django管理视图中对对象列表进行排序
class UserAdmin(admin.ModelAdmin): ordering = ('-id', ) # 加‘-’号,按照降序排列
作用: 详细页面,删除、修改,更新后跳转回列表后,是否保留原搜索条件 。
class UserAdmin(admin.ModelAdmin): preserve_filters = False
作用: Django默认将下拉选择框界面用于ForeignKey
或者choices
字段。如果radio_fields
中存在字段,则Django将改为使用单选按钮界面 。
class UserAdmin(admin.ModelAdmin): radio_fields = {'gender': admin.HORIZONTAL} # 您可以选择使用HORIZONTAL还是VERTICAL从 django.contrib.admin模块中选择。 # HORIZONTAL横向排列 # VERTICAL竖向排列
作用:针对多对多和一对多关系, 会将外键数据全部加载出来,渲染到select表中,如果数据量比较大,就会很慢,使用autocomplete_fields只会展示几条数据,在最上侧会出现一个搜索框,可以使用搜索框查询未显示的数据。既然有搜索,那么就需要定义搜索属性search_fields,示例如下
class UserAdmin(admin.ModelAdmin): autocomplete_fields = ('company', ) # 外键 class CompanyAdmin(admin.ModelAdmin): search_fields = ('title', ) # 对应的表数据能够通过某一个字段或者多个字段进行搜索
作用:默认情况下,Django 的admin对"外键"字段使用select选择框界面,有时不希望在下拉列表中选择要显示的所有相关实例产生开销。则可以使用raw_id_fields只显示当前的外键id,代码与示例如下:
# 一对多 class UserAdmin(admin.ModelAdmin): raw_id_fields = ('company', ) # 外键
# 多对多 class UserAdmin(admin.ModelAdmin): raw_id_fields = ('roles', ) # 多对多
作用:只读字段
class UserAdmin(admin.ModelAdmin): readonly_fields = ('name', 'age')
在详情页面添加自定义的只读字段
class UserAdmin(admin.ModelAdmin): readonly_fields = ('address_report',) def address_report(self, instance): return 'xxx' # short_description functions like a model field's verbose_name address_report.short_description = "Address"
作用: 在管理员更改表单上启用“另存为”功能
通常,对象具有三个保存选项:“保存”,“保存并继续编辑”和“保存并添加另一个”。如果save_as
为True
,则“保存并添加另一个”将由“另存为新”按钮代替,该按钮创建新对象(具有新ID)而不是更新现有对象。
默认情况下,save_as
设置为False
。
class UserAdmin(admin.ModelAdmin): save_as = True
作用:当save_as=True
,保存新对象后的默认重定向是该对象的更改视图即详情页面。如果设置了 save_as_continue=False
,重定向将转到更改列表视图。默认情况下,save_as_continue
设置为True
。
作用:通常,保存按钮仅出现在表单底部。如果设置save_on_top
为True
,则按钮将同时显示在顶部和底部。
默认情况下,save_on_top
设置为False
。
作用: 设置search_fields
为在管理员更改列表页面上启用搜索框。应该将其设置为一个字段名称列表,只要有人在该文本框中提交搜索查询,就将对其进行搜索。
class UserAdmin(admin.ModelAdmin): search_fields = ['name', 'age'] # 搜索框可通过name或者age搜索
可以使用外键或者多对多关系
search_fields = ['user__email']
还提供了一些用于指定字段查找的(较旧的)快捷方式。您可以在字段中search_fields
添加以下字符作为前缀,这等同于添加__
到该字段中:如‘=name‘
相当于‘name__iexact‘
字首 | 抬头 |
---|---|
^ | startswith 以什么开头 |
= | iexact 不区分大小写的完全匹配 |
无 | icontains 不区分大小写的包含 |
作用: 设置show_full_result_count
以控制是否应在过滤的管理页面上显示对象的全部数量。如果将此选项设置为True
, 则搜索框旁会显示类似的文本99 results (103 total)
;设置为False
显示99 results (Show all)
作用:默认情况下,更改列表页允许按list_display
中指定的所有模型字段进行排序(直接点击下图字段名可进行排序)。如果要禁用某些列的排序,请使用sortable_by设置,将不希望排序的字段不填写在sortable_by元组或者列表中。
class UserAdmin(admin.ModelAdmin): list_display = ('name', 'age', 'gender') sortable_by = ( 'age', 'gender') # 此设置,上图name字段无法通过点击进行排序
作用: 这个属性可以控制是否在admin页面显示“View site”的链接。这个链接主要用于跳转到你指定的URL页面。
属性的值可以是布尔值或某个调用。如果是True(默认值),对象的get_absolute_url()
方法将被调用并生成rul。
如果你的模型有一个get_absolute_url()
方法,但你不想显示“View site”链接,你只需要将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
save_model(request,obj,form,change)
重写此方法可进行保存前或保存后的操作。调用super().save_model()
以保存对象。
例如,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) # 执行保存 # 此处写保存后需要执行的操作
delete_model(request,obj)
重写此方法可进行删除前或删除后的操作。调用super().delete_model()
以使用删除对象 Model.delete()
,使用方式同save_model。
delete_queryset(request,queryset)
重写此方法可自定义" delete selected objects "操作的删除过程。
save_formset(request, form, formset, change)
save()
方法返回已经保存到数据库的实例。如果一个给定实例的数据没有在绑定数据中改变,那么实例不会被保存到数据库,也不会包含在返回值里(上述例子中的 instances
)。
当表单中缺少字段时(比如因为它们被排除),这些字段不会被 save()
方法设置。在选择使用字段里( Selecting the fields to use ),你可以找到有关此限制的更多信息,这也适用于的普通的 ModelForms
。
传递 commit=False
,返回未保存的模型实例:
# don't save to the database >>> instances = formset.save(commit=False) >>> for instance in instances: ... # do something with instance ... instance.save()
这会你在保存它们到数据库之前,附加数据给实例。如果你的表单集包含 ManyToManyField
,你也将需要调用 formset.save_m2m()
来确保正确保存了多对多关系。
完整示例:
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()
get_ordering(request)
get_ordering方法将request作为参数,并预期返回一个列表或元组,以便进行类似于ordering属性的排序。
class PersonAdmin(admin.ModelAdmin): def get_ordering(self, request): if request.user.is_superuser: # 判断是不是超级用户 return ['name', 'rank'] else: return ['name']
get_search_results(request, queryset, search_term
自定义搜索条件
class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'age') search_fields = ('name',) def get_search_results(self, request, queryset, search_term): """ 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‘) 更有效
如果 search_fields 多于一个,django 会使用 OR 语句拼接每个条件,速度不如直接使用确定的 field 进行搜索。
save_related(request, form, formsets, change)
在这里,您可以对与父对象相关的对象执行任何保存前或保存后操作。请注意,此时父对象已保存。
get_autocomplete_fields(request)
该方法返回list
或tuple
的字段名称,该字段名称将与自动完成小部件一起显示 。源码如下:
def get_autocomplete_fields(self, request): """ Return a list of ForeignKey and/or ManyToMany fields which should use an autocomplete widget. """ return self.autocomplete_fields
get_readonly_fields(request, obj=None)
该方法返回将显示为只读的字段名称的list
或tuple
get_list_display(request)
该方法返回list
或tuple
的字段名称,该名称将显示在更改列表视图中
get_list_display_links(request, list_display)
返回将链接到变更视图的变更列表中的一个None
或一个list
或tuple
多个字段名称
get_exclude(request, obj=None)
该方法返回exclude的字段列表
get_fields(request, obj=None)
返回fields。
get_fieldsets(request, obj=None)
该方法返回一个二元列表,fieldsets
get_list_filter(request)
返回list_filter。
get_search_fields(request)
返回search_fields。
get_sortable_by(request)
返回一个集合(如list
,tuple
或set
)字段名,通过该集合进行排序。
get_urls()
返回ModelAdmin的可用urls。
在get_urls
上一个方法ModelAdmin
中相同的方式,URL配置要用于该返回的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 %}
在此示例中,my_view
将通过访问 /admin/myapp/mymodel/my_view/
。
但是,self.my_view
上面注册的功能存在两个问题:
由于通常这不是您想要的,因此Django提供了一个方便的包装器来检查权限并将视图标记为不可缓存。该包装器是AdminSite.admin_view()
(即self.admin_site.admin_view
在ModelAdmin
实例内部);像这样使用它:
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
属性。
**get_form(request, obj=None, **kwargs)**
返回ModelForm
要在管理员添加和更改视图中使用的类,请参见add_view()
和change_view()
。
例如,如果您想向超级用户提供其他字段,则可以以其他基本形式进行交换,如下所示:
class MyModelAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): if request.user.is_superuser: kwargs['form'] = MySuperuserForm # 如果是管理员,那么就显示MySuperuserForm return super().get_form(request, obj, **kwargs)
**formfield_for_foreignkey(db_field, request, **kwargs)**
允许您覆盖外键字段的默认formfield。例如,根据用户返回此外键字段的对象子集:
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)
**formfield_for_manytomany(db_field, request, **kwargs)**
与formfield_for_foreignkey方法一样,可以重写formfield_for_manytomany方法以更改多对多字段的默认表单字段。例如,如果车主可以拥有多辆汽车,并且汽车可以属于多个车主(多对多关系)则可以筛选"汽车外键"字段,以便仅显示用户拥有的汽车:
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)
**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)
**get_changelist(request, **kwargs)**
返回要用于列表的Changelist类。默认情况下,使用 django.contrib.admin.views.main.ChangeList。通过继承此类,您可以更改列表的行为。
**get_changelist_form(request, **kwargs)**
返回一个 ModelForm 类,用于更改列表页上的"表单集"。要使用自定义form,例如:
from django import forms class MyForm(forms.ModelForm): pass class MyModelAdmin(admin.ModelAdmin): def get_changelist_form(self, request, **kwargs): return MyForm
**get_changelist_formset(request, **kwargs)**
如果使用list_editable,则返回用于changelist页面上的 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)
has_view_permission(request, obj=None)
如果允许查看 obj,应返回 True,否则应返回 False。如果 obj 为 None,则应返回 True 或 False 以指示是否允许查看此类型的对象(例如,False 将解释为不允许当前用户查看此类型的任何对象)。
如果用户具有"更改"或"查看"权限,则默认实现将返回 True。就是设置是否有访问权限。
has_add_permission(request)
是否具有添加数据的权限。如果允许添加对象,应返回 True,否则应返回 False。
has_change_permission(request, obj=None)
是否具有change权限。 如果允许编辑 obj,应返回 True,否则应返回 False。如果 obj 为"无",则应返回 True 或 False 以指示通常是否允许编辑此类型的对象
has_delete_permission(request, obj=None)
是否具有delete权限。 如果允许删除 obj,应返回 True,否则应返回 False。如果 obj 为"无",则应返回 True 或 False 以指示通常是否允许删除此类型的对象。
has_module_permission(request)
如果允许在管理页上显示模块并访问模块,则应返回 True,否则为 False。默认情况下使用 User.has_module_perms() 。虽然设置为True后不显示该模块,但是通过url依然可以访问。
get_queryset(request)
modelAdmin 上的get_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)
message_user(request, message, level=messages.INFO, extra_tags=‘‘, fail_silently=False)
使用django.contrib.messages backend向django后台用户发送信息。
class ArticleAdmin(admin.ModelAdmin): ... def make_published(self, request, queryset): rows_updated = queryset.update(status='p') if rows_updated == 1: message_bit = "1 story was" else: message_bit = "%s stories were" % rows_updated self.message_user(request, "%s successfully marked as published." % message_bit)
get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
返回一个分页实例
response_add(request, obj, post_url_continue=None)
决定add_view()的HttpResponse , response_add在提交管理表单后以及创建和保存对象和所有相关实例之后调用。可以重写它,以在创建对象后更改默认行为。
response_change(request, obj)
决定change_view()的HttpResponse,model被修改后运行。
response_delete(request, obj_display)
决定delete_view()的HttpResponse,model被删除后运行。
get_changeform_initial_data(request)
默认情况下,字段从 GET 参数中给出初始值。例如,?name=initial_value将名称字段的初始值设置为initial_value。
def get_changeform_initial_data(self, request): return {'name': 'custom_initial_value'}
add_view(request, form_url=‘‘, extra_context=None)
Django的视图模型实例新增页面
change_view(request, object_id, form_url=‘‘, extra_context=None)
Django的视图模型实例编辑页面
changelist_view(request, extra_context=None)
模型实例更改列表/操作页面的Django视图
delete_view(request, object_id, extra_context=None)
模型实例删除确认页面的Django视图
history_view(request, object_id, extra_context=None)
该页面的Django视图,显示了给定模型实例的修改历史记录。
这5个方法是被实际的设定为django的view方法的。可以重构,一般是添加渲染view使用的模板的context data:
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, )