Опция "Отключено" для выбораField - Django
У меня возникли проблемы с простым вопросом:
Как иметь какое-то "отключенное" поле в выпадающем меню, сгенерированном с помощью modelForm и choiceFied в django Framework?
В настоящий момент я не могу понять, как получить такой вывод:
- Вход в корень 1 - (отключен)
- Elt 1 - (не отключено)
- Запись Root 2 - (отключено)
Есть ли у вас какие-либо советы?
Пьер
Ответы
Ответ 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'}))
К сожалению, это не сработает для вас, потому что атрибут будет применен к какому-либо тегу опций EVERY. 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)
Ответ 2
Вы можете создавать варианты, как упомянуто Брайаном, как это. В приведенных ниже параметрах 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
Ответ 3
Кажется, 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'),
])
Это imho должно быть.
Ответ 4
Вы пытаетесь создать меню, в котором элементы списка разделены на категории, и вы не хотите, чтобы сами категории могли быть выбраны?
Если это так, вы можете добиться этого, если ваш шаблон визуализирует поле с помощью тегов, например.
<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>
Ответ 5
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
Ответ 6
Это может быть поздний ответ, но это упрощенная версия, которую можно изменить с помощью экземпляра формы.
Вы можете передать список значений, которые должны быть отключены, т.е.
def __init__(self, disabled_choices, *args, **kwargs):
self.disabled_choices = disabled_choices
ИЛИ ЖЕ
from django.forms import Select
class SelectWidget(Select):
"""
Subclass of Django 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):
if not request.user.is_superuser:
self.fields['status'].widget.disabled_choices = [1, 4]
super().__init__(*args, **kwargs)
Ответ 7
Простые вопросы иногда имеют сложные ответы в 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
as-is:
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 в вашу модель и _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)