Implement Autocomplete in Django
Tags: django, ajax, webdev, python
Published: 2020-12-19 17:49:20

Hello pals,
In this blog, I am going to guide you to implement autocomplete in Django admin interface. I chose admin interface because it provides CRUD ( CREATE-READ-UPDATE-DELETE ) view for defined models out of the box.
 
For implementing auto completion feature, I am going to use `django-ajax-selects` currently maintained by  [crucialfelix](https://github.com/crucialfelix).

## Setup
Let's start by creating a django project.
First of all, I'll create a virtual environment `.venv`. You can create  virtual environment just by `python -m venv .venv`.
Let's activate the virtual environment `.venv\Scripts\activate` as I am currently on Windows environment.

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`. 
```python
# 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.
```python
# 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 through 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.

```python
# 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.

```python
# 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 displays`blog.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` .

```python 
# 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`.
```python
# 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, 
```html
{{ 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](https://i.imgur.com/MOTFNvP.png)

Let's try to add another record to observe auto-completion.
![Autcompletion Demo](https://i.imgur.com/fjoYRXx.png)
Notice that, when I am typing `A`, the records with the letter `A` in their name appeared as dropdown so user may autocomplete the record.

Thank you for reading.