Django 自定义 Admin change page 的一般方法

定制的代码通常写在 app/admin.py 中的一个名为 ModelAdmin 的类里,主要以属性和方法的形式。或者更进一步说,代码大都存在于该类的 change_view(self, request, object_id, extra_context=None) 方法 或 save_model(self, request, obj, form, change) 方法中,这也就是下面示例代码在修改属性时都会使用一个 self.xxx = ‘xxx‘ 语句的原因。

类名中的 Model 指的是 app/models.py 中具体的模型名。可定制的内容如下:

修改字段显示


fields & exclude 属性

这两个属性的作用类似,类型也都是元组(推荐)。其中 fields=() 表示 “仅显示这些字段”,而 excludes=() 则表示 “不要显示这些字段”。视具体情境,一般二者只用其一,通常 fields 出现的机会更高些,毕竟他的控制更加精准。

class UserAdmin(models.ModelAdmin):
    def change_view():
        self.fields = (‘name‘,‘mobile‘,‘email‘)

注意元组元素的类型为字符串,后面类似元素的类型一般也都是字符串,不再强调。

readonly_fields 属性

这个属性的作用就和他的名字一样。默认 change page 显示的字段都是可编辑状态的,因为 admin 本来就是做可视化编辑数据库之用的嘛。

...
    self.readonly_fields = (‘name‘,)

额外需注意的 Python 语法:对于单元素的元组,必须在元素后面加一个 “,” 。这是因为 Python 支持对语句使用小括号封装,你不加逗号,会被解释器误认为一条语句,而不是元组。

fieldsets 属性

默认情况下,change page 对字段的显示是一个字段显示一行,然后把所有字段一字排下来。换句话说就是页面只有一个 <fieldset>。而使用 fieldsets 属性,就可以建立多个 <fieldset> ,通常伴随着的,还有将不同字段放在同一行的操作:

...
    self.fieldsets = (
        (u‘基本信息‘,{
            ‘fields‘:(
                (‘name‘,‘mobile‘,‘email‘,),#一行
                (‘address‘,‘city‘,‘country‘,),#另一行
                #新一行
                ),
            ‘classes‘:(‘person‘,),#html 标签的 class 属性
            }),
        (u‘身份证信息‘,{ #另一个 fieldset
            ‘fields‘:(
                (‘display_idcrdfnt‘,‘display_idcrdbck‘,),),
            }),
        )

这段代码因为括号太多看起来会比较乱,实际自己写一写就好了。其中 fieldsets 的元素为一个二元组:其中第一元为 <fieldset> 的名称,第二元为字典,键包括定义字段的 fields 和 定义html元素属性的 classes

显示额外字段

前面的手段都是控制如何隐藏字段,显示 model 的子集。对于 model 里没有的字段,想在 Form 里显示的话就要把他们定义为 方法,并将方法名加入 readonly_fields 元组,因为这些字段都是不存在于数据库中的,所以必须展示为不可编辑状态。

...
    self.fields = (‘display_img‘,...)
    self.readonly_fields = (‘display_img‘,...)

def display_img(self,obj):
    url = obj.img_url
    return ‘<img ref="%s" />‘%url

display_img.short_description = ‘用户照片‘
display_img.allow_tags = True

上例中假设 model 的img_url 字段存储了用户照片的链接,但我们想在 change page 展示出用户的照片,而不是一段字符串,那么就可以通过这种方式自定义一个显示字段,最后定义的两个属性:

  • short_description :对字段的说明性文字,会显示在图片的前面,类似于 sql 的 comment,或 model Field 的第一参数
  • allow_tags :tags 指的是 html 标签,上例中我们直接返回了一个 <img> 标签,那么这里就必须设置为 True,否则会被转义

直接修改 admin 模板

除了像上面通过 change_view() 方法来修改显示内容外,还可以直接编辑模板。template/admin/change_form.html (或其他路径)文件就是渲染 admin 页面所使用的模板。通常我们会选择在以下路径派生一个 app 专用模板 template/admin/appname/change_form.html

而额外 context 的传递,就要通过 change_view(...,extra_context=None) 参数来实现了:

...
    def change_view(self, request, object_id, extra_context=None):
        ...
            extra_context = {‘custom‘:‘some_value‘}
        return super(ModelAdmin,self).change_view(...,extra_context)

修改保存请求



在 change page 页面,如果用户点击了保存按钮,那么一个 POST 请求就会被提交上来,然后保存到数据库中。从用户提交请求到更新数据库之间有两个机会修改请求的内容。分别为 修改 POST 请求修改 Model 实例

修改 POST 请求

用户提交的 POST 请求会首先被 change_view(self, request, object_id, extra_context=None) 捕获,因此可以在这个方法里直接对其进行修改。

...
    def change_view(self, request, object_id, extra_context=None)
        ...
        if request.method == ‘POST‘:
            request.POST[‘field‘] = xxx
        return super(Model,self).change_view()

注意:django 出于安全考虑,不在 fields 中的字段,添加到 POST 里也没用;在 read_only 中的字段,你改了也没用。

对于这种状况,要么你在方法中改一次 fieldsreadonly_fields 属性,要么就把修改放到下一节的 save_model() 方法里。

...
    def change_view(...):
        ...
        if request.method == ‘POST‘:
            new_readonly = list(self.readonly_fields)
            new_readonly.remove(‘some_field‘)
            self.readonly_fields = set(new_readonly)
            ...
        return super(...)

要想避免因此操作导致用户下一次 GET change page 显示出额外可编辑字段的话,就应尽量把对如 readonly_fieldsfieldsets 等字段的定义写在 channge_view()if request.method == ‘GET‘: 里,而不是直接写成类属性。

但其实并不太推荐上面这种过于 hack 的写法。

在保存前修改 Model 实例

更常用的对保存请求的修改发生在 save_model(self, request, obj, form, change) 里,参数里不仅有 request ,还有表示 Model 实例的 obj 可以用。这里因为 POST 已经被处理过了,所以你可以不受限制地修改 obj 的属性(当然要在数据有效的前提下)。

objform 分别是修改后待保存的 model 实例和 POST 提交的 Form 对象,即 obj.attr1form.save(commit=False).attr1 的值是相同的,都是最新的数据。但 form 里可能不会包含全部的模型字段,因为可能有一些字段被隐藏,还有一些是只读状态。change 参数是一个布尔值,表示当前的保存请求是来自于新建还是变更操作。

def save_model(self, request, obj, form, change):
    if change:
        obj.mod_time = datetime.datetime.now()
    return super(ModelAdmin,self).save_model(request, obj, form, change)

虽然上面这个功能可以在 Model 中简单使用 auto_now=True 来实现。

一个更有代表性的例子可能是:我们希望用户不必手动修改某个字段,而是依据其点击的提交按钮的不同,来自动修改该字段。具体修改提交按钮(submit button)的方法在更下面给出,这里先假设我们自定义了一个 <input type="submit" value="审核通过" name="apply_approved"/> 按钮,并希望用户按此按钮时,自动将 Model 实例的 state 属性改为 approved

...
    def save_model(self,request,obj,...):
        if ‘apply_approved‘ in request.POST:
            obj.state = ‘approved‘
        ...
        return super(...)

注意:save_model() 方法实际执行的就是将 POST 请求更新到数据库中的过程,因此 obj.save()super().save_model() 方法你要保证至少调用一个。不要将此方法用于否决 POST 请求(比如发现数据不合法时就简单的 pass 掉),这项要求是官方文档提出的,具体我也不清楚为什么。

这两种修改方法具体使用哪一种要视具体情况而定。一种可能的状况是:下节要讲到的 Model 里的 clean() 方法对用户提示数据非法的过程发生在 change_view() 之中,即 save_model() 之前,所以如果你想修改其中将要校验的字段,那么最好在 POST 里改,不然用户即接收不到提示,保存也不会生效。

数据校验(data validation)

django 对提交数据的有效性验证提供了三种方式,分别对应三个层面。

  1. Form 对象校验

    通过 Form 对象来进行数据校验的方法很容易找到,包括 django book 中也有一章在讲,这里就不再赘述了

  2. Model 实例校验

    django 的 ORM 将每一条数据库记录映射为一个 Model 的实例,那么通过该实例的方法来对属性进行验证就非常顺理成章了。如

    from django.core.exceptions import ValidationError
    
    class Model(models.Model):
        ...
        def clean(self):
            if self.age < 18 and self.18x_authorization == True:
                raise ValidationError(‘too young, too simple.‘)

    这里 raise ValidationError 会被 save_model() 捕捉,并将错误信息返回给 change page view,还有针对该非法字段的 CSS 样式展现。总之就是督促用户修改表单,再重新提交。注意若在 admin 的 save_model() 方法中引发此异常会导致 HTTP 500,所以记得要把它写在 Model 的 clean(self) 里。

    说到给用户返回消息,还有一个专用方法:

    from django.contrib import messages
    
    ...
        messages.info(request, ‘Hello world.‘)
        messages.warning(request, ‘FBI WARNING.‘)
        messages.error(request, ‘You shall not pass!‘)
  3. validators 字段校验

    这是一种针对 ModelField 的校验规则,具体我也没用过,可以去官网 ref/forms/validation/ 查看文档。

这三种不同的校验方法所作用的层面也不同,一般在 admin 中,或其他处理表单提交的地方,Form 和 Model 实例较常用。Form 多针对单个字段的数据类型或内容合法性验证,Model 实例则擅长做多字段间的逻辑性验证。

修改底部提交栏


submit_line 模板

在 admin 的模板加载路径内,可以找到一个 submit_line.html 文件,这便是 admin change page 底部那三个 保存并继续编辑保存并新增保存 按钮定义的位置。

可惜单在 template/admin/appname/change_form.html 同级目录下新建一个 submit_line.html 的方式并不能使其自动加载。你还得显式地在 change_form.htmlinclude 它。

{% block submit_buttons_bottom %}
{% include "admin/audit/mmauth/submit_line.html" %}
{% endblock %}

django 1.4 起 submit row 才包含在一个 block 中,1.3 及以前的版本你需要手动修改 template/admin 目录下的公用模板.

保存后重定向

在 admin 页面中,保存并转到下一个 (save and view next) 应该是一个很常用的按钮,可惜 django 并没有标配。

实现此功能的第一步是修改 submit_line 模板,把按钮改成(或添加成)我们需要的样子,注意按钮的 name = "_save" 属性,这个随 form 一起提交的字段是我们判断用户到底按了什么的依据。

save_model() 返回后,还有一个方法会被默认调用:response_change(self,request,obj)。重定向到 下一个 的代码就可以写在这里:

...
    def response_change(self,request,obj):
        next = Auth.objects.filter(...)[:1]
        if next:
            return HttpResponseRedirect(‘../%d/‘%next.get().id)
        return super(ModelAdmin,self).response_change(request,obj)

其他


访问 Field 的 verbose_name

Model 的 Field 第一位置参数是 verbose_name ,这个更加易读的字段名会被自动展示在 Admin Form 中,如果我们想在别处访问它的话,比如 messages 中,那么获取的方式可以是

Model[instance]._meta.get_field(‘field_name‘).verbose_name
时间: 2024-08-24 22:22:37

Django 自定义 Admin change page 的一般方法的相关文章

django自定义Admin actions

通常情况下,admin的工作模式是“选中目标,然后修改目标”,但在同时修改大量目标的时候,这种模式就变得重复.繁琐. 为此,admin提供了自定义功能函数actions的手段,可以批量对数据进行修改.admin内置了一个批量删除对象的操作,如下图所示: 下面以一个新闻应用的文章模型为例,介绍一个批量更新的自定义actions,它将选择的文章由“草稿”状态更新为“发布”状态: 首先是模型的代码: from django.db import models STATUS_CHOICES = ( ('d

django 自定义User

django本身的auth_user 只包含了基本的信息包括用户名,密码,邮箱以及注册时间和最新的登录时间,但是这些字段很难满足我们的要求,有时我们想记录用户更多的信息,例如手机号等信息,这时就需要在auth_user 的基础上增加字段,django自定义User网上有四种方法. 1,是官网上推荐的方法,就是增加一个表auth_profile,其中以auth_user 表中的id作为Forgein Key将两个表过关联起来,这样可以在auth_profile 中增加多个用户的信息. 2,另外一种

Part 7:自定义admin站点--Django从入门到精通系列教程

该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453131687 本节我们主要介绍在第二部分提到过的admin后台管理站点. Django的admin站点是自动生成的.高度可定制的,它是Django相较其它Web框架独有的内容,广受欢迎.如果你觉得它不够美观,还有第三方美化版xadmin.请一定不要忽略它,相信我,它值得拥有! 一.定制模型表单 在前

自定义路由组件,Django的admin后台管理,DRF的三大认证,jwt认证

目录 一.自定义路由组件 1. 为什么要自定义路由组件 2. 自定义路由组件实例 二.Django的admin后台管理 三.DRF的三大认证组件概括 1. 认证组件 2. 权限组件 3. 频率组件 四.Django中的用户权限管理 五.jwt认证 1. jwt认证和普通session认证的区别 2. jwt认证介绍 (1)jwt的原理 (2)jwt三部分的内容 3. jwt的签发算法 (1)第一步:头部算法 (2)第二步:载荷部分的算法 (3)第三步:签名部分的算法 (4)第四步:连接生成tok

自定义admin

平时我们用的django自带admin,怎么评价呢?一个字简陋,而且也人性化,如下图,首先只显示数据对象,如果要查看详细还有点进去,其次不能对自己想要的数据进行刷选 我们的期望是:数据如excel显示,可以搜索查询,也可以条件查询 ok!是没问题的,这个我们可以实现自订制 场景分析 登录到django的admin里,首先就是一个列表索引页,并且是分app显示的,点击表就进入到表里,可以查询看数据行,想看行数据,还有要点击一下 如果上述描述让你领悟不到,可自行登录到django的admin看一下

Django 自定义用户认证

Django 自定义用户认证 Django附带的认证对于大多数常见情况来说已经足够了,但是如何在 Django 中使用自定义的数据表进行用户认证,有一种较为笨蛋的办法就是自定义好数据表后,使用OnetoOne来跟 Django 的表进行关联,类似于这样: from django.contrib.auth.models import User class UserProfile(models.Model): """ 用户账号表 """ user =

Django的admin后台

ModelAdmin对象 ModelAdmin类的简单使用 该ModelAdmin是在管理界面模型的表示.通常这些文件存储在admin.py应用程序中命名的文件中.让我们来看一个示例ModelAdmin: from django.contrib import admin from myproject.myapp.models import Author class AuthorAdmin(admin.ModelAdmin): pass admin.site.register(Author, Au

Django防Admin定制插件(一)

程序启动时查找所有注册了的apps.py 会执行def ready方法MyAdmin.apps.py: def ready(self): super(MyadminConfig,self).ready() from django.utils.module_loading import autodiscover_modules autodiscover_modules('reg') 这里应该是收集所有的reg文件.执行reg.py中的注册函数app01.reg.py: from app01 imp

django 自定义分页模块

django 自定义分页模块 from django.shortcuts import render, HttpResponse, redirect from django.utils.safestring import mark_safe class Page(object): def __init__(self, current_page): self.current_page = int(current_page) @property def start(self): return (se