Upgrading from django-autocomplete-light v1 to v2

Please enjoy this v1 to v2 upgrade instructions to upgrade from DAL 1.x to 2.x (documented with love !).

Quick upgrade

  • the Autocomplete class design hasn’t changed at all.
  • yourlabsWidget() doesn’t parses data-* options the same,
  • the django/form python code has been re-organised ie. get_widgets_dict() is gone and autocomplete_light.ModelForm wraps around all features by default.
  • use autocomplete_light.ModelForm instead of autocomplete_light.GenericModelForm - generic foreign keys and django-generic-m2m are supported by default if installed.

Detailed upgrade

You should not use widget directly anymore

We used to have things like this:

class YourForm(autocomplete_light.GenericModelForm):
    user = forms.ModelChoiceField(User.objects.all(),
        widget=autocomplete_light.ChoiceWidget('UserAutocomplete'))

    related = GenericModelChoiceField(
            widget=autocomplete_light.ChoiceWidget(
                autocomplete='AutocompleteTaggableItems',
                autocomplete_js_attributes={'minimum_characters': 0}))

    class Meta:
        model = YourModel

This caused several problems:

  • broke a DRY principle: if you have defined a user foreign key and registered an Autocomplete for the model in question, User, then you should not have to repeat this.
  • broke the DRY principle since you had to set choices on both the ModelChoiceField and the Autocomplete - UserAutocomplete in this example.
  • also, validation was done in the widget’s render() function, mostly for security reasons. Validation is not done in the widget anymore, instead it is done in autocomplete_light.fields.

What should the above code be like ? Well it depends, it could just be:

class YourForm(autocomplete_light.ModelForm):
    class Meta:
        model = YourModel

If you have registered an Autocomplete for the model that the user ForeignKey is for, then autocomplete_light.ModelForm will pick it up automatically.

Assuming you have registered a generic autocomplete, autocomplete_light.ModelForm will pick it up automatically.

If you want Django’s default behavior back (using a <select> tag), then you could tell autocomplete_light.ModelForm to not be “autocomplete-aware” for user as such:

class YourForm(autocomplete_light.ModelForm):
    class Meta:
        model = YourModel
        autocomplete_exclude = ('user',)

autocomplete_light.ModelChoiceField and autocomplete_light.GenericModelChoiceField:

class YourForm(autocomplete_light.ModelForm):
    user = autocomplete_light.ModelChoiceField('UserAutocomplete')
    related = autocomplete_light.GenericModelChoiceField('AutocompleteTaggableItems')

    class Meta:
        model = YourModel
        autocomplete_exclude = ('user',)

You can still override widgets the same way as before, but you should consider the DRY breaking implications (which are not specific to django-autocomplete-light, but Django’s design in general).

Specification of the Autocomplete class to use

New rules are:

  • if an Autocomplete class was registered for a model then it becomes the default Autocomplete class for autocompletion on that model,
  • other Autocomplete classes registered for a model will not be used by default

You can still define the Autocomplete class you want in the field definition:

class FooForm(autocomplete_light.ModelForm):
    bar = autocomplete_light.ModelChoiceField('SpecialBarAutocomplete')

    class Meta:
        model = Foo

But this will break some break django DRY logic. Instead, this won’t break DRY:

class FooForm(autocomplete_light.ModelForm):
    class Meta:
        model = Foo
        autocomplete_names = {'bar': 'SpecialBarAutocomplete'}

Python class re-organisation

Form classes like FixedModelform or GenericModelForm were renamed. But if you can, just inherit from autocomplete_light.ModelForm instead.

Generic field classes were moved from autocomplete_light.contrib.generic_m2m into autocomplete_light.fields: just import autocomplete_light.GenericModelChoiceField and autocomplete_light.GenericModelMultipleChoiceField <autocomplete_light.fields.GenericModelMultipleChoiceField.

Deprecation of autocomplete_js_attributes and widget_js_attributes

In the past, we used autocomplete_js_attributes and widget_js_attributes. Those are deprecated and HTML data attributes should be used instead.

For example:

class PersonAutocomplete(autocomplete_light.AutocompleteModelBase):
    model = Person
    autocomplete_js_attributes = {
        'minimum_characters': 0,
        'placeholder': 'foo',
    }
    widget_js_attributes = {'max_values': 3}

Should now be:

class PersonAutocomplete(autocomplete_light.AutocompleteModelBase):
    model = Person
    attrs = {
        'data-autcomplete-minimum-characters': 0,
        'placeholder': 'foo',
    }
    widget_attrs = {'data-widget-maximum-values': 3}

As you probably understand already magic inside autocomplete_js_attributes and widget_js_attributes is gone, we’re just setting plain simple HTML attributes now with attrs.

Also notice the other two differences which are detailed below:

  • max-values was renamed to maximum-values (see below)
  • data-autocomplete-placeholder is gone in favor of HTML5 placeholder attribute (see below)

max-values was renamed to maximum-values

For consistency with one of my naming conventions which is: no abbreviations.

data-autocomplete-placeholder is gone in favor of HTML5 placeholder attribute

It made no sense to keep data-autocomplete-placeholder since we now have the HTML5 placeholder attribute.

Widget template changes

This is a side effect from the deprecation of autocomplete_js_attributes and widget_js_attributes.

This:

<span class="autocomplete-light-widget {{ name }}
    {% if widget.widget_js_attributes.max_values == 1 %}single{% else %}multiple{% endif %}"
    id="{{ widget.html_id }}-wrapper"
    {{ widget.widget_js_attributes|autocomplete_light_data_attributes }}
    {{ widget.autocomplete_js_attributes|autocomplete_light_data_attributes:'autocomplete-' }}
    >

Is now:

<span id="{{ widget.html_id }}-wrapper" {{ widget_attrs }} >

Script changes

.yourlabsWidget() used to parse data-* attributes:

  • data-foo-bar used to set the JS attribute yourlabs.Widget.fooBar,
  • data-autocomplete-foo-bar used to set the JS attribute yourlabs.Widget.autocomplete.fooBar.

Now:

  • .yourlabsWidget() parses data-widget-* attributes and,
  • .yourlabsAutocomplete() parses data-autocomplete-* on the ``<input />`` !

So this:

<span class="autocomplete-light-widget" data-autocomplete-foo-bar="2" data-foo-bar="3">
    <input .. />

Becomes:

<span class="autocomplete-light-widget" data-widget-foo-bar="3">
    <input data-autocomplete-foo-bar="2" ... />

.choiceDetail and .choiceUpdate were renamed to .choice-detail and .choice-update

This makes the CSS class names standard.