Python Django创建的表单字段';s使用小部件只读

Python Django创建的表单字段';s使用小部件只读,python,django,forms,widget,Python,Django,Forms,Widget,“我的表单”字段如下所示: class FooForm(ModelForm): somefield = models.CharField( widget=forms.TextInput(attrs={'readonly':'readonly'}) ) class Meta: model = Foo 使用上面的代码获取如下错误:init()获得意外的关键字参数“widget” 我认为这是对表单小部件的合法使用?您应该使用表单字段而不是模型

“我的表单”字段如下所示:

class FooForm(ModelForm):
    somefield = models.CharField(
        widget=forms.TextInput(attrs={'readonly':'readonly'})
    )

    class Meta:
        model = Foo
使用上面的代码获取如下错误:init()获得意外的关键字参数“widget”


我认为这是对表单小部件的合法使用?

您应该使用表单字段而不是模型字段:

somefield = models.CharField(
    widget=forms.TextInput(attrs={'readonly': 'readonly'})
)
替换为

somefield = forms.CharField(
    widget=forms.TextInput(attrs={'readonly': 'readonly'})
)

应该修复它。

注意,
readonly
属性不会阻止Django处理客户端发送的任何值。如果值不改变对您来说很重要,那么无论您的用户多么有创意,您都需要使用更复杂的方法,例如,像Alex Gaynor在a中演示的那样,使用
ReadOnlyField
/
ReadOnlyWidget

,正如Benjamin()很好地解释的,除了正确渲染之外,您需要正确处理后端上的字段

有一个SO,它有许多好的解决方案。但无论如何:

1) 第一种方法-删除save()方法中的字段,例如(未测试;):

def保存(self,*args,**kwargs):
对于self.readonly_字段中的fname:
如果fname在self.u数据中:
del self.cleaned_数据[fname]
返回super(,self).save(*args,**kwargs)
2) 第二种方法-在清洁方法中将字段重置为初始值:

def clean_<fieldname>(self):
    return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
def清洁(自身):
返回self.initial[]#或getattr(self.instance,)
基于第二种方法,我将其概括如下:

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)
从functools导入部分
类别(…):
定义初始化(自…):
...
super(,self)。\uuuuu初始值(*args,**kwargs)
...
对于枚举(self.fields.iteritems())中的i(fname,field):
如果在self.readonly_字段中有fname:
field.widget.attrs['readonly']=“readonly”
field.required=False
#将“清除”方法设置为重置值
清除方法\u name=“清除%s”%fname
断言clean_方法_名称不在目录中(self)
setattr(self,clean\u方法\u名称,部分(self.\u clean\u用于\u readonly\u字段,fname=fname))
只读字段(self,fname)的def\u clean\u:
“”将值重置为初始值-不会更改任何内容
需要动态添加-部分,请参见初始化字段
"""
返回self.initial[fname]#或getattr(self.instance,fname)

我也遇到了同样的问题,所以我创建了一个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')

那看起来不安全!在jpic看来,用户可以禁用readonly标志并仍然提交信息。有关更全面的答案,请参见。这在django 1.9中发生了更改
class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')