Добавить класс в вывод Django label_tag()

Мне нужен какой-то способ добавить атрибут класса к выводу метода label_tag() для поля формы.

Я вижу, что есть возможность передать в словарь attrs, я проверил это в оболочке, и я могу сделать что-то вроде:

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

Я увижу class='Foo' в своем выводе, но я не вижу способа добавить аргумент attrs из шаблона - на самом деле, шаблоны разработаны специально для этого, нет?

Есть ли способ в моем определении формы, чтобы определить класс, который будет отображаться в метке?

В форме я могу сделать следующее, чтобы дать входам класс

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

Мне просто нужно, чтобы он выводил класс для.

10 ответов

Решение

Пользовательский тег шаблона, кажется, является решением. Также подойдет пользовательский фильтр, хотя он может быть и менее элегантным. Но вам нужно будет вернуться к пользовательской визуализации формы в обоих случаях.

Если это задача высокой важности; Я бы создал Mixin, который позволял бы мне аннотировать поля формы с помощью классов меток и предоставлять методы рендеринга форм с использованием этих классов. Так что следующий код работает:

{{ form.as_table_with_label_classes }}

Но я хотел бы спросить; Вам действительно нужен класс на теге label? Я имею в виду HTML-дизайн. Там абсолютно необходимо добавить класс? Разве это не может быть решено с помощью некоторых CSS, как:

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

Я иногда использую jQuery для таких случаев, где; это улучшит страницу, если она будет работать, но это не будет катастрофой, если это не так. И сохраняйте исходный код HTML как можно меньше.

Техника 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

В качестве альтернативы, один из наиболее интересных методов, которые я нашел, это добавление * в обязательные поля.

Вы создаете декоратор для BoundField.label_tag, который будет вызывать его с соответствующими атрибутами. Затем вы делаете патч 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)    

Как насчет добавления класса CSS в поле формы в forms.py, например:

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 в шаблоне.

Я согласен с ответом номер один, с CSS это можно сделать, но. Какой резон для этого должен быть в источнике django?

В django.forms.forms.py есть это определение, которое показывает, что есть код для отображения атрибутов в ярлыках:

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 вызывает эту функцию без атрибутов:

label = bf.label_tag(label) or ''

Таким образом, кажется, что Джанго частично подготовлен к этому, но на самом деле он не использовал его.

Слишком поздно, но столкнулся с аналогичной проблемой. Надеюсь, это поможет вам.

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']
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']}

Вам нужно изменить метод label_tag в классе BoundField и использовать его в форме

Мы также можем использовать {{field.label}} и {{field.id_for_label}}

<label class="your_class_name" id="{{form.link.id_for_label}}">{{form.link.label}}</label>

Визуализировать в HTML как-

<label class="your_class_name" id="id_name">Name</label>
@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)

Попробуйте django-widget-tweaks

      $ pip install django-widget-tweaks

Добавьте его в INSTALLED_APPS в файле settings.py вашего проекта:

      INSTALLED_APPS += [
'widget_tweaks',
]

Используйте фильтр add_label_class:

      {% load widget_tweaks %}
{{ form.your_field|add_label_class:"label" }}
 # equal to
<label class="label" for="{{ form.your_field.id_for_label }}">{{ form.your_field.label }}</label>

Прочтите документ, чтобы получить дополнительную информацию и функции.

OP уже упоминает, что использование , вероятно, является .

Ответы user240515 и user2732686 содержат некоторые предложения по реализации, но ни один из них не дает никакого обоснования. Большинство других решений, основанных на настраиваемых тегах шаблонов, требуют, чтобы мы отображали поля формы вручную, поэтому они не работают, если мы просто хотим использовать {{ form }}.

Таким образом, в дополнение ко всем этим ответам, вот попытка предоставить еще немного предыстории.

Зачем использовать label_tag?

Ярлыки форм отображаются BaseForm.as_table(), as_ul(), или as_p() ярлыки с помощью "частного" метода, как видно из источника .

Это делается путем звонка, как можно увидеть здесь . Метод BoundField.label_tag()label_tag()правильным решением принимает аргумент с дополнительными атрибутами HTML для <label> ярлык.

Как расширить label_tag?

Однако проблема в том, что BaseForm._html_output() звонков без, и нет простой альтернативы для настройки attrs аргумент.

Джанго contrib.admin решает эту проблему, расширяя label_tag() метод в своем AdminField, как становится ясно из источника .

Расширить BoundField.label_tag(), мы можем создать настраиваемый 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 get_bound_field(self, form, field_name, label_attrs=None):
        # we could also set label_attrs here, based on the field properties
        return MyBoundField(
            form=form, field=self, name=field_name, label_attrs=label_attrs)

Затем мы можем использовать настраиваемое поле на нашем Form:

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

Но что, если мы хотим сделать это для всех полей формы?

Как это сделать для всех полей?

В конце концов, Field.get_bound_field() называется в BaseForm.__getitem__()(см. источник ). Это означает, что мы можем получить связанное поле, вызвав, например, my_form['some_field'].

Чтобы использовать наше настраиваемое связанное поле для всех полей формы, мы могли бы либо заплатить обезьяну field.get_bound_field для всех полей в форме, или мы могли бы переопределить форму __getitem__() игнорировать get_bound_field() а вместо этого используйте напрямую.

Вот пример переопределения, которое в основном является копией исходного источника , за исключением 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]

В конце концов, все это кажется много неприятностей для немного стиля.

Другие вопросы по тегам