Django Autocomplete Light
Django Autocomplete Light (DAL) provides a modern, fresh set of widgets for Django forms and admin, enabling easy integration of autocompletion functionality for fields like foreign keys, many-to-many, and tags. It's currently at version 3.12.1 and typically releases updates in line with Django's development or new feature requirements.
Warnings
- breaking Major API changes when upgrading from Django Autocomplete Light 2.x to 3.x. The `autocomplete_light.shortcuts` API was removed, and the way autocompletes are defined and integrated into forms and views was significantly refactored.
- gotcha Incorrect `INSTALLED_APPS` ordering. `dal` and `dal_select2` must be listed in `INSTALLED_APPS` BEFORE any of your apps that use them.
- gotcha URL configuration mismatch between the form widget and the autocomplete view. The `url` parameter in `Select2QuerySetView` must point to a valid URL pattern for your autocomplete view.
Install
-
pip install django-autocomplete-light
Imports
- autocomplete_light
import autocomplete_light
- Select2QuerySetView (widget)
from dal_select2.widgets import Select2QuerySetView
- Select2QuerySetView (view)
from dal_select2.views import Select2QuerySetView
Quickstart
import os
import django
from django.conf import settings
from django.db import models
from django import forms
from django.urls import path, reverse_lazy
settings.configure(
DEBUG=True,
INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'dal',
'dal_select2',
'testapp' # Your app name
],
MIDDLEWARE=[
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
],
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
STATIC_URL='/static/',
SECRET_KEY=os.environ.get('DJANGO_SECRET_KEY', 'a-very-secret-key-for-testing-only'),
ROOT_URLCONF=__name__,
)
django.setup()
# --- Your app's models.py ---
class Country(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class Meta:
app_label = 'testapp'
class City(models.Model):
name = models.CharField(max_length=100)
country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='cities')
def __str__(self):
return f"{self.name} ({self.country.name})"
class Meta:
app_label = 'testapp'
# --- Your app's forms.py ---
import autocomplete_light
from dal_select2.widgets import Select2QuerySetView
class CityForm(forms.ModelForm):
class Meta:
model = City
fields = ('name', 'country')
widgets = {
'country': Select2QuerySetView(
url=reverse_lazy('country-autocomplete'),
attrs={
'data-placeholder': 'Search for a country',
'data-minimum-input-length': 2,
}
)
}
# --- Your app's views.py and urls.py combined for quickstart ---
from dal_select2.views import Select2QuerySetView
class CountryAutocomplete(Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results if a query is provided
qs = Country.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
# --- ROOT_URLCONF definitions ---
urlpatterns = [
path('country-autocomplete/', CountryAutocomplete.as_view(), name='country-autocomplete'),
# Example: If you were to integrate this form into a view
# path('city/add/', lambda request: HttpResponse(CityForm().as_p()), name='add-city'),
]
# Example usage (requires setting up Django's test client or a view)
if __name__ == '__main__':
# This part would typically be in a Django shell or a real view.
# For demonstration, we'll just show the form rendering.
from django.core.management import call_command
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("CREATE TABLE testapp_country (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) UNIQUE)")
cursor.execute("CREATE TABLE testapp_city (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100), country_id INTEGER REFERENCES testapp_country(id) ON DELETE CASCADE)")
cursor.execute("INSERT INTO testapp_country (name) VALUES ('France')")
cursor.execute("INSERT INTO testapp_country (name) VALUES ('Germany')")
cursor.execute("INSERT INTO testapp_country (name) VALUES ('Spain')")
# Print the form HTML to demonstrate the widget integration
print("\n--- Example City Form HTML ---\n")
print(CityForm().as_p())
print("\nCheck Django admin or a custom view for full functionality with the autocomplete URL.")