Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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"

like image 299
Abilash Amarasekaran Avatar asked May 16 '18 22:05

Abilash Amarasekaran


People also ask

Is a Django form field that takes in a text input?

Common Fields These built-in widgets represent HTML elements. CharField() is a Django form field that takes in a text input. It has the default widget TextInput, the equivalent of rendering the HTML code <input type="text" ...> . This field works well for collecting one line inputs such as name or address.

What is form Is_valid () in Django?

The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class. It returns True if data is valid and place all data into a cleaned_data attribute.

What is form cleaned_data in Django?

cleaned_data returns a dictionary of validated form input fields and their values, where string primary keys are returned as objects. form. data returns a dictionary of un-validated form input fields and their values in string format (i.e. not objects).

How do I get form fields in Django?

Basically to extract data from a form field of a form, all you have to do is use the form. is_valid() function along with the form. cleaned_data. get() function, passing in the name of the form field into this function as a parameter.


1 Answers

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

like image 178
Abilash Amarasekaran Avatar answered Oct 01 '22 03:10

Abilash Amarasekaran