Implement Autocompletion in Django

Implement Autocompletion in Django

Hello pals,

In this blog, I'll show you how to add autocomplete to the Django admin interface. I chose the admin interface because it lets me create, read, update, and delete (CREATE, READ, UPDATE, DELETE) models.

For implementing the auto completion feature, I am going to use django-ajax-selects currently maintained by crucialfelix.

Setup

Let's begin by making a Django project. First of all, I’ll create a virtual environment .venv. You can create a virtual environment just by python -m venv .venv. Let’s activate the virtual environment .venv\Scripts\activate as I am currently on Windows.

After that, install django and django-ajax-selects using pip.

pip install django django-ajax-selects

Following installing libraries, let’s create django project named autocomplete_demo_project.

django-admin startproject autocomplete_demo_project

Subsequently, Let’s create a django app named blog .

python manage.py startapp blog

And register the app 'blog' and 'ajax_select' in INSTALLED_APPS inside settings.py.

# autocomplete_demo_project/settings.py
INSTALLED_APPS  = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ajax_select', # new apps
    'blog.apps.BlogConfig',
]

Thereafter, let’s define URLConfs for ajax_select so it may load the necessary javascripts, stylesheets for autocompletion.

# autocomplete_demo_project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from ajax_select import urls as ajax_select_urls

urlpatterns = [
    path('ajax_select/', include(ajax_select_urls)),
    path('admin/', admin.site.urls),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Models and Lookups

We have to start by defining models which will later be used for observing auto-completion in admin panel. Without adding any complexity, I’ll just start with a single field name in Blog model.

# blog/models.py
from django.db import models
from django.utils.translation import gettext_lazy as _

class Blog(models.Model):
    name = models.CharField(_("Blog"), max_length=50)

def  __str__(self):
    return  self.name

Let’s also create a lookup for the field name which fetches the most suitable result by matching the query entered while entering a record in admin panel. For doing so, let’s create LookupChannel for our field name (NameLookup) in lookup.py inside blog app directory.

# blog/lookups.py
from ajax_select import register, LookupChannel

from .models import Blog

@register('names')
class  NameLookup(LookupChannel):
    model = Blog

    def  get_query(self, q, request):
        return  self.model.objects.filter(name__icontains=q).order_by('name')[:50]

    def  format_item_display(self, item):
        return  u"<span class='blog_name'>%s</span>"  % item.name

Here, we’ve observed two methods inside our NameLookup class. Let’s dig into both of them:

  1. get_query method takes q parameter as query which will be used to filter the record in database. Here, we’ve used the name__icontains approach which will filter the matched records in DB with sub string query q and return the filtered records. We may also proceed with name__startswith approach which only tries to match the starting string with the records in DB with sub string query q and return the filtered records. But, let’s just stick into one approach ( name__icontains approach) to implement the auto completion.

  2. format_item_display methods takes each filtered records and displaysblog.name (data) which is the need in our case with proper styling. We can override the display properties to make it look better and match with our preferences.

Also notice that we’ve registered a lookup name names with register decorator which will later be used with form to match with required form field i.e. The form field with the property names will match with the above lookup.

ModelAdmin and Forms

Ensuing step will be to create Django forms which will be provided as property to our ModelAdmin which we’ll later create in admin.py .

# blog/forms.py
from ajax_select.fields import AutoCompleteField
from django import forms

from .models import Blog

class  BlogForm(forms.ModelForm):

    name = AutoCompleteField('names')
    class  Meta:
        model = Blog
        fields = [
            'name'
        ]

It’s worth noticing that the field name is an AutoCompleteField with the property names which is a lookup name we’ve registered in NameLookup class inside blog/lookups.py. It’s how we interlink forms and lookups using django-ajax-selects.

Following this, let’s create ModelAdmin inside admin.py.

# blog/admin.py
from ajax_select.admin import AjaxSelectAdmin
from django.contrib import admin

from .forms import BlogForm
from .models import Blog

@admin.register(Blog)
class  BlogAdmin(AjaxSelectAdmin):

    form = BlogForm

What we’ve done here is overriding the default form property provided by Django Admin and get it replaced by our BlogForm form object.

Also note that, the use of BlogForm isn’t limited to ModelAdmin for auto completion. It can also be used in used defined templates by passing form inside context parameter from views.py. For doing so,

{{ form.media }}
{{ form }}

We need to load ((form.media}} to load the necessary JS and CSS files defined by ajax_selects app.

Observation

After defining lookups, models, forms and modeladmin, now we can make migrations and run the migration files.
python manage.py makemigrations blog

Post hoc, let’s run those migration files.
python manage.py migrate

Then, create a superuser for accessing admin interface,
python manage.py createsuperuser

Then, log into 127.0.0.1:8000/admin to access admin interface and navigate to Blog model.

Try adding some records and you’ll notice auto completion making work easy for you.

Demo

I’ve added some records in my models.

Added Records

Let’s try to add another record to observe auto-completion.

Autcompletion Demo

Notice that, when I am typing A, the records with the letter A in their name appeared as a dropdown so the user may autocomplete the record.

Thank you for reading.