【问题标题】:Add class to Django label_tag() output将类添加到 Django label_tag() 输出
【发布时间】:2010-09-29 16:54:31
【问题描述】:

我需要一些方法将类属性添加到表单字段的label_tag() 方法的输出中。

我看到可以传入attrs 字典,并且我已经在 shell 中对其进行了测试,我可以执行以下操作:

for field in form:
    print field.label_tag(attrs{'class':'Foo'})

我会在我的输出中看到class='Foo',但我看不到从模板中添加attrs 参数的方法——事实上,模板是专门针对这一点设计的,不是吗?

我的表单定义中有没有办法定义要在标签中显示的类?

在表单中,我可以执行以下操作来给输入一个类

self.fields['some_field'].widget.attrs['class'] = 'Foo'

我只需要让它输出<label /> 的类。

【问题讨论】:

标签: python django forms newforms


【解决方案1】:

custom template tag 似乎是解决方案。自定义过滤器也可以,尽管它可能不那么优雅。但在这两种情况下,您都需要回退到自定义表单呈现。

如果这是一项非常重要的任务;我将创建一个 Mixin,它允许我使用标签类注释表单字段并使用这些类提供表单呈现方法。这样下面的代码就可以工作了:

{{ form.as_table_with_label_classes }}

但我想问一下;你真的需要标签标签上的类吗?我的意思是 HTML 设计方面。是否绝对有必要在其中添加一个类?难道不能用一些CSS来解决吗:

encapsulating_selector label {
    some-attr: some-value;
}

我有时会在以下情况下使用jQuery如果它有效,它会改进页面,但如果它不起作用,它不会是一场灾难。并尽可能精简 HTML 源代码。

【讨论】:

  • Do you really need a class on the label tag?,是的,如果您使用的是引导程序或基础等框架。
【解决方案2】:

我同意第一个答案,使用 css 可以做到这一点,但是。 这是在 django 源中的原因是什么?

在 django.forms.forms.py 中有这个定义显示有代码在标签中显示 attrs

class BoundField(StrAndUnicode): 
    ...
    def label_tag(self, contents=None, attrs=None):
        contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))

_html_output 调用此函数时没有 attrs:

label = bf.label_tag(label) or ''

所以看起来 django 已经部分准备好了,但实际上它并没有使用它。

【讨论】:

【解决方案3】:

给forms.py的表单域添加CSS类怎么样,比如:

class MyForm(forms.Form):
    title = forms.CharField(widget=forms.TextInput(attrs={'class': 'foo'}))

然后我只是在模板中做以下事情:

<label for="id_{{form.title.name}}" class="bar">
    {{ form.title }}
</label>

当然,这可以很容易地修改为在模板中的 for 循环标记中工作。

【讨论】:

  • 您可以使用 {{ form.title.auto_id }} 而不是假设“id_”前缀。
【解决方案4】:
@register.simple_tag
def advanced_label_tag(field):
    """ Return form field label html marked to fill by `*` """
    classes = []
    attrs = {}
    contents = force_unicode(escape(field.label))

    if field.field.required:
        classes.append(u'required')
        contents = force_unicode('%s <span>*</span>'%escape(field.label))

    if classes:
        attrs['class'] = u' '.join(classes)

    return field.label_tag(contents=contents, attrs=attrs)

【讨论】:

    【解决方案5】:

    技巧1

    我不同意另一个答案的断言,即过滤器会“不那么优雅”。如您所见,它确实非常优雅。

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

    在模板中使用它同样优雅:

    {{ form.my_field|label_with_classes:"class1 class2"}}
    

    技巧2

    另外,我发现的一种更有趣的技术是:Adding * to required fields

    您为 BoundField.label_tag 创建一个装饰器,该装饰器将在适当设置 attrs 的情况下调用它。然后你猴子修补 BoundField 以便调用 BoundField.label_tag 调用装饰函数。

    from django.forms.forms import BoundField
    
    def add_control_label(f):
        def control_label_tag(self, contents=None, attrs=None):
            if attrs is None: attrs = {}
            attrs['class'] = 'control-label'
            return f(self, contents, attrs) 
        return control_label_tag
    
    BoundField.label_tag = add_control_label(BoundField.label_tag)    
    

    【讨论】:

    • 技术 1 在 Django 1.6 中给了我“AttributeError: 'SafeText' object has no attribute 'label_tag'”。你能查一下吗?
    • 是因为一个空格。写过滤器的时候去掉空格,之前和之后|
    • @AdamSilver 你找到解决方案了吗?
    • @Anuj 技术 1 类似 {{ form.password|label_with_classes:"col-sm-2 control-label" }} 工作正常。
    • 使用技巧 2,当我去管理员添加或编辑一个对象时,它会抛出一个 TypeError:control_label_tag() got an unexpected keyword argument 'label_suffix'
    【解决方案6】:
    class CustomBoundField(BoundField):
        def label_tag(self, contents=None, attrs=None):
            if self.field.required:
                attrs = {'class': 'required'}
            return super(CustomBoundField, self).label_tag(contents, attrs)
    
    class ImportViewerForm(forms.Form):
        url = fields.URLField(widget=forms.TextInput(attrs={'class': 'vTextField'}))
        type = fields.ChoiceField(choices=[('o', 'Organisation'), ('p', 'Program')], widget=forms.RadioSelect,
                                  help_text='Url contain infornation about this type')
        source = fields.ChoiceField(choices=[('h', 'hodex'), ('s', 'studyfinder')], initial='h', widget=forms.RadioSelect)
    
        def __getitem__(self, name):
            "Returns a BoundField with the given name."
            try:
                field = self.fields[name]
            except KeyError:
                raise KeyError('Key %r not found in Form' % name)
            return CustomBoundField(self, field, name)
    
    class Media:
        css = {'all': [settings.STATIC_URL + 'admin/css/forms.css']}
    

    你需要改变BoundField类的label_tag方法,并在form中使用

    【讨论】:

    • 这适用于普通表单。但是当我对 ModelForms 应用相同的修复时,我遇到了错误。
    【解决方案7】:

    有点晚了,但遇到了类似的问题。希望这对您有所帮助。

    class MyForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            super(MyForm, self).__init__(*args, **kwargs)
            self.fields['myfield1'].widget.attrs.update(
                {'class': 'form-control'})
            self.fields['myfield2'].widget.attrs.update(
                {'class': 'form-control'})
    
        def as_two_col_layout(self):
    
            return self._html_output(
                normal_row='<div class="form-group"><span class="col-xs-2">%(label)s</span> <div class="col-xs-10">%(field)s%(help_text)s</div></div>',
                error_row='%s',
                row_ender='</div>',
                help_text_html=' <span class="helptext">%s</span>',
                errors_on_separate_row=True)
    
        class Meta:
    
            model = mymodel
            fields = ['myfield1', 'myfield2']
    

    【讨论】:

    • 我尝试使用这种方法,但标签没有样式。你能告诉我你的模板吗?我的模板中有这个: {% for field in form %} {{ field.label_tag }}{{ field }} {% if field.help_text %} {{ field.help_text|safe }} {% endif %} { % endfor %} 好像没有应用html_output...
    • 或者只在as_table(), as_ul(), as_p()使用时使用?
    • @cwhisperer 在你的模板中你必须用作 form.as_two_col_layout 并且代码将完成这项工作。
    • 这很好用......我怎么说myfield1它是一个textarea?
    【解决方案8】:

    我们也可以使用 {{field.label}} 和 {{field.id_for_label}}

    &lt;label class="your_class_name" id="{{form.link.id_for_label}}"&gt;{{form.link.label}}&lt;/label&gt;

    以 HTML 格式呈现-

    <label class="your_class_name" id="id_name">Name</label>
    

    【讨论】:

      【解决方案9】:

      注意:希望即将推出的 Django 4.0 将使这一切变得更容易,因为它提供了template based form rendering

      在那之前:

      OP 要求一种在表单定义中使用BoundField.label_tag() 的方法。

      user240515user2732686 的回答确实提供了一些实施建议,但没有提供任何理由。 大多数其他基于自定义模板标签的解决方案都需要我们手动渲染表单字段,因此如果我们只是想使用{{ form }},它们就不起作用。

      因此,除了所有这些答案之外,这里还尝试提供更多背景信息。

      关于标签标签

      表单标签由BaseForm.as_table()as_ul()as_p()shortcuts通过“私有”BaseForm._html_output()方法呈现,如source所示。

      这是通过调用BoundField.label_tag() 来完成的,如here 所示。 label_tag() 方法采用 attrs 参数以及 &lt;label&gt; 标记的附加 HTML 属性。

      但是,问题在于BaseForm._html_output() 调用label_tag() 时没有attrs,并且没有简单的替代方法来设置attrs 参数。

      如何扩展label_tag?

      Django 的contrib.admin 通过在其AdminField 中扩展label_tag() 方法解决了这个问题,正如source 所清楚的那样。

      要扩展BoundField.label_tag(),我们可以create a customized BoundField

      class MyBoundField(forms.BoundField):
          def __init__(self, form, field, name, label_attrs=None):
              super().__init__(form, field, name)
              self.label_attrs = label_attrs
      
          def label_tag(self, contents=None, attrs=None, label_suffix=None):
              if attrs is None:
                  attrs = dict()
              attrs.update(self.label_attrs or {})
              return super().label_tag(contents, attrs, label_suffix)
      

      现在我们可以创建一个带有特定标签属性的绑定字段,但是我们如何处理它呢?

      如何使用自定义绑定字段?

      绑定字段使用forms.Field.get_bound_field() 进行实例化,因此,我们可以重写该方法以返回我们的自定义绑定字段:

      class MyField(forms.Field):
          # note typically we would use any of django's forms.Field subclasses
          def __init__(self, *args, **kwargs):
              # we could also set label_attrs here, based on the field properties
              self.label_attrs = kwargs.pop('label_attrs', None)
              super().__init__(*args, **kwargs)
      
          def get_bound_field(self, form, field_name):
              return MyBoundField(
                  form=form, field=self, name=field_name, label_attrs=self.label_attrs)
      

      然后我们可以使用Form上的自定义字段:

      class MyForm(forms.Form):
          some_field = MyField(..., label_attrs={'class': 'my-class'})
      

      但是如果我们想对所有的表单域都这样做呢?

      如何为所有表单字段使用自定义绑定字段?

      最后,Field.get_bound_field()BaseForm.__getitem__() 中被调用(见source)。这意味着我们可以通过调用例如得到一个绑定字段。 my_form['some_field'].

      为了对表单的所有字段使用我们的自定义绑定字段,我们可以为表单中的所有字段使用monkey patchfield.get_bound_field,或者我们可以覆盖表单的__getitem__()以忽略get_bound_field(),而是使用直接MyBoundField

      这是一个覆盖示例,它主要是 original source 的副本,MyBoundField 行除外:

      class MyForm(forms.Form):
          label_attrs = {'class': 'my-class'}
          
          def __getitem__(self, name):
              """Return a MyBoundField with the given name."""
              try:
                  field = self.fields[name]
              except KeyError:
                  ...  # left out for clarity
              if name not in self._bound_fields_cache:
                  self._bound_fields_cache[name] = MyBoundField(
                      form=self, field=field, name=name, label_attrs=self.label_attrs)
              return self._bound_fields_cache[name]
      

      最后,这一切似乎很多对于一些造型的麻烦。

      【讨论】:

        猜你喜欢
        • 2011-01-11
        • 2020-09-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-21
        • 1970-01-01
        • 1970-01-01
        • 2021-06-21
        • 1970-01-01
        相关资源
        最近更新 更多