«Отключено» опция для choiceField - Django - PullRequest
21 голосов
/ 23 марта 2009

У меня возникли проблемы с простым вопросом: Как иметь какое-то «отключенное» поле в выпадающем меню, созданное с помощью modelForm и choiceFied в django Framework?

В данный момент я не могу понять, как получить такой вывод: - Корень 1 запись - (отключено) - Elt 1 - (не отключен) - Root 2 entry - (отключено)

Есть ли у вас какие-либо советы?

Pierre

Ответы [ 7 ]

21 голосов
/ 24 марта 2009

Виджеты форм Django предлагают способ передать список атрибутов, которые должны отображаться в теге <option>:

my_choices = ( ('one', 'One'), ('two', 'Two'))
class MyForm(forms.Form):
    some_field = forms.ChoiceField(choices=my_choices, 
                                   widget=forms.Select(attrs={'disabled':'disabled'}))

К сожалению, это не будет работать для вас, потому что атрибут будет применен к КАЖДОМУ тегу опции, который отображается. У Django нет способа автоматически узнать, что должно быть включено, а что отключено.

В вашем случае я рекомендую написать собственный виджет. Это довольно легко сделать, и у вас нет особой логики для применения. Документы по этому вопросу здесь . Короче говоря:

  • подкласс forms.Select, который является выбранным по умолчанию средством визуализации
  • в вашем подклассе, реализуйте метод render(self, name, value, attrs). Используйте вашу собственную логику, чтобы определить, квалифицируется ли value как нуждающийся в отключении. Посмотрите на очень короткую реализацию render в django/forms/widgets.py, если вам нужно увлечение.

Затем определите поле формы, чтобы использовать собственный виджет:

class MyForm(forms.Form):
    some_field = forms.ChoiceField(choices=my_choices, 
                                   widget=MyWidget)
5 голосов
/ 26 декабря 2012

Вы можете создавать варианты, как упомянуто Брайаном, как это. В приведенных ниже параметрах Root 1, Root 2 автоматически отключаются, и они будут выглядеть как Параметры группы

CHOICES = (
  ('-- Root 1--', 
      (
        ('ELT1', 'ELT1'),
        ('ELT2', 'ELT2'),
        ('ELT3', 'ELT3'),
      )
   ),
  ('-- Root 2--', 
      (
        ('ELT3', 'ELT3'),
        ('ELT4', 'ELT4'),
      )
  ),
)

Вышеуказанная опция будет отображаться следующим образом. На изображении ниже, Root 1 и Root 2 не могут быть выбраны.

enter image description here

Надеюсь, это решит вашу проблему

-Vikram

4 голосов
/ 24 марта 2009

Кажется, django 1.1 разрешит "optgroup": Документация Django

class MyForm(forms.Form):
    some_field = forms.ChoiceField(choices=[
        ('Audio', (
                ('vinyl', 'Vinyl'),
                ('cd', 'CD'),
            )
        ),
        ('Video', (
                ('vhs', 'VHS Tape'),
                ('dvd', 'DVD'),
            )
        ),
        ('unknown', 'Unknown'),
    ])

Это имхо, должно быть.

3 голосов
/ 16 июля 2018
field_choices = (
        ('','Make choice'),
        (1,'first'),
        (2,'second'),
        (3,'third')
    )

from django.forms import Select

class Select(Select):
    def create_option(self, *args,**kwargs):
        option = super().create_option(*args,**kwargs)
        if not option.get('value'):
            option['attrs']['disabled'] = 'disabled'

        if option.get('value') == 2:
            option['attrs']['disabled'] = 'disabled'

        return option
3 голосов
/ 24 марта 2009

Вы пытаетесь создать меню, в котором элементы списка разделены на категории, и вы не хотите, чтобы сами категории можно было выбирать?

Если это так, вы можете добиться этого, если ваш шаблон визуализирует поле с помощью тегов, например,

<select name="my_field" id="id_my_field">
<optgroup label="-- Root 1 entry --">
    <option value="1">Elt 1</option>
    <option value="2">Elt 2</option>
    <option value="3">Elt 3</option>
</optgroup>
<optgroup label="--- Root 2 entry ---">
    <option value="4">Elt 4</option>
    <option value="5">Elt 5</option>
</optgroup>
</select>
1 голос
/ 26 марта 2019

Простые вопросы иногда имеют сложные ответы в Django. Я потратил много времени, чтобы заставить это работать хорошо. Объединяя обзор Джарретта с важной запиской от jnns о render_option и некоторой помощью #django по freenode, у меня есть хорошо работающее полное примерное решение:

Во-первых, в этом примере предполагается, что в модели, которую я называю Правило, обычно определен тип выбора CharField. Я подкласс своего TimeStampedModel, но вы можете использовать models.Model:

class Rule(TimeStampedModel):

    ...

    # Rule Type
    SHORT_TERM_RULE = 'ST'
    MAX_SIGHTINGS_PER_PERIOD_RULE = "MA"
    WHITE_LIST_RULE = "WL"
    BLACK_LIST_RULE = "BL"
    RULE_CHOICES = (
        (SHORT_TERM_RULE, 'Short Term Rule'),
        (MAX_SIGHTINGS_PER_PERIOD_RULE, 'Max Sightings Per Period Rule'),
        (WHITE_LIST_RULE, 'White List Rule'),
        (BLACK_LIST_RULE, 'Black List Rule'),
    )
    rule_type = models.CharField(
        max_length=2,
        choices=RULE_CHOICES,
        default=SHORT_TERM_RULE,
    )

    ...

В файле forms.py определите подкласс виджета Select, который принимает disabled_choices. У него есть пользовательский render_option(), который добавляет disabled к выводу html тегов параметров, если их выбор включен в список переданных в disabled_choices. Обратите внимание, я оставил большую часть кода render_option() из Select как есть:

class MySelect(Select):
    def __init__(self, attrs=None, choices=(), disabled_choices=()):
        super(MySelect, self).__init__(attrs, choices=choices)
        self.disabled_choices = disabled_choices

    def render_option(self, selected_choices, option_value, option_label):
        if option_value is None:
            option_value = ''
        option_value = force_text(option_value)
        if option_value in selected_choices:
            selected_html = mark_safe(' selected="selected"')
            if not self.allow_multiple_selected:
                selected_choices.remove(option_value)
        else:
            selected_html = ''
        for key, value in self.disabled_choices:
            if option_value in key:
                return format_html('<option disabled value="{}"{}>{}</option>', option_value, selected_html,
                                   force_text(option_label))
        return format_html('<option value="{}"{}>{}</option>', option_value, selected_html, force_text(option_label))

Затем, при определении подкласса формы ModelForm, проверьте переданный список disabled_choices и инициализируйте поле соответствующим образом. В этом примере я также выбираю вариант по умолчанию.

class RuleChoiceForm(ModelForm):
    class Meta:
        model = Rule
        fields = ['rule_type']

    # Add a default choice to the list defined in the Rule model
    default_choice = ('DF', 'Choose a Rule Type...')
    choices = list(Rule.RULE_CHOICES)
    choices.insert(0, default_choice)
    choices = tuple(choices)

    rule_type = forms.ChoiceField(widget=MySelect(attrs={'class': 'form-control'}, disabled_choices=[]),
                                  choices=choices)

    def __init__(self, *args, disabled_choices=None, **kwargs):
        super(RuleChoiceForm, self).__init__(*args, **kwargs)
        if disabled_choices:
            self.fields['rule_type'].widget.disabled_choices = disabled_choices

Затем, по вашему мнению, определите disabled_choices как список, добавив варианты из вашего _CHOICES var в вашу модель и передайте его в экземпляр формы. В моей логике я использую список RULE_CHOICES, чтобы получить кортеж выбора, который я хочу отключить. Хотя может быть более простой способ, пожалуйста, не стесняйтесь размещать предложения, чтобы упростить или улучшить этот ответ.

    disabled_choices = []
    # Logic disabling choices
    if Rule.objects.filter(route=route, rule_type=Rule.SHORT_TERM_RULE).exists():
        disabled_choices.append([item for item in Rule.RULE_CHOICES if Rule.SHORT_TERM_RULE in item][0])
        disabled_choices.append([item for item in Rule.RULE_CHOICES if Rule.MAX_SIGHTINGS_PER_PERIOD_RULE in item][0])

    rule_choice_form = RuleChoiceForm(disabled_choices=disabled_choices)
1 голос
/ 01 мая 2018

Это может быть поздний ответ, но это упрощенная версия, которую можно изменить с помощью экземпляра формы.

Вы можете передать список значений для отключения, т. Е.

def __init__(self, disabled_choices, *args, **kwargs):
        self.disabled_choices = disabled_choices

OR

from django.forms import Select


class SelectWidget(Select):
    """
    Subclass of Django's select widget that allows disabling options.
    """
    def __init__(self, *args, **kwargs):
        self._disabled_choices = []
        super(SelectWidget, self).__init__(*args, **kwargs)

    @property
    def disabled_choices(self):
        return self._disabled_choices

    @disabled_choices.setter
    def disabled_choices(self, other):
        self._disabled_choices = other

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        option_dict = super(SelectWidget, self).create_option(
            name, value, label, selected, index, subindex=subindex, attrs=attrs
        )
        if value in self.disabled_choices:
            option_dict['attrs']['disabled'] = 'disabled'
        return option_dict

Чтобы отключить опцию на основе условия, т. Е. Пользователь не является суперпользователем.

class MyForm(forms.Form):
    status = forms.ChoiceField(required=True, widget=SelectWidget, choices=Status.choices())

    def __init__(self, request, *args, **kwargs):
        if not request.user.is_superuser:
            self.fields['status'].widget.disabled_choices = [1, 4]
        super().__init__(*args, **kwargs)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...