8π
One way is using a custom Select widget which allows passing individual attributes in options, through the label
part of the widget choices:
(code from this great answer)
class SelectWithOptionAttribute(Select):
"""
Use a dict instead of a string for its label. The 'label' key is expected
for the actual label, any other keys will be used as HTML attributes on
the option.
"""
def create_option(self, name, value, label, selected, index,
subindex=None, attrs=None):
# This allows using strings labels as usual
if isinstance(label, dict):
opt_attrs = label.copy()
label = opt_attrs.pop('label')
else:
opt_attrs = {}
option_dict = super().create_option(name, value,
label, selected, index, subindex=subindex, attrs=attrs)
for key,val in opt_attrs.items():
option_dict['attrs'][key] = val
return option_dict
To populate the individual options override label_from_instance
method on a ModelChoiceField
subclass(see django docs):
IngredientChoiceField(ModelChoiceField):
"""ChoiceField with puts ingredient-type on <options>"""
# Use our custom widget:
widget = SelectWithOptionAttribute
def label_from_instance(self, obj):
# 'obj' will be an Ingredient
return {
# the usual label:
'label': super().label_from_instance(obj),
# the new data attribute:
'data-ingredient-type': obj.type
}
Finally, simple use this field in a form:
RecipeModelForm(ModelForm):
class Meta:
model = Recipe
fields = [
# other fields ...
'ingredients',
]
field_classes = {
'ingredients': IngredientChoiceField
}
4π
Why not render the fields manually?
Itβll be something like
<select>
{% for option in form.ingredient.choices %}
<option value="{{ option.id }}" data-ingredient-type={{ option.type }}>{{ option.name }}</option>
{% endfor %}
</select>
Or maybe in you model form class you add the attribute to it, but this must be a string (or probably a function)
widgets = { ...
'ingredients' = forms.Select(attrs={'data-ingredient-type': 'fruit'}),
...}
- Pycharm error: Improperly configured
- Celery immediately exceeds memory on Heroku
- Django: foreign key value in a list display admin
2π
This got a lot easier on the new versions of Django:
class SelectWithAttributeField(forms.Select):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
if value:
option["attrs"]["data-ingredient-type"] = value.instance. ingredient
return option
While at the Recipe model form use it as:
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = "__all__"
widgets = {
"ingredient": SelectWithAttributeField,
}
def __init__(self, *args, **kwargs):
super(RecipeForm, self).__init__(*args, **kwargs)
self.fields["ingredient"].queryset = Ingredient.objects.filter(
recipe=recipe
)
- Django make_password too slow for creating large list of users programatically
- How to access POST data inside tastypie custom Authentication
0π
My solution was to create a custom widget that overrides create_option()
:
class IngredientSelect(forms.Select):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
if value:
ingredient = models.Ingredient.objects.get(pk=value)
option['attrs'].update({
'data-type': ingredient.type
})
return option
Then you need to specify the widget to use for the ingredient field in your form:
class RecipeForm(forms.ModelForm):
class Meta:
model = models.Recipe
fields = '__all__'
widgets = {
'ingredient': IngredientSelect,
}
Thanks to In Django form, custom SelectField and SelectMultipleField to pointing me towards this solution.
Iβm not entirely satisfied with this solution because it assumes that value
is a pk
for Ingredient
and executes a direct database query to get the Ingredient
option. It seems like the model object should be available from the ModelChoiceField
, but I was unable to find a way to get it.
- How can I create a partial search filter in Django REST framework?
- Exposing django admin to users. Harmful?
- Query a template for the variables it needs?
- Issue with createsuperuser when implementing custom user model