Django创建一个只使用小部件读取的表单字段

时间:2009-12-01 16:58:46

标签: python django forms widget

我的表单字段如下所示:

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

    class Meta:
        model = Foo

使用上面的代码获取如下错误: init ()得到一个意外的关键字参数'widget'

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

4 个答案:

答案 0 :(得分:40)

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

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

替换为

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

应该修理它。

答案 1 :(得分:17)

请注意,readonly属性不会阻止Django处理客户端发送的任何值。如果您认为价值不会发生变化很重要,那么无论您的用户对FireBug有多么有创意,您都需要使用更具参与性的方法,例如一个ReadOnlyField / ReadOnlyWidget,如Alex Gaynor在blog entry中所示。

答案 2 :(得分: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')

答案 3 :(得分:1)

正如Benjamin(https://stackoverflow.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)第二种方法 - 在清洁方法中将字段重置为初始值:

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)