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": "user1@mydomain.com", "text": "user1@mydomain.com"}, {"id": "user2@mydomain.com", "text": "user2@mydomain.com"}, {"id": "user3@mydomain.com", "text": "user3@mydomain.com"}]}
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"
Advertisement
Answer
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