【问题标题】:Django creating a form field that's read only using widgetsDjango使用小部件创建一个只读的表单字段
【发布时间】: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'

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

【问题讨论】:

标签: python django forms widget


【解决方案1】:

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

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

替换为

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

应该修复它。

【讨论】:

  • 看起来不安全!
  • 就 jpic 而言,用户可以禁用只读标志并仍然提交信息。请参阅stackoverflow.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://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) 第二种方法 - 在 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)
      

      【讨论】:

        猜你喜欢
        • 2011-02-25
        • 2014-03-12
        • 1970-01-01
        • 2015-02-03
        • 2017-05-07
        • 2015-10-06
        • 1970-01-01
        • 2018-03-08
        • 2021-10-14
        相关资源
        最近更新 更多