【问题标题】:How to add data-attribute to django modelform modelchoicefield如何将数据属性添加到 django modelform modelchoicefield
【发布时间】:2016-08-14 17:35:53
【问题描述】:

我有一个 django modelform 'Recipe',其外键字段指向模型'Ingredient'。

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

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

例如,假设这是当前正在渲染的内容:

<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>

【问题讨论】:

    标签: python django django-forms


    【解决方案1】:

    一种方法是使用自定义 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
    

    要填充各个选项,请覆盖 ModelChoiceField 子类(see django docs) 上的 label_from_instance 方法:

    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
        }
    

    【讨论】:

      【解决方案2】:

      为什么不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'}),
         ...}
      

      【讨论】:

      • 你会把模板代码放在哪里?在表单模板中?如果你正在做类似form.as_p 的事情怎么办?所以现在我必须手动渲染所有字段?
      • 第二个代码sn -p是给每个&lt;option&gt;添加一个数据属性还是给&lt;select&gt;添加一个数据属性?
      • 很抱歉,我之前没有看到这个,但我现在看到你已经有了解决方案
      • 不用担心。谢谢你的回答。
      【解决方案3】:

      我的解决方案是创建一个覆盖 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 为我指明此解决方案。

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

      【讨论】:

        猜你喜欢
        • 2011-05-19
        • 2011-12-28
        • 1970-01-01
        • 1970-01-01
        • 2017-03-07
        • 2012-06-05
        • 2012-09-12
        • 1970-01-01
        相关资源
        最近更新 更多