将条件 class 添加到 Django 字段的标签

Adding a conditional class to a Django field's label

我正在尝试重构手动呈现字段的 Django 模板(参见 https://docs.djangoproject.com/en/2.0/topics/forms/#rendering-fields-manually)。标签生成如下:

  <label for="{{ field.id_for_label }}"
      class="{% if field.value %}active{% endif %} {% if field.errors %}invalid{% endif %}">
  </label>

其中 field 循环使用 {% for field in form %} ... {% endfor %}.

我正在尝试通过编写自定义过滤器来重构它(参见 https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#writing-custom-template-filters)。到目前为止,我已经想出了以下内容。在 templatetags 目录中,我添加了一个 label_with_classes.py ,内容为

from django import template

register = template.Library()

@register.filter(is_safe=True)
def label_with_classes(value, arg):
    return value.label_tag(attrs={'class': arg})

我用

替换上面的 HTML
  {{ field|label_with_classes:"active"}}

问题是这实际上并没有做原始模板做的事情;它总是用 class "active" 标记它并且不实现条件逻辑。

我的问题:是否可以使用过滤器实现此逻辑?过滤器函数的 value 输入参数实际上代表什么,它是 field.value(顾名思义)还是 field 本身?

通过在开发服务器 运行 时进入调试器并刷新页面,我发现 value 实际上是 BoundField 的一个实例,它有一个 value() 方法和一个 errors 属性:

> /Users/kurtpeek/Documents/Dev/lucy/lucy-web/dashboard/templatetags/label_with_classes.py(8)label_with_classes()
      7     import ipdb; ipdb.set_trace()
----> 8     return value.label_tag(attrs={'class': arg})
      9 

ipdb> value
<django.forms.boundfield.BoundField object at 0x113957eb8>
ipdb> dir(value)
['__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'as_hidden', 'as_text', 'as_textarea', 'as_widget', 'auto_id', 'build_widget_attrs', 'css_classes', 'data', 'errors', 'field', 'form', 'help_text', 'html_initial_id', 'html_initial_name', 'html_name', 'id_for_label', 'initial', 'is_hidden', 'label', 'label_tag', 'name', 'subwidgets', 'value']
ipdb> value.errors
[]
ipdb> value.value
<bound method BoundField.value of <django.forms.boundfield.BoundField object at 0x113957eb8>>
ipdb> value.value()
4

我对变量 value 的使用感到有点困惑,并已将虚拟变量重命名为 bound_field

以下是我如何实现自定义过滤器来实现条件 类(在 templatetags/label_with_classes.py 中):

from django import template

register = template.Library()


@register.filter(is_safe=True)
def label_with_classes(bound_field):
    classes = f"{'active' if bound_field.value() else ''} {'invalid' if bound_field.errors else ''}"
    return bound_field.label_tag(attrs={'class': classes.strip()})

之后 <label> 元素可以在模板中替换为

{% load label_with_classes %}

{% for field in form %}
  {{ field|label_with_classes }}
{% endfor %}