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