Django使用小部件创建一个只读的表单字段
Posted
技术标签:
【中文标题】Django使用小部件创建一个只读的表单字段【英文标题】:Django creating a form field that's read only using widgets 【发布时间】:2010-12-22 02:26:30 【问题描述】:我的表单域如下所示:
class FooForm(ModelForm):
somefield = models.CharField(
widget=forms.TextInput(attrs='readonly':'readonly')
)
class Meta:
model = Foo
上面的代码出现如下错误:init() got an unexpected keyword argument 'widget'
我认为这是对表单小部件的合法使用?
【问题讨论】:
这在 django 1.9 ***.com/questions/324477/… 中改变了 【参考方案1】:您应该使用表单字段而不是模型字段:
somefield = models.CharField(
widget=forms.TextInput(attrs='readonly': 'readonly')
)
替换为
somefield = forms.CharField(
widget=forms.TextInput(attrs='readonly': 'readonly')
)
应该修复它。
【讨论】:
看起来不安全! 就 jpic 而言,用户可以禁用只读标志并仍然提交信息。请参阅***.com/a/325038/4972 以获得更全面的答案。【参考方案2】:请注意,readonly
属性不会阻止 Django 处理客户端发送的任何值。如果值不变对您很重要,无论您的用户对FireBug 有多么有创意,您都需要使用更复杂的方法,例如ReadOnlyField
/ReadOnlyWidget
就像 Alex Gaynor 在 blog entry 中展示的那样。
【讨论】:
【参考方案3】:我遇到了同样的问题,所以我创建了一个似乎适用于我的用例的 Mixin。
class ReadOnlyFieldsMixin(object):
readonly_fields =()
def __init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = False
def clean(self):
cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
for field in self.readonly_fields:
cleaned_data[field] = getattr(self.instance, field)
return cleaned_data
用法,只定义哪些必须是只读的:
class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
readonly_fields = ('field1', 'field2', 'fieldx')
【讨论】:
【参考方案4】:正如 Benjamin (https://***.com/a/2359167/565525) 很好解释的那样,除了正确渲染之外,您还需要正确处理后端的字段。
有一个 SO question and answers 有很多好的解决方案。但无论如何:
1) 第一种方法 - 删除 save() 方法中的字段,例如(未测试;)):
def save(self, *args, **kwargs):
for fname in self.readonly_fields:
if fname in self.cleaned_data:
del self.cleaned_data[fname]
return super(<form-name>, self).save(*args,**kwargs)
2) 第二种方法 - 在 clean 方法中将字段重置为初始值:
def clean_<fieldname>(self):
return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
基于第二种方法,我将其概括如下:
from functools import partial
class <Form-name>(...):
def __init__(self, ...):
...
super(<Form-name>, self).__init__(*args, **kwargs)
...
for i, (fname, field) in enumerate(self.fields.iteritems()):
if fname in self.readonly_fields:
field.widget.attrs['readonly'] = "readonly"
field.required = False
# set clean method to reset value back
clean_method_name = "clean_%s" % fname
assert clean_method_name not in dir(self)
setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))
def _clean_for_readonly_field(self, fname):
""" will reset value to initial - nothing will be changed
needs to be added dynamically - partial, see init_fields
"""
return self.initial[fname] # or getattr(self.instance, fname)
【讨论】:
以上是关于Django使用小部件创建一个只读的表单字段的主要内容,如果未能解决你的问题,请参考以下文章