我有一个django模型' Recipe'将foreignkey字段添加到模型' Ingredient'。
在渲染表单时,我得到一个SELECT列表,其ID与成分ID相匹配,文本显示等于字段的字符串表示。
但是,我想在选择列表中添加一个数据属性,该数据属性与Ingredient查询集中的呈现选项相匹配。
例如,假设这是当前正在呈现的内容:
<option value="1158">Carrots</option>
<option value="1159">Strawberry</option>
<option value="1160">Onion</option>
<option value="1161">Spinach</option>
但我想为相关对象添加数据属性:
<option value="1158" data-ingredient-type="vegetable">Carrots</option>
<option value="1159" data-ingredient-type="fruit">Strawberry</option>
<option value="1160" data-ingredient-type="vegetable">Onion</option>
<option value="1161" data-ingredient-type="vegetable">Spinach</option>
答案 0 :(得分:3)
一种方法是使用自定义的Select小部件,该部件允许通过小部件选项的label
部分传递选项中的各个属性:
(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
要在label_from_instance
子类(see django docs)上填充各个选项覆盖ModelChoiceField
方法:
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
}
最后,以以下形式简单使用此字段:
RecipeModelForm(ModelForm):
class Meta:
model = Recipe
fields = [
# other fields ...
'ingredients',
]
field_classes = {
'ingredients': IngredientChoiceField
}
答案 1 :(得分:1)
为什么不render the fields manually?
它会像
<select>
{% for option in form.ingredient.choices %}
<option value="{{ option.id }}" data-ingredient-type={{ option.type }}>{{ option.name }}</option>
{% endfor %}
</select>
或者在你的模型表单类中你可以添加attribute,但这必须是一个字符串(或者可能是一个函数)
widgets = { ...
'ingredients' = forms.Select(attrs={'data-ingredient-type': 'fruit'}),
...}
答案 2 :(得分:1)
我的解决方案是创建一个覆盖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
然后,您需要指定用于表单中成分字段的小部件:
class RecipeForm(forms.ModelForm):
class Meta:
model = models.Recipe
fields = '__all__'
widgets = {
'ingredient': IngredientSelect,
}
感谢In Django form, custom SelectField and SelectMultipleField向我指出该解决方案。
我对这个解决方案并不完全满意,因为它假定value
是pk
的{{1}}并执行直接数据库查询以获取Ingredient
选项。看来应该可以从Ingredient
获得模型对象,但是我找不到找到它的方法。