how to create a autocomplete input field in a form using Django

19,779

I finally got the autocomplete search working using the instructions found here

https://github.com/xcash/bootstrap-autocomplete
https://bootstrap-autocomplete.readthedocs.io/en/latest/

It is very simple to use and does not need to install any app in settings.py and it works for both bootstrap 3 and bootstrap 4

There are lot of other packages available but this was simple and easy for my need.

I am going to explain the code I used

page.html

{% block script %}
    <script src="https://cdn.rawgit.com/xcash/bootstrap-autocomplete/3de7ad37/dist/latest/bootstrap-autocomplete.js"></script>
    <script>
        $('.basicAutoComplete').autoComplete(
            {minLength: 1}
        );
        $('.dropdown-menu').css({'top': 'auto', 'left': 'auto'})

    </script>
{% endblock %}
.
.
.
.
.
{% if field.name == "from_email" %}
   {% render_field field class="basicAutoComplete form-control" %}
{% else %}
   {% render_field field class="form-control" %}
{% endif %}

autoComplete is the function called to perform the action and minLength specifies the minimum length of the text before performing the fetch action I added an extra CSS to fix the autocomplete dropdown otherwise it was weird.

The Jinja render kept overwriting the class definition from views so I added an if check for it.

urls.py

from . import views

urlpatterns = [
    .
    .
    .
    path('email_autocomplete/', views.email_autocomplete, name='email_autocomplete')
]

Added this line to urls.py

forms.py

class LeaveForm(forms.Form):
    from_email = forms.EmailField(required=True, widget=forms.TextInput(
        attrs={
            'style': 'width: 400px',
            'class': 'basicAutoComplete',
            'data-url': "/domain/email_autocomplete/"
        }))

The above is the code for the input field in forms.py. data-url points to where the JSON result would be generated.

views.py

from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from .models import model
        
def email_autocomplete(request):
    if request.GET.get('q'):
        q = request.GET['q']
        data = model.objects.using('legacy').filter(email__startswith=q).values_list('email',flat=True)
        json = list(data)
        return JsonResponse(json, safe=False)
    else:
        HttpResponse("No cookies")
            

This was the most confusing part for me. The GET request is easy to understand but it took a while to convert the data from model.objects into a JSON formatted object. The trick was to use

values_list('columnName',flat=True)

when filtering the data from the database, then converting to a list using list(data) and finally use JsonResponse to convert it to JSON.

Hope this will help any one who wants a simple autocomplete

Share:
19,779
Abilash Amarasekaran
Author by

Abilash Amarasekaran

Living life and having fun living. Love to Game and write code to help people use tech better. Looking for new opportunities to continuously learn and do better.

Updated on June 05, 2022

Comments

  • Abilash Amarasekaran
    Abilash Amarasekaran almost 2 years

    I am pretty new to django and its ways. I am trying to create an autocomplete field for a form. My code is as below

    forms.py

    from django import forms
    
    class LeaveForm(forms.Form):
        leave_list = (
            ('Casual Leave', 'Casual Leave'),
            ('Sick Leave', 'Sick Leave')
        )
        from_email = forms.EmailField(required=True, widget=forms.TextInput(attrs={'style': 'width: 400px'}))
        start_date = end_date = forms.CharField(widget=forms.TextInput(attrs={'type': 'date', 'style': 'width: 175px'}))
        leave_type = forms.ChoiceField(choices=leave_list, widget=forms.Select(attrs={'style': 'width: 400px'}))
        comments = forms.CharField(required=True, widget=forms.Textarea(attrs={'style': 'width: 400px; height: 247px'}))
    
        def clean_from_email(self):
            data = self.cleaned_data['from_email']
            if "@testdomain.com" not in data:
                raise forms.ValidationError("Must be @testdomain.com")
            return data
    

    What I want to achieve is that when an user types words into the "From Email" field the list of emails I have stored in an external DB should appear in the autocomplete list option.

    models.py

    from django.db import models
    
    
    class ListOfUsers(models.Model):
        emp_number = models.CharField(db_column='Emp_Number', primary_key=True, max_length=50, unique=True)  # Field name made lowercase.
        name = models.CharField(db_column='Name', max_length=40)  # Field name made lowercase.
        supervisor = models.CharField(db_column='Supervisor', max_length=40)  # Field name made lowercase.
        email = models.CharField(db_column='Email', max_length=50, blank=False, null=False, unique=True)  # Field name made lowercase.
    
    
        class Meta:
            managed = False
            db_table = 'List of users'
    

    Any idea how this can be done ?

    Update :

    I started messing around with django-autocomplete-light and now able to get a reply from the autocomplete url. It looks like this

    {"results": [{"id": "[email protected]", "text": "[email protected]"}, {"id": "[email protected]", "text": "[email protected]"}, {"id": "[email protected]", "text": "[email protected]"}]}
    

    views.py

    class EmailAutocomplete(autocomplete.Select2ListView):
        def get_list(self):
            qs = ListOfUsers.objects.using('legacy')
    
            if self.q:
                qs = qs.filter(email__icontains=self.q).values_list('email', flat=True)
    
            return qs
    

    I still do not know how to get this data to appear in the field "from_email"

  • Travis N. Miller
    Travis N. Miller over 4 years
    I appreciate your sharing of this solution. I am trying to follow your example and am trying to mesh this with my class based view CreateView. Is there a way to do this? If so do i still need a separate url for the JsonResponce? I woudl be happy to share my code or if you could explain the views a little more I would appreciate it. I am piping in a separate list of expected text (not connected to my model instances) so I am less interested in creating the json object and more interested in how the view looks in general. Keeping in mind i am coming from the generic class based view. Many thanks
  • Tim Richardson
    Tim Richardson almost 4 years
    This is amazing. I was completely lost with django auto-complete-light.
  • Abilash Amarasekaran
    Abilash Amarasekaran almost 4 years
    @TravisN.Miller sorry have not visited here in a while. Do you still need help.
  • Fred
    Fred about 3 years
    Great post, way simpler and more robust than django-autocomplete-light, which I could not get to work in a modal...
  • Pierric
    Pierric almost 3 years
    In fact Django proposes this functionality in the admin.ModelAdmin class, with the property 'autocomplete_fields'. Why isn't this also available in custom forms?
  • giaggi
    giaggi over 2 years
    This is great! Any suggestion on how to handle the use-case where the autocomplete returns and empty set so you allow the user to create a new object on the fly?
  • Abilash Amarasekaran
    Abilash Amarasekaran over 2 years
    @giaggi I have not worked on Django for a while. If any one wants to work on this feel free.
  • joshlsullivan
    joshlsullivan about 2 years
    For your filter, you can also use email__icontains=q