1

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'

Adam
  • 1,724
  • 4
  • 21
  • 31

1 Answers1

0

Do you want to make this as Widget in Django?

<input type="text" name="example" list="exampleList">
<datalist id="exampleList">
  <option>hello</option>
  <option>there</option>
</datalist>

if you do, you can check this.

class ListTextWidget(forms.TextInput):
    def __init__(self, data_list, name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._name = name
        self._list = data_list
        self.attrs.update({'list':'list_%s' % self._name, 'autocomplete':'off'})

    def render(self, name, value, attrs=None):
        text_html = super().render(name, value, attrs=attrs)
        data_list = '<datalist id="list_%s">' % self._name
        for item in self._list:
            data_list += '<option value="%s">' % item
        data_list += '</datalist>'

        return (text_html + data_list)


class ExampleForm(ModelForm)
    ...
    include = ['exampleOne', ...]
    widgets = { 
    'exampleOne': ListTextWidget(data_list=yourModel.objects.all(),name='exampleOne')
    }
Jayground
  • 1,797
  • 1
  • 17
  • 32
  • No, I've added the widget code. Also I'm using django 1.11, where instead of overriding `render` you can use templates and `get_context` – Adam Jul 28 '17 at 08:29
  • I totally misunderstood the intention of your question. Did you solve your problem? Can you explain what you want to build more detail? In decompress method, are you sure value is proper??? – Jayground Aug 02 '17 at 01:44