Django: как добавить динамические классы для выбора параметров в форме

У меня есть ModelForm, который я отображаю в шаблоне с помощью django-widget-tweaks. У меня есть выбор ChoiceField, который выводит что-то вроде этого:

<select>
    <option value="object.id">object.name</option>
</select>

Но для целей манипулирования Javascript мне нужно следующее:

<select>
    <option value="object.id" class="object.otherPropery">object.name</option>
</select>

django-widget-tweaks имеет функцию add_class, но только добавляет класс в поле выбора, а не содержащиеся в нем параметры.

Я также попытался свернуть свое собственное поле выбора:

<select>
    {% for object in form.object_field %}
        <option value="{{ object.id }}" class="{{ object.otherProperty }}">{{ object.name }}</option>
    {% endfor %}
</select>

Но когда форма не проходит проверку и возвращаются ошибки, параметр, выбранный пользователем ранее, сбрасывается до значения по умолчанию (первого) (это действительно происходит с django-widget-tweaks).

Я также пытался переопределить поля render_option и render_options виджета Select, но дополнительные свойства объекта не передаются этим функциям.

Я чувствую, что есть простое решение для того, что я пытаюсь сделать. Любая помощь приветствуется!


person grokpot    schedule 03.09.2014    source источник


Ответы (3)


Я думаю, вы можете сделать что-то вроде этого (обратите внимание на тег «если»):

<select>
    {% with value=form.object_field.value %}
        {% for object in form.object_field %}
            <option 
                value="{{ object.id }}" 
                class="{{ object.otherProperty }}"
                {% if object.id == value %}selected="selected"{% endif %}
            >
                {{ object.name }}
            </option>
       {% endfor %}
    {% endwith %}
</select>

Хотя условие if может не привести к истинному результату, когда должно, если object.id является целым числом, потому что value всегда будет строкой для уже отправленной формы (все значения в данных запроса POST/GET являются строками). Но если значение исходит из «initial_data», оно будет целым числом.

В таких случаях вы можете обновить свой тег if следующим образом:

{% if object.id|slugify == value|slugiy %}selected="selected"{% endif %}

Делаем окончательный код следующим образом:

<select>
    {% with value=form.object_field.value %}
        {% for object in form.object_field %}
            <option 
                value="{{ object.id }}" 
                class="{{ object.otherProperty }}"
                {% if object.id|slugify == value|slugify %}selected="selected"{% endif %}
            >
                {{ object.name }}
            </option>
       {% endfor %}
    {% endwith %}
</select>
person Debanshu Kundu    schedule 05.09.2014

Вы можете попробовать это.

render_option был удален из Django 1.11 и выше. Это то, что я сделал, чтобы добиться этого. Немного копания, и это кажется простым. Работает с Джанго 2.0+

class CustomSelect(forms.Select):
    def __init__(self, attrs=None, choices=()):
        self.custom_attrs = {}
        super().__init__(attrs, choices)

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        index = str(index) if subindex is None else "%s_%s" % (index, subindex)
        if attrs is None:
            attrs = {}
        option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
        if selected:
            option_attrs.update(self.checked_attribute)
        if 'id' in option_attrs:
            option_attrs['id'] = self.id_for_label(option_attrs['id'], index)

        # setting the attributes here for the option
        if len(self.custom_attrs) > 0:
            if value in self.custom_attrs:
                custom_attr = self.custom_attrs[value]
                for k, v in custom_attr.items():
                    option_attrs.update({k: v})

        return {
            'name': name,
            'value': value,
            'label': label,
            'selected': selected,
            'index': index,
            'attrs': option_attrs,
            'type': self.input_type,
            'template_name': self.option_template_name,
        }


class MyModelChoiceField(ModelChoiceField):

    # custom method to label the option field
    def label_from_instance(self, obj):
        # since the object is accessible here you can set the extra attributes
        if hasattr(obj, 'type'):
            self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
        return obj.get_display_name()

Форма:

class BookingForm(forms.ModelForm):

    customer = MyModelChoiceField(required=True,
                                  queryset=Customer.objects.filter(is_active=True).order_by('name'),
                                  widget=CustomSelect(attrs={'class': 'chosen-select'}))

Вывод, который мне нужен, выглядит следующим образом:

  <select name="customer" class="chosen-select" required="" id="id_customer">
      <option value="" selected="">---------</option>
      <option value="242" type="CNT">AEC Transcolutions Private Limited</option>
      <option value="243" type="CNT">BBC FREIGHT CARRIER</option>
      <option value="244" type="CNT">Blue Dart Express Limited</option>
person Nihal Sharma    schedule 29.03.2018

Я думаю, что есть проблемы с добавлением классов или чего-либо еще к элементам option. Я точно знаю, что применяется для CSS, см., например, этот другой пост: Можем ли мы добавить атрибут класса в элемент option? Думаю, то же самое можно применить и к управлению им с помощью JavaScript.

Это может быть немного сложно, но, возможно, в шаблоне нужно написать какой-нибудь JavaScript, который сопоставляет {{ object.id }} с {{ object.otherProperty }}, чтобы таким образом у вас был доступ к значению.

person Michael Buckley    schedule 03.09.2014