choiceField的“禁用”选项 - Django

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了choiceField的“禁用”选项 - Django相关的知识,希望对你有一定的参考价值。

我遇到一个简单的问题:如何通过modelForm生成的下拉菜单中有一些“禁用”字段,并在django Framework中选择了什么?

目前,我无法弄清楚如何获得这样的输出: - 根1条目 - (禁用) - Elt 1 - (未禁用) - 根2条目 - (禁用)

你有什么建议吗?

答案

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无法自动知道哪些应该启用以及哪些应该被禁用。

在您的情况下,我建议编写自定义小部件。这很容易做到,而且你没有那么多的自定义逻辑可以应用。这方面的文件是here。简而言之:

  • 子类forms.Select,它是默认的选择渲染器
  • 在你的子类中,实现render(self, name, value, attrs)方法。使用您的自定义逻辑来确定value是否符合需要禁用的条件。如果您需要启发,请查看renderdjango/forms/widgets.py的非常短的实现。

然后,定义表单字段以使用自定义窗口小部件:

class MyForm(forms.Form):
    some_field = forms.ChoiceField(choices=my_choices, 
                                   widget=MyWidget)
另一答案

您可以像这样创建布莱恩所提到的选择。在以下选项Root 1中,Root 2会自动禁用,它们看起来像Group Options

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

以上选项将显示如下。在下图中,根1和根2不可选。

希望这能解决你的问题

-Vikram

另一答案

似乎django 1.1将允许“optgroup”:Django documentation

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

这个imho是必须的。

另一答案

您是否尝试创建一个菜单,其中列表项分为不同的类别,并且您不希望类别本身可选?

如果是这样,您可以通过让模板使用标记呈现字段来实现此目的,例如:

<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>
另一答案
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
另一答案

简单的问题有时在Django中有复杂的答案。我花了很多时间让这个工作得很好。结合Jarrett的概述和jnns关于render_option的重要说明以及来自frenode的#django的一些帮助我有一个完善的完整示例解决方案:

首先,这个例子假定我称之为Rule的模型中正常定义的选择类型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列表中,它会将disabled_choices添加到选项标签的html输出中。注意,我保留了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)
另一答案

这可能是一个迟到的答案,但这是一个可以使用表单实例修改的简化版本。

您可以传递要禁用的值列表,即

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

要么

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

以上是关于choiceField的“禁用”选项 - Django的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 表单 ChoiceField 中获取选项标签?

从 Django 表单 ChoiceField 中检索选定的选项

Django:ChoiceField 不返回选项

Django:如何在给定实际值和选项的情况下访问模板中 ChoiceField 的显示值?

Django Form 将 ChoiceField 选项设置为 Form 被调用

Django MultiValueField - 如何将选择传递给 ChoiceField?