如何将数据属性添加到django modelform modelchoicefield

时间:2016-08-14 17:35:54

标签: python django django-forms

我有一个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>

3 个答案:

答案 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向我指出该解决方案。

我对这个解决方案并不完全满意,因为它假定valuepk的{​​{1}}并执行直接数据库查询以获取Ingredient选项。看来应该可以从Ingredient获得模型对象,但是我找不到找到它的方法。