【问题标题】:Render form errors with the label rather than field name使用标签而不是字段名称呈现表单错误
【发布时间】:2013-05-04 09:04:06
【问题描述】:

我想在模板中使用 {{ form.errors }} 列出所有表单错误。这会生成表单字段列表和每个字段的错误嵌套列表。但是,使用字段的文字名称。生成的特定字段中有错误的 html 可能如下所示。

<ul class="errorlist">
    <li>
        target_date_mdcy
        <ul class="errorlist">
            <li>This field is required.</li>
        </ul>
    </li>
</ul>

我想使用错误列表功能,因为它既好用又简单。但是,我想使用标签(例如“目标日期”)而不是字段名称。实际上,我想不出您希望为网页用户显示字段名称的情况。有没有办法将渲染的错误列表与字段标签一起使用?

【问题讨论】:

    标签: python django django-forms


    【解决方案1】:

    我没有看到一个简单的方法来做到这一点。

    表单的errors 属性实际上返回一个ErrorDict,一个在django.forms.utils 中定义的类——它是dict 的一个子类,它知道将自身的ul 渲染生成为其unicode 表示。但是键实际上是字段名称,这对于维护其他行为很重要。因此,它无法轻松访问字段标签。

    您可以定义一个接受表单的自定义模板标签来生成您喜欢的渲染,因为在 Python 代码中很容易获得给定表单和字段名称的字段标签。或者您可以在视图中按标签构建错误列表,将其添加到您的上下文中,然后使用它。

    编辑 或者,您可以遍历字段并检查它们的各个错误,记住也要显示non_field_errors。比如:

    <ul class="errorlist">
      {% if form.non_field_errors %}
        <li>{{ form.non_field_errors }}</li>
      {% endif %}
      {% for field in form %}
        {% if field.errors %}
          <li>
            {{ field.label }}
            <ul class="errorlist">
              {% for error in field.errors %}
                <li>{{ error }}</li>
              {% endfor %}
            </ul>
          </li>
        {% endif %}
      {% endfor %}
    </ul>
    

    您可能还想将 non_field_errors 包装在一个列表中,具体取决于。

    【讨论】:

    • 谢谢,彼得。我不知道ErrorDict。我选择了自定义过滤器,因为我可能会重复使用它。
    【解决方案2】:

    我知道这个问题已经得到解答,但是我遇到了同样的场景,发现有一种使用标签的简单方法:

    {% if form.errors %}
        <ul class="user-msg error">
        {% for field in form %}
            {% for error in field.errors %}
                <li>
                  {% if field != '__all__' %}
                    <strong>{{ field.label }}:</strong>
                  {% endif %}
                  {{ error }}
                </li>
            {% endfor %}
        {% endfor %}
        </ul>
    {% endif %}
    

    【讨论】:

    • 谢谢,这很有帮助。
    • 很好的答案,django docs 在这方面也很有帮助。
    【解决方案3】:

    我在一个自定义表单类中解决了这个问题,我的所有表单都继承而不是django.forms.Form。在那里,我通过返回一个自定义的ErrorDict 来改变form.errors 的工作方式,其as_ul 方法将标签考虑在内。因此,您无需更改模板,但您需要让表单继承 CustomBaseForm

    class CustomErrorDict(ErrorDict):
        def __init__(self, form, iterable=None, **kwargs):
            self.form = form
            super(CustomErrorDict, self).__init__(iterable, **kwargs)
    
        def as_ul(self):
            if not self:
                return u''
    
            def humanify(field_name):
                try:
                    return self.form.fields[field_name].label or field_name
                except:
                    return field_name
    
            # main logic is copied from the original ErrorDict:
            return mark_safe(u'<ul class="errorlist">%s</ul>'
                    % ''.join([u'<li>%s%s</li>' % (humanify(k), force_unicode(v))
                        for k, v in self.items()]))
    
    class CustomBaseForm(forms.Form):
        @property
        def errors(self):
            return CustomErrorDict(self, super(forms.Form, self).errors)
    
        ... rest of CustomBaseForm ...
    

    【讨论】:

      【解决方案4】:
      from django import forms
      
      def my_clean(self):
          self.my_errors = ''
          for x in self.visible_fields():
              if x.errors:
                  self.my_errors += "<p>%s: %s</p>" % (x.label, x.errors)
      
      
      class SetPwdForm(forms.Form):
          pwd= forms.CharField(label='password', required=True, min_length=6)
          def clean(self):
              ...
              my_clean(self)
      

      在视图中使用 myform.my_errors

      【讨论】:

        【解决方案5】:

        以防万一有人想在 django.contrib.messages 框架中执行类似的操作,例如 FormView

        def form_invalid(self, form):
            for field, errors in form.errors.items():
                for error in errors:
                    messages.error(
                        self.request,
                        form.fields[field].label + ": " + error
                    )
        

        请注意,这只是一个基本模板,在 form.fields[field] 没有意义的情况下,您必须注意代码中的非字段错误等。

        【讨论】:

          【解决方案6】:

          以下方法显示verbose_name 而不是字段名称。 也可以在get_context_data() 中使用,但我个人更喜欢这种方式:

          from django.core.exceptions import FieldDoesNotExist
          
          
          class ShowVerboseNameInFormsMixin:
          
              def add_error(self, field, error):
                  super(ShowVerboseNameInFormsMixin, self).add_error(field, error)
                  for field, message in self._errors.copy().items():
                      try:
                          verbose_name = self._meta.model._meta.get_field(field).verbose_name
                          del self._errors[field]
                          self._errors[verbose_name] = self.error_class()
                          self._errors[verbose_name].extend(message)
                      except FieldDoesNotExist:
                          pass
          
          

          然后像这样使用它:

          from django import forms
          
          
          class FooForm(ShowVerboseNameInFormsMixin, forms.ModelForm):
              class Meta:
                  model = Foo
                  fields = ['foo', 'bar', 'baz']
          

          加上一点额外的代码,它可以显示__all__all 或任何其他预期的字符串。

          【讨论】:

            【解决方案7】:

            根据 Peter 的建议,这是我用来呈现带有字段标签的错误列表的过滤器。

            from django.utils.safestring import mark_safe
            from django.template import Library, Context, loader
            register = Library()
            
            @register.filter
            def form_error_list(form):
            
                result = ""    
                if form.errors:
                    fields = form.fields
                    error_items = []
                    for error_field in form.errors:
                        label = fields[error_field].label
                        if not label:
                            label = ' '.join([word.capitalize() for word in error_field.split('_')])
                        errors = form.errors[error_field]
                        error_li = ''.join(['<li>{0}</li>'.format(error) for error in errors])
                        error_items.append({'label': label, 'error_li': error_li})    
                    inner = ''.join(['<li>{0}<ul class="errorlist">{1}</ul></li>'.format(item['label'], item['error_li']) for item in error_items])
                    result = mark_safe('<ul class="errorlist">{0}</ul>'.format(inner))
            
                return result
            

            【讨论】:

            • 如果您有任何非字段错误,这将引发异常 - 对于完整的一般情况,您需要考虑form.errors 中的键实际上不是字段名称的可能性。现在我查看代码,ErrorDict 的值是ErrorList 的实例,该类有一个as_ul 方法,可以构造你的error_li 方法和周围的ul 元素,带有转义启用 - 所以你可以做error_li = errors.as_ul()
            猜你喜欢
            • 2022-07-22
            • 1970-01-01
            • 1970-01-01
            • 2019-12-09
            • 2017-05-22
            • 1970-01-01
            • 2011-11-26
            • 1970-01-01
            • 2017-12-06
            相关资源
            最近更新 更多