【问题标题】:Django MultiValueField - How to pass choices to ChoiceField?Django MultiValueField - 如何将选择传递给 ChoiceField?
【发布时间】:2010-12-09 16:50:23
【问题描述】:

我有一个带有字符域和选择域的多值域。我需要将选项传递给choicefield 构造函数,但是当我尝试将它传递给我的自定义多值字段时,我收到错误__init__() got an unexpected keyword argument 'choices'。

我知道其余代码可以正常工作,因为当我从 __init__ 和 super 中删除选项关键字参数时,多值字段正确显示但没有任何选项。

这就是我设置自定义多值字段的方式:

class InputAndChoice(object):
    def __init__(self, text_val='', choice_val=''):
        self.text_val=text_val
        self.choice_val=choice_val

class InputAndChoiceWidget(widgets.MultiWidget):
    def __init__(self, attrs=None):
        widget = (widgets.TextInput(),
                  widgets.Select()
                 )
        super(InputAndChoiceWidget, self).__init__(widget, attrs=attrs)

    def decompress(self,value):
        if value:
            return [value.text_val, value.choice_val]
        return [None, None]


class InputAndChoiceField(forms.MultiValueField):
    widget = InputAndChoiceWidget

    def __init__(self, required=True, widget=None, label=None, initial=None,
                 help_text=None, choices=None):
        field = (
                 fields.CharField(),
                 fields.ChoiceField(choices=choices),
                 )
        super(InputAndChoiceField, self).__init__(fields=field, widget=widget, 
              label=label, initial=initial, help_text=help_text, choices=choices)

我这样称呼它:

input_and_choice = InputAndChoiceField(choices=[(1,'first'),(2,'second')])

那么如何将选项传递给我的 ChoiceField 字段?

编辑:

我已经尝试了 stefanw 的建议,但仍然没有运气。我使用 logging.debug 在 init 的末尾打印出 InputAndChoiceField 的内容,self.fields[1].choices 包含正确的值,但它不会在浏览器中显示任何选项。

【问题讨论】:

    标签: django django-forms django-widget django-models


    【解决方案1】:

    我遇到了同样的问题并这样解决:

    class InputAndChoiceWidget(widgets.MultiWidget):
        def __init__(self,*args,**kwargs):
            myChoices = kwargs.pop("choices")
            widgets = (
                widgets.TextInput(),
                widgets.Select(choices=myChoices)
            )
            super(InputAndChoiceWidget, self).__init__(widgets,*args,**kwargs)
    
    class InputAndChoiceField(forms.MultiValueField):
        widget = InputAndChoiceWidget
    
        def __init__(self,*args,**kwargs):
            # you could also use some fn to return the choices;
            # the point is, they get set dynamically 
            myChoices = kwargs.pop("choices",[("default","default choice")])
            fields = (
                fields.CharField(),
                fields.ChoiceField(choices=myChoices),
            )
            super(InputAndChoiceField,self).__init__(fields,*args,**kwargs)
            # here's where the choices get set:
            self.widget = InputAndChoiceWidget(choices=myChoices)
    

    向小部件的构造函数添加一个“选择”kwarg。然后在字段创建后显式调用构造函数。

    【讨论】:

    • 这个例子将导致InputAndChoiceWidget.__init__() 被调用两次,并且由于 kwars.pop 你会得到一个 keyError 异常。我通过删除 widget = InputAndChoiceWidget 行解决了这个问题
    【解决方案2】:

    ModelChoiceField is technically a ChoiceField,但它实际上并没有使用任何ChoiceField 的实现。所以,这就是我的使用方法。

    class ChoiceInputMultiWidget(MultiWidget):
        """Kindly provide the choices dynamically"""
        def __init__(self, attrs=None):
            _widget = (
                Select(attrs=attrs),
                TextInput(attrs=attrs)
            )
            super().__init__(_widget, attrs)
    
    class ModelChoiceInputField(MultiValueField):
        widget = ChoiceInputMultiWidget
    
        def __init__(self, *args, **kwargs):
    
            _fields = (
                ModelChoiceField(queryset=Type.objects.all()),
                CharField()
            )
            super().__init__(_fields, *args, **kwargs)
    
            # Use the auto-generated widget.choices by the ModelChoiceField
            self.widget.widgets[0].choices = self.fields[0].widget.choices
    

    【讨论】:

    • 正是我想要的,谢谢!也比上面的一些答案更好,因为它不需要更改小部件。
    【解决方案3】:

    看看forms.MultiValueField__init__的出处:

    def __init__(self, fields=(), *args, **kwargs):
        super(MultiValueField, self).__init__(*args, **kwargs)
        # Set 'required' to False on the individual fields, because the
        # required validation will be handled by MultiValueField, not by those
        # individual fields.
        for f in fields:
            f.required = False
        self.fields = fields
    

    所以我会像这样覆盖__init__

    def __init__(self, *args, **kwargs):
        choices = kwargs.pop("choices",[])
        super(InputAndChoiceField, self).__init__(*args, **kwargs)
        self.fields = (
            fields.CharField(),
            fields.ChoiceField(choices=choices),
        )
    

    您甚至可能想要使用super(MultiValueField, self).__init__(*args, **kwargs) 而不是super(InputAndChoiceField, self).__init__(*args, **kwargs),因为您自己设置字段而不是通过参数获取它们。

    【讨论】:

    • 我刚试过这个,但我仍然得到一个空的选择字段。我检查并正确通过了选择。有什么想法吗?
    • 看起来您应该在调用 MultiValueField 构造函数之前构造一个“字段”对象,然后将该“字段”值传递给超类构造函数的第一个参数。
    【解决方案4】:

    通过小部件中的选择为我解决了这个问题

    class InputAndChoiceWidget(widgets.MultiWidget):
        def __init__(self, attrs=None):
            choices = [('a', 1), ('b', 2)]
            widget = (widgets.TextInput(),
                      widgets.Select(choices=choices)
                     )
            super(InputAndChoiceWidget, self).__init__(widget, attrs=attrs)
    

    【讨论】:

      【解决方案5】:
      class HTML5DateInput(DateInput):
      
          input_type = 'date'
      
      
      class CustomSelectRangeWidget(forms.MultiWidget):
      
          def __init__(self, attrs=None, choices = ()):
              widgets = (Select(attrs=attrs, choices=choices), HTML5DateInput(attrs=attrs), HTML5DateInput(attrs=attrs))
              super(CustomSelectRangeWidget, self).__init__(widgets, attrs)
      
          def decompress(self, value):
              if value:
                  return [value.field, value.start, value.stop]
              return [None, None, None]
      
          def format_output(self, rendered_widgets):
              return '-'.join(rendered_widgets)
      
      class CustomSelectRangeField(forms.MultiValueField):
      
          widget = CustomSelectRangeWidget
      
          def __init__(self, *args, **kwargs):
              if kwargs.has_key('choices') :
                  choices = kwargs.pop('choices')
              else:
                  choices = ()
              fields = (
                  forms.ChoiceField(choices=choices),  #field with choices,
                                                   # so that clean can be passed
                  forms.DateField(),
                  forms.DateField(),
                  )
              super(CustomSelectRangeField, self).__init__(fields=fields, *args, **kwargs)
              #initialize widget with choices.
              self.widget = CustomSelectRangeWidget(choices=choices)
      
          def compress(self, data_list):
              if data_list:
                 #check if datalist has 3 not null values
                 if len([v for v in data_list if v not in [None, '']]) == 3:
                     out_dict = {'field':data_list[0], 'start':data_list[1], 'stop':data_list[2]}
                     return out_dict
             return None
      

      【讨论】:

      • 这解决了我在选择字段中的选择问题。它是由 3 个字段、1 个 Choice 字段和其他 2 个字段组成的 DateInput
      猜你喜欢
      • 2020-03-29
      • 1970-01-01
      • 2016-02-09
      • 2022-12-05
      • 1970-01-01
      • 2015-09-13
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      相关资源
      最近更新 更多