Opened 7 years ago

Closed 7 years ago

Last modified 7 years ago

#28443 closed Bug (needsinfo)

MultiValueField does not work with ModelChoiceField? — at Version 4

Reported by: adam-kral Owned by: nobody
Component: Forms Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by adam-kral)

The code: (where AddressInput is a MultiWidget)

class AddressInput(widgets.MultiWidget):
    def __init__(self, attrs=None):
        self.widgets = widgets.HiddenInput(attrs), widgets.TextInput(attrs) # note that the second widget would be customized, I'm only providing simplified example, which does produce the same error
        super().__init__(self.widgets, attrs)

    def decompress(self, value):
        try:
            address = AddressPoint.objects.get(pk=value)
        except (AddressPoint.DoesNotExist, ValueError):
            address = None

        return [value, str(address)]

    def value_from_datadict(self, data, files, name):
        return tuple(widget.value_from_datadict(data, files, f'{name}_{i}') for i, widget in enumerate(self.widgets))


class AddressFormField(MultiValueField):
    widget = AddressInput

    def __init__(self, queryset, empty_label="---------", to_field_name=None, limit_choices_to=None, *args, **kwargs):
        fields = (
            ModelChoiceField(queryset, empty_label=empty_label, to_field_name=to_field_name, limit_choices_to=limit_choices_to, *args, **kwargs),
            CharField()
        )

        super().__init__(fields=fields, require_all_fields=False, *args, **kwargs)

        #self.widget.choices = self.fields[0].widget.choices  #### if not commented out, I get another error: AttributeError: 'RelatedFieldWidgetWrapper' object has no attribute 'decompress'

    def compress(self, data_list):
        if not data_list[1]:
            return None

        if not data_list[0]:
            raise ValidationError('Invalid address')

        return data_list[0]


class AddressForeignKey(ForeignKey):
    def formfield(self, **kwargs):
        # This is a fairly standard way to set up some defaults
        # while letting the caller override them.
        defaults = {'form_class': AddressFormField}
        defaults.update(kwargs)
        return super().formfield(**defaults)

I get this error: AttributeError: 'AddressInput' object has no attribute 'choices'
Because ModelChoiceField did not declare it. Passing the widget to ModelChoiceField does not work as it makes a copy if it's an instance.
Thus I set the choices attribute manually as you can see in the commented out code.
But then I got another error which I didn't resolve: AttributeError: 'RelatedFieldWidgetWrapper' object has no attribute 'decompress'

Change History (4)

comment:1 by adam-kral, 7 years ago

Component: UncategorizedForms
Type: UncategorizedBug

comment:2 by Tim Graham, 7 years ago

Resolution: needsinfo
Status: newclosed

It's not immediately obvious to me whether or not Django is at fault and the ticket tracker isn't the place to get debugging help. Although I can't say for sure since you didn't provide code for AddressInput, it looks more likely that you haven't constructed the widget correctly. Please see TicketClosingReasons/UseSupportChannels for ways to get help and reopen the ticket if you confirm a bug. Thanks.

comment:3 by adam-kral, 7 years ago

Description: modified (diff)

comment:4 by adam-kral, 7 years ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top