【问题标题】:"Disabled" option for choiceField - Django选择字段的“禁用”选项 - Django
【发布时间】:2009-03-23 12:30:02
【问题描述】:

我遇到了一个简单的问题: 如何在 django 框架中通过 modelForm 和 choiceFied 生成的下拉菜单中有一些“禁用”字段?

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

你有什么建议吗?

皮埃尔

【问题讨论】:

标签: django


【解决方案1】:

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 是否符合需要禁用的条件。如果您需要灵感,请查看 django/forms/widgets.pyrender 的简短实现。

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

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

【讨论】:

  • 这是一个非常好的解决方案,但更改选项属性的正确方法是 render_option() 而不是 render()(呈现整个小部件)
  • 记得也要实现相应的字段验证。如果您不想使用该选项,仅禁用 HTML 中的选项是不够的。
  • 这提供了一个良好的开端,尽管我在下面的解决方案中大大扩展了这个答案:stackoverflow.com/questions/673199/…
【解决方案2】:

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

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

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

根据条件禁用选项,即用户不是超级用户。

class MyForm(forms.Form):
    status = forms.ChoiceField(required=True, widget=SelectWidget, choices=(('on', 'On'), ('off', 'Off')))

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)

        if not request.user.is_superuser:
            self.fields['status'].widget.disabled_choices = ['off']

【讨论】:

【解决方案3】:

您可以像这样创建 Bryan 提到的选择。在下面的选项中,Root 1、Root 2 被自动禁用,它们看起来像 Group Options

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

上面的选项会这样显示。在下图中,Root 1 和 Root 2 不可选。

希望这能解决您的问题

-维克拉姆

【讨论】:

    【解决方案4】:
    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
    

    【讨论】:

    • 很好的解决方案,我唯一改变的是option['attrs']['disabled'] = True,因为这会使属性出现没有赋值,例如只是disabled而不是disabled="disabled"
    【解决方案5】:

    在 Django 中,简单的问题有时会有复杂的答案。我花了很多时间让它正常工作。结合 Jarrett 的概述与 jnns 关于render_option 的重要说明以及#django on freenode 的一些帮助,我有一个运行良好的完整示例解决方案:

    首先,此示例假定在我称为 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 中,定义接受disabled_choices 的小部件子类Select。它有一个自定义的render_option(),如果选项标签的选择包含在传入的disabled_choices 列表中,则将disabled 添加到选项标签的html 输出中。请注意,我将Select 中的大部分render_option() 代码原样保留:

    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 变量中的选项附加到您的模型中,并将其传递给表单实例化。在我的逻辑中,我使用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)
    

    【讨论】:

      【解决方案6】:

      似乎 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'),
          ])
      

      这个恕我直言是必须的。

      【讨论】:

        【解决方案7】:

        您是否尝试创建一个菜单,其中列表项被分成多个类别,并且您不希望这些类别本身是可选择的?

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

        <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>
        

        【讨论】:

          猜你喜欢
          • 2010-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-30
          • 2015-07-17
          • 1970-01-01
          • 2011-02-13
          相关资源
          最近更新 更多