Upgrading from 3.12.1 to 4.0.3

DAL 4.0.3 is a breaking upgrade from 3.12.1. It focuses on supported runtimes and removes unmaintained integration packages.

Runtime requirements

Upgrade your project to Python 3.11 or newer and Django 4.2 or newer before upgrading DAL.

pip install -U "django-autocomplete-light<5"

Optional integrations

Install the extras your project uses explicitly.

pip install -U "django-autocomplete-light[gfk]<5"
pip install -U "django-autocomplete-light[tags]<5"
pip install -U "django-autocomplete-light[nested]<5"

Removed packages

Remove these packages from INSTALLED_APPS and from Python imports. They are no longer shipped in DAL 4.0.3:

  • dal_genericm2m

  • dal_genericm2m_queryset_sequence

  • dal_gm2m

  • dal_gm2m_queryset_sequence

  • dal_select2_tagging

  • dal_legacy_static

The genericm2m extra was removed. Generic foreign key support remains available through django-autocomplete-light[gfk] and dal_queryset_sequence. django-taggit support remains available through django-autocomplete-light[tags] and dal_select2_taggit.

Python API changes

If your project subclasses DAL classes or overrides DAL methods, check these changes.

Removed integration imports

If you import from dal_genericm2m, dal_genericm2m_queryset_sequence, dal_gm2m, dal_gm2m_queryset_sequence, dal_select2_tagging, or dal_legacy_static, remove those imports. DAL v4 does not ship replacement classes for these packages. Migrate generic object autocompletes to dal_contenttypes / dal_queryset_sequence or keep custom compatibility code in your project.

If you use from dal import autocomplete as a convenience import, remove references to the deleted re-exports:

  • GenericM2MQuerySetSequenceField

  • GM2MQuerySetSequenceField

  • TaggingSelect2

The contenttypes field mixins are now re-exported when dal_contenttypes and django.contrib.contenttypes are installed:

  • ContentTypeModelFieldMixin

  • ContentTypeModelMultipleFieldMixin

  • GenericModelMixin

FutureModelForm

If you subclass FutureModelForm and override save() only for the old Django 1.8/1.9 compatibility behavior, delete that override and use Django’s ModelForm.save() behavior. DAL v4 no longer overrides FutureModelForm.save().

If you override FutureModelForm._save_m2m(), update any opts.virtual_fields fallback. v4 iterates over chain(opts.many_to_many, opts.private_fields). The opts.virtual_fields compatibility path was removed with support for old Django versions.

Widget rendering

If you override WidgetMixin.render_options(), move that logic to optgroups() or to render(). render_options() was a Django < 1.10 compatibility hook and is no longer called by supported Django versions.

If your widget subclass passes attrs=None or mutates attrs before calling WidgetMixin.__init__(), keep doing so safely: v4 reads the placeholder with (kwargs.get("attrs") or {}).get("data-placeholder"). You no longer need a local workaround for attrs=None.

Selected choice rendering

If you override filter_choices_to_render() or ModelSelect2Multiple.filter_choices_to_render(), note that ModelSelect2Multiple now preserves the submitted Select2 order by ordering the queryset with Case / When. Keep that ordering in custom multiple-queryset widgets if display order matters.

If you overrode render() on Select2Multiple, ListSelect2, ModelSelect2, or ModelSelect2Multiple only to make initial AJAX values render as selected options, remove that override and use the v4 built-in behavior. These widgets now include dal_select2.widgets.Select2InitialRenderMixin.

If you subclass Select2 widgets and want the v4 initial-value behavior, include Select2InitialRenderMixin before Select2WidgetMixin in the base-class list, following the built-in widgets.

class MyWidget(Select2InitialRenderMixin, Select2WidgetMixin, forms.Select):
    pass

List choice fields

If you subclass Select2ListChoiceField or call ChoiceCallable() directly, callable/list choices are normalized to tuples in v4. Code that expected list instances from ChoiceCallable should accept tuples instead.

Old compatibility branches

If you use Python 2 / old-Django compatibility imports from DAL internals, remove those branches. Examples:

  • django.core.urlresolvers.reverse

  • callable user.is_authenticated()

  • django.contrib.admin.utils.lookup_needs_distinct

  • backports.functools_lru_cache

DAL v4 is Python 3.11+ and Django 4.2+ only.

JavaScript changes

If your project only uses the bundled DAL static files through widget media, you normally do not need JavaScript changes. If you copied DAL JavaScript into your project or override DAL’s Select2 initialization, check these changes.

HTMX swaps

DAL v4 listens for htmx:afterSettle and initializes autocomplete widgets in the swapped content, including the root element from e.detail.elt.

If your project has custom HTMX code that manually calls window.__dal__initialize after every swap, keep it only for custom cases that DAL cannot see. Otherwise it can initialize the same widget twice.

The v4 initialization pattern is:

document.addEventListener('htmx:afterSettle', function (e) {
    if (window.__dal__initialize && window.django && django.jQuery) {
        var $root = django.jQuery(e.detail.elt);
        $root.find('[data-autocomplete-light-function]')
            .addBack('[data-autocomplete-light-function]')
            .excludeTemplateForms()
            .each(window.__dal__initialize);
    }
});

Select2 placeholder and clear button

If you copied or replaced autocomplete_light/select2.js, update your Select2 options to match v4:

var isRequired = $element.is('[required]');
var placeholderText = (
    $element.attr('data-placeholder') || (!isRequired ? ' ' : '')
);

$element.select2({
    placeholder: placeholderText,
    allowClear: !isRequired,
    // keep your other DAL options
});

This keeps Select2’s clear button disabled for required fields and gives optional fields a space placeholder when no explicit data-placeholder is configured. That fallback is needed by Select2’s clear-button behavior.

Removed legacy static package

If templates or build scripts refer to files from dal_legacy_static, remove those references. The package is not shipped in v4.

Builds from source

If your deployment or packaging scripts build DAL from source, switch from setup.py entry points to the PEP 517 build backend.

python -m build

What to test after upgrading

After upgrading, test forms that render existing Select2 values, multiple Select2 submissions, clearable placeholders, HTMX swaps, and any custom attrs=None widget construction. These areas changed between 3.12.1 and 4.0.3.