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

cor*_*ory 6 python django django-forms

我有一个带有外键字段的 django 模型形式“食谱”,其中包含模型“成分”的外键字段。

渲染表单时,我得到一个 SELECT 列表,该列表的 ID 与成分 ID 匹配,文本显示等于字段的字符串表示形式。

但是,我想将数据属性添加到与成分查询集中呈现的选项相匹配的选择列表。

例如,假设这是当前正在呈现的内容:

<option value="1158">Carrots</option>
<option value="1159">Strawberry</option>
<option value="1160">Onion</option>
<option value="1161">Spinach</option>
Run Code Online (Sandbox Code Playgroud)

但我想为相关对象添加一个数据属性:

<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>
Run Code Online (Sandbox Code Playgroud)

mgl*_*art 8

一种方法是使用自定义选择小部件,它允许通过label小部件选择的一部分传递选项中的各个属性:( 来自这个伟大答案的代码)

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
Run Code Online (Sandbox Code Playgroud)

要填充子类label_from_instance上的各个选项覆盖方法(请参阅 django 文档)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
    }
Run Code Online (Sandbox Code Playgroud)

最后,简单地在表单中使用该字段:

RecipeModelForm(ModelForm):

class Meta:
    model = Recipe
    fields = [
        # other fields ...
        'ingredients',
    ]
    
    field_classes = {
        'ingredients': IngredientChoiceField
    }
Run Code Online (Sandbox Code Playgroud)


Res*_*ues 4

为什么不手动渲染字段
会是这样的

<select>
  {% for option in form.ingredient.choices %}
     <option value="{{ option.id }}" data-ingredient-type={{ option.type }}>{{ option.name }}</option>
  {% endfor %}
</select>  
Run Code Online (Sandbox Code Playgroud)

或者也许在模型表单类中添加属性但这必须是一个字符串(或者可能是一个函数)

widgets = { ...
     'ingredients' = forms.Select(attrs={'data-ingredient-type': 'fruit'}),
   ...}
Run Code Online (Sandbox Code Playgroud)