1 后台管理修改--增加栏目
在左侧增加自定义的栏目是网站项目的常见需求。因为Django后台栏目是根据Model自动生成的,所以可以在model.py中定义一个model,然后在admin.py中定义对应的类,并注册上去,这样就会在左侧自动生成一个栏目。
model.py中添加如下类:
class CheckAll(models.Model):
class Meta:
app_label = 'case_check'
verbose_name = '校对批量分配(按来源)'
verbose_name_plural = '校对批量分配(按来源)'
在admin.py中添加如下的类(此类的功能还没有实现,后期再实现):
@admin.register(CheckAll)
class CheckAllAdmin(admin.ModelAdmin):
def changelist_view(self, request, extra_content=None):
return request
这样,左侧就有了“校对批量分配(按来源)”的栏目。
2 后台管理修改--change_list页面修改
需求:需要在admin后台的list页面上增加链接(或者是按钮)“显示已校对”、“显示未校对”、“显示所有”,点击之后list页面按照对应要求显示内容。
2.1 用action的方法实现--失败
由于对admin后台不熟悉,开始在action里面添加一个动作,想通过action完成此功能。结果发现,在点击go之后,虽然会去执行action对应的函数里面的代码,但是还是返回到当前的model list页面执行所有记录的查询。所以此路不通。
添加action方法,在admin.py文件对应的XXModelAdmin类中添加如下代码:
# 修改action内容
actions = ['show_all']
def show_all(self, request, queryset):
pass
show_all.short_description = "显示已校对"
show_all.acts_on_all = True
action还有一个问题,就是在执行go操作之前必须选择至少一条记录进行操作,去掉此限制,可以在changelist_view()中加入如下代码实现:
# 移除action必须选择一个记录的限制
def changelist_view(self, request, extra_context=None):
try:
action = self.get_actions(request)[request.POST['action']][0]
action_acts_on_all = action.acts_on_all
except (KeyError, AttributeError):
action_acts_on_all = False
if action_acts_on_all:
post = request.POST.copy()
post.setlist(admin.helpers.ACTION_CHECKBOX_NAME,
self.model.objects.values_list('id', flat=True))
request.POST = post
return super(OriginCaseAdmin, self).changelist_view(request, extra_context)
2.2 在change_list页面上添加链接
为了能够在后台列表页面上进行修改,必须修改对应的模板文件,admin后台的模板文件没有放在项目里面,而是放在Django的源码中,其体路径是在你项目的虚拟路径下:venv4network\Lib\site-packages\django\contrib\admin\templates\admin
Django的admin后台列表页面对应的是change_list.html页面,需要将此文件复制到模板文件夹下。在项目的templates目录下,新建admin文件夹,在新建项目名称的文件夹(我这里是case_check),在新建你对应的Model类名称的文件夹(我这里是origincase),然后把change_list.html文件复制在此文件夹中。
打开change_list.html,在对应位置添加一个div,把我们对应的链接添加上去,下面截取部分代码:
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
<div><a href="?mode=show_all"> 显示所有</a> <a href="?mode=show_check"> 仅显示已校对</a> <a href="?mode=show_nocheck"> 仅显示未校对</a></div>
{% result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
2.3 用GET传递参数,在get_queryset()方法中进行判断--失败
想到get_queryset()方法是返回数据集的最终方法,如果在这个函数里面做一个条件判断,那么应该可以实现。但是,如何触发条件呢?以action的方式已经不行,那么可以通过链接的方式,在GET上传递一个参数,根据这个参数触发条件。但还是失败了。
这个我的链接http://127.0.0.1:8000/admin/c...,通过request.GET获得mode对应的show_all这个值。但是,每次点击完这个链接之后,都会跳转到一个带错误的链接地址上:
http://127.0.0.1:8000/admin/c...
为了找到失败原因,查看了Django的changelist_view()方法的源码,终于发现了问题所在。下面是一段源码,里面对GET参数进行了检查,把不合法的参数进行了重定向。当然,这是Django对于防注入攻击的一个很好的功能,却是把我整晕了好一阵。
try:
cl = self.get_changelist_instance(request)
except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
# parameter via the query string. If wacky parameters were given
# and the 'invalid=1' parameter was already in the query string,
# something is screwed up with the database, so display an error
# page.
if ERROR_FLAG in request.GET:
return SimpleTemplateResponse('admin/invalid_setup.html', {
'title': _('Database error'),
})
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
2.3 传递GET参数,写入session,在在get_queryset()方法中进行判断--成功
2.2中,每一次传递的参数都被自动重定向了,我不可能去改Django的源码啊,也不推荐这么去做。怎么办呢?
第一,需要对GET传递的参数进行获取和判断,并对这个判断结果进行状态保持;
第二,避免触发重定向;
第三,在get_queryset()方法对状态进行判断,以返回对应的数据集。
所以第一步的状态必须在第三步中能够使用,而request已经不适合作为参数传递了,所以想到用session来保持这个参数。把GET中获取的参数保持在session中,最后利用session中的参数,在get_queryset()中进行判断。同时,为了避免触发重定向,我们需要修改request。
下面分别来实现:
第一、第二步:获取GET参数,这个需要在admin的业务逻辑中实现,这个对应在changelist_view()方法中实现。获取到show_all之后,写入到session,然后必须把GET里面内容删除,删除之后就不会触发重定向;否则super(OriginCaseAdmin, self).changelist_view(request, extra_context)之后,还是会重定向页面。
def changelist_view(self, request, extra_context=None):
if request.method == "GET" and (mode := request.GET.get('mode')) == "show_all":
request.session["mode"] = "show_all"
get = request.GET.copy()
del get["mode"]
request.GET = get
return super(OriginCaseAdmin, self).changelist_view(request, extra_context)
第三步,在get_queryset()方法中对session值进行判断,过滤数据集对应的值。
def get_queryset(self, request):
qs = super(OriginCaseAdmin, self).get_queryset(request)
if request.user.is_superuser:
if request.session.get("mode") == "show_all":
request.session["mode"] = None
if request.session.get("mode") == "show_check":
request.session["mode"] = None
return qs.filter(check_flag="是")
if request.session.get("mode") == "show_nocheck":
request.session["mode"] = None
return qs.filter(check_flag="")
return qs
else:
if request.session.get("mode") == "show_all":
request.session["mode"] = None
if request.session.get("mode") == "show_check":
request.session["mode"] = None
return qs.filter(check_flag="是").filter(user = request.user)
if request.session.get("mode") == "show_nocheck":
request.session["mode"] = None
return qs.filter(check_flag="").filter(user = request.user)
return qs.filter(user=request.user)
总觉得实现的怪怪的,代码也冗长,但好歹实现了功能。由于对Django不熟悉,目前想到的是这种方法,也许还有更好的、更方便的方法来实现。
参考文献
几个比较好的参考文献保存一下:
3.Python Django之GET请求和POST请求及响应处理
Django定制Admin页面(展示页面和编辑页面)
这篇告诉定义列表行中的自定义按钮或链接:关于在django框架中在admin页面下添加自定义按钮并实现功能
如何在Django admin中创建自定义按钮,它会创建许多对象?
Django Admin 管理后台添加自定义信息及定制化页面
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。