标签:des http io os 使用 ar strong for 文件
定制的代码通常写在 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=() 表示 “仅显示这些字段”,而 excludes=() 则表示 “不要显示这些字段”。视具体情境,一般二者只用其一,通常 fields 出现的机会更高些,毕竟他的控制更加精准。
class UserAdmin(models.ModelAdmin):
def change_view():
self.fields = (‘name‘,‘mobile‘,‘email‘)
注意元组元素的类型为字符串,后面类似元素的类型一般也都是字符串,不再强调。
这个属性的作用就和他的名字一样。默认 change page 显示的字段都是可编辑状态的,因为 admin 本来就是做可视化编辑数据库之用的嘛。
...
self.readonly_fields = (‘name‘,)
额外需注意的 Python 语法:对于单元素的元组,必须在元素后面加一个 “,” 。这是因为 Python 支持对语句使用小括号封装,你不加逗号,会被解释器误认为一条语句,而不是元组。
默认情况下,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,否则会被转义除了像上面通过 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 请求会首先被 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 中的字段,你改了也没用。
对于这种状况,要么你在方法中改一次 fields 或 readonly_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_fields ,fieldsets 等字段的定义写在 channge_view() 的 if request.method == ‘GET‘: 里,而不是直接写成类属性。
但其实并不太推荐上面这种过于 hack 的写法。
更常用的对保存请求的修改发生在 save_model(self, request, obj, form, change) 里,参数里不仅有 request ,还有表示 Model 实例的 obj 可以用。这里因为 POST 已经被处理过了,所以你可以不受限制地修改 obj 的属性(当然要在数据有效的前提下)。
obj 和 form 分别是修改后待保存的 model 实例和 POST 提交的 Form 对象,即 obj.attr1 和 form.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 里改,不然用户即接收不到提示,保存也不会生效。
django 对提交数据的有效性验证提供了三种方式,分别对应三个层面。
Form 对象校验
通过 Form 对象来进行数据校验的方法很容易找到,包括 django book 中也有一章在讲,这里就不再赘述了
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!‘) validators 字段校验
这是一种针对 ModelField 的校验规则,具体我也没用过,可以去官网 ref/forms/validation/ 查看文档。
这三种不同的校验方法所作用的层面也不同,一般在 admin 中,或其他处理表单提交的地方,Form 和 Model 实例较常用。Form 多针对单个字段的数据类型或内容合法性验证,Model 实例则擅长做多字段间的逻辑性验证。
在 admin 的模板加载路径内,可以找到一个 submit_line.html 文件,这便是 admin change page 底部那三个 保存并继续编辑、保存并新增、保存 按钮定义的位置。
可惜单在 template/admin/appname/change_form.html 同级目录下新建一个 submit_line.html 的方式并不能使其自动加载。你还得显式地在 change_form.html 中 include 它。
{% 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)
Model 的 Field 第一位置参数是 verbose_name ,这个更加易读的字段名会被自动展示在 Admin Form 中,如果我们想在别处访问它的话,比如 messages 中,那么获取的方式可以是
Model[instance]._meta.get_field(‘field_name‘).verbose_name
Django 自定义 Admin change page 的一般方法
标签:des http io os 使用 ar strong for 文件
原文地址:http://my.oschina.net/lionets/blog/312576