Django MultiValueField - 如何将选择传递给 ChoiceField?

发布于 2024-08-08 23:57:53 字数 1625 浏览 4 评论 0原文

我有一个带有 charfield 和 choicefield 的多值字段。我需要将选择传递给 choicefield 构造函数,但是当我尝试将其传递到自定义多值字段时,我收到错误 __init__() 获得了意外的关键字参数“choices”。

我知道代码的其余部分有效,因为当我从 __init__ 和 super 中删除choices关键字参数时,多值字段显示正确,但没有任何选择。

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

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和self.fields[1].choices末尾打印出InputAndChoiceField的内容,其中包含如上所述的正确值,但它不会在浏览器中显示任何选择。

I have a multivaluefield with a charfield and choicefield. I need to pass choices to the choicefield constructor, however when I try to pass it into my custom multivaluefield I get an error __init__() got an unexpected keyword argument 'choices'.

I know the rest of the code works because when I remove the choices keyword argument from __init__ and super, the multivaluefield displays correctly but without any choices.

This is how I setup my custom multivaluefield:

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)

And I call it like so:

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

So how do I pass the choices to my ChoiceField field?

Edit:

I've tried stefanw's suggestion but still no luck. I've used logging.debug to print out the contents of InputAndChoiceField at the end of the init and self.fields[1].choices contains the correct values as per above however it doesnt display any choices in the browser.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

国粹 2024-08-15 23:57:53

我遇到了这个完全相同的问题并像这样解决了它:

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)

将“choices”kwarg 添加到小部件的构造函数中。然后在创建字段后显式调用构造函数。

I ran into this exact same problem and solved it like this:

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)

Add a "choices" kwarg to the widget's constructor. Then explicitly call the constructor after the field is created.

顾挽 2024-08-15 23:57:53

ModelChoiceField 是从技术上讲,它是一个 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

ModelChoiceField is technically a ChoiceField, but it doesn't actually use any of the ChoiceField's implementations. So, here's how I use it.

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
数理化全能战士 2024-08-15 23:57:53

看看 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) 因为你自己设置字段而不是通过参数获取它们。

Have a look at the source of __init__ of forms.MultiValueField:

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

So I would overwrite the __init__ probably like this:

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

You might even want to do super(MultiValueField, self).__init__(*args, **kwargs) instead of super(InputAndChoiceField, self).__init__(*args, **kwargs) because you are setting the fields yourself instead of getting them via parameters.

甲如呢乙后呢 2024-08-15 23:57:53

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

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)

passing the choices in the widget solved this for me

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)
一身骄傲 2024-08-15 23:57:53
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
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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文