readonly_fields 列表中为只读字段,不能修改
所以在form.py文件中对在readonly_fields 列表里的字段添加一个 disabled 样式
for field_name,field_obj in cls.base_fields.items():
# 根据 cls.base_field 的特性给每个字段加上样式
field_obj.widget.attrs['class'] = 'form-control'
if field_name in admin_class.readonly_fields:
field_obj.widget.attrs['disabled'] = 'disabled'
这时在修改页面无法对readonly_fields里的字段做修改,但是修改其它数据并点击保存时,不会报错,还是跳转到当前页,但是readonly_fields 里字段的数据没有显示了,并且修改的数据也没有保存。
此时应该加一个js方法,当点击保存时,把所有的 disabled 移除
function SelectAllData() {
//remove all disabled attrs
$("form").find("[disabled]").removeAttr("disabled")
return true
}
这样就能正常保存了,但是这种方法毫无技术含量,只能骗骗小白,在网页的检查源代码里可以直接对只读字段进行修改,所以还需要对这种方法进行补充!
现在我们需要利用form.py里的一个clean方法,这个方法会在提交数据并调用 form_obj.is_valid() 时自动调用,然后可以在clean 方法体里对前端提交的数据与数据库里的数据进行对比,两个数据不相同就抛出一个 raise
# 视图中调用 is_valid() 时,会自动调用这个方法
def default_clean(self):
'''给所有的form默认加一个clean验证'''
from django.forms import ValidationError
from django.utils.translation import ugettext as _ # 这是国际化处理
for field in admin_class.readonly_fields:
field_val = getattr(self.instance, field) # 数据库中的值
# print("clean ----",self) cleaned_data主要用来检查字段是否符合定义的格式,如果是则返回其值
field_web_val = self.cleaned_data.get(field) # 前端修改后的值
if field_val !=field_web_val:
ValidationError(
_('Field %(field)s is readonly, data should be %(field_val)s'),
code='invalid',
params={'field':field, 'field_val':field_val},
)
但是这样尽管修改了多个不能修改的敌方,只会抛出第一个错误,所以可以创键一个 error_list 来 append ValidationError对象,最后通过判断 error_list不为空抛出整个error_list。
大家应该发现了方法里有一个admin_class,但是并没有哪里传进来或者创建,其实clean方法是在create_model_form方法里面的,而create_model_form里有一个admin_class参数,这就有点像闭包的形式了,虽然创建表单的方法被调用过了,但是admin_class参数还会被保留下来,在里面的方法还是可以直接调用,也不需要传参
但是现在又出现了一个新的问题
在 add 页面 readonly 字段也不能填写数据了,有的朋友会说,添加页面不需要 instance参数,用 instance 来判断是修改还是添加页面就好了嘛!可是,disabled 样式是在 _ new_函数中添加的,_new _函数是比 _init _还要先调用的函数,所以_new _函数中是没有instance参数的。
现在教大家一个简单的方法:
在add的视图函数中给admin_class添加一个change_form属性,随便给个值,例如False,然后在添加样式之前判断一下 hasattr(admin_class, 'change_form'),没有change_form属性才给字段对象加上 disabled样式。
现在迎来最后一个问题了!!是最后一个!!!
这个时候在add页面点击提交时会报出
因为add视图函数中也有一个 is_valid(), 就会默认调用clean函数,这个时候就会报错,只要在clean函数体里用 self.instance.id判断不是添加页面就好了。
在这里要注意了!!
可能有的人会用self.instance来做判断,因为 add 函数中并没有传入一个 instance = model_obj,注意了,不管传没传,self.instance 这句话都不会报错,所以加上一个id属性就可以了。