In this Python tutorial, I will show you How to Create Search Autocomplete with Filter in Django. Basically, you will learn how to create a search bar with a filter that shows an auto-suggested list of words based on whatever type in the search bar field.
How to Create Search Autocomplete with Filter in Django
The first step to prepare the virtual environment for your project so follow the below steps:
Open your favorite terminal or command line prompt and create the new directory ‘django_search’ and change the directory to this newly created directory.
mkdir django_search
cd django_search
Create a virtual environment named ‘env_search’ and you are free to choose different environment names as you want. Run the below command to create the env.
python -m venv env_search
After creating the virtual environment ‘env_search’, activate it using the below command.
env_search\Scripts\activate
Now install the latest Django version using the below command.
python -m pip install django
Now, if you have installed any other IDE ( such as vs code and etc) and can perform the next step in that IDE, as I have installed visual studio code so I will perform all the work from the IDE now.
Create a Django project named ‘search_auto_filter’ in the current directory using the below command.
django-admin startproject search_auto_filter .
Move to the project directory and create a new app named ‘autocomplete_search_filter’ using the below command.
django-admin startapp autocomplete_search_filter
Add the newly created project name ‘autocomplete_search_filter‘ in the setting.py file of your project under the INSTALLED_APPS section.
INSTALLED_APPS = [
'django.contrib.admin',
......,
autocomplete_search_filter,
]
Read How to Create a Chip with Filter in Django
Run the command to create a default database and tables that come with Django using the below code.
cd ..
python manage.py migrate
Create two models Skills and Location in the file models.py using the below code.
from django.db import models
class Skills(models.Model):
skill_name = models.CharField(null=True, max_length=100)
def __str__(self):
return self.skill_name;
class Location(models.Model):
location = models.CharField(null= True, max_length=100)
def __str__(self):
return self.location;
Register the model’s Skills and Location in the file admin.py using the below code.
from django.contrib import admin
from .models import Skills, Location
admin.site.register(Skills)
admin.site.register(Location)
Then execute the following command to create the model’s table in the database.
python manage.py makemigrations autocomplete_search_filter
python manage.py migrate
Now open the file views.py of your app ‘autocomplete_search_filter’ and add the following code.
from django.shortcuts import render
from django.views import View
from .models import Skills, Location
from django.core import serializers
class AutocompleteSearchFilter(View):
def get(self, request):
skills = Skills.objects.all()
locations = Location.objects.all()
skills_list = serializers.serialize('json',list(skills))
location_list = serializers.serialize('json',list(locations))
context = {'skills':skills_list, 'locations':location_list}
return render(request, 'index.html', context)
The above code contains a view called ‘AutocompleteSearchFilter’ which fetches the data of table Skills and Locations from the database and stores in the variables skills and locations respectively.
Then fetched data is serialized using the function serialize() which exists in the module serializers. Serialization is the process where the objects are converted into data types so that they can be used by other javascript and front-end frameworks.
Next, the serialized data is passed as context to the template ‘index.html’ through the render() method.
Let’s create a folder named ‘templates’ within your app ‘autocomplete_search_filter’ and then create a new file called ‘index.html’ within that folder with the following HTML code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Search Bar</title>
</head>
<body>
<div class='container'>
<form action="" class="search_filter">
<select name="drop-down" id="filter">
<option value="location" id="option_location">Location</option>
<option value="skill" id="option_skill">Skill</option>
</select>
<input type='text' placeholder="Search Skills or Location" name="search" id="search_data">
<button type="submit">Search</button>
</form>
</div>
</body>
</html>
After completing the above steps, let’s set up the URL path for the templates or view so you can access the page in the browser.
Create a new file called ‘urls.py’ in your app ‘autocomplete_search_filter’ and add the following URL path.
from django.urls import path
from .views import AutocompleteSearchFilter
urlpatterns = [
path('', AutocompleteSearchFilter.as_view(), name= 'search-filter')
]
Now again specify the URL path at the project level so open the file ‘urls.py’ in your project ‘search_auto_filter’ using the below code.
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('autocomplete_search_filter.urls'))
]
Save each file and make sure you have followed the steps correctly till now. Run the following command in your terminal to run the Django server.
python manage.py runserver
Now open the address ‘http://127.0.0.1:8000/’ in your browser and you see the following page containing a search bar with filter Location and Skill.
When you type anything in the search box you only see the typed words by you in the input field but you want some autocomplete or auto-suggested words as soon as you start to type in the search box based on the filter Location and Skill.
I mean when you select Location from the filter, it should show only the name of the location as auto-suggested words below the search box based on the typed characters or words within the search box, and the same for the filter Skill.
Let’s implement that functionality in the front end using javascript.
Open the index.html file that you have created above and add the tag ‘<script></script>’ before the closing tag </body>. And within the tag ‘<script></script>’ add the following code.
<script>
var loc = JSON.parse('{{ locations|safe }}');
var skill = JSON.parse('{{ skills|safe }}');
var availableSkill = [];
var availableLocation = [];
var allData = []
var filterField = document.getElementById('filter');
var searchInput = document.getElementById('search_data');
filterField.addEventListener('change', function() {
var filterValue = filterField.value;
if (filterValue === 'location') {
availableLocation = loc.map(function(item) {
return item.fields.location;
});
searchInput.value = '';
autocompleteSetup(availableLocation);
} else if (filterValue === 'skill') {
availableSkill = skill.map(function(item) {
return item.fields.skill_name;
});
searchInput.value = '';
autocompleteSetup(availableSkill);
} else {
}
});
function autocompleteSetup(source) {
searchInput.addEventListener('input', function() {
var inputValue = this.value;
var matches = source.filter(function(item) {
return item.toLowerCase().startsWith(inputValue.toLowerCase());
});
if (matches.length > 0) {
var autocompleteList = document.createElement('ul');
autocompleteList.classList.add('autocomplete-list');
matches.forEach(function(match) {
var listItem = document.createElement('li');
listItem.textContent = match;
autocompleteList.appendChild(listItem);
});
removeExistingAutocomplete();
this.parentNode.appendChild(autocompleteList);
autocompleteList.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
searchInput.value = e.target.textContent;
removeExistingAutocomplete();
}
});
} else {
removeExistingAutocomplete();
}
});
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target)) {
removeExistingAutocomplete();
}
});
function removeExistingAutocomplete() {
var existingAutocomplete = document.querySelector('.autocomplete-list');
if (existingAutocomplete) {
existingAutocomplete.remove();
}
}
}
</script>
Let’s break down the above code into different parts and understand each part separately. Look at the below picture of the code.
In the above picture look at line numbers 27 and 28 where the data from the variables’ locations and skills are parsed as JSON data. The {{ locations|safe }} and {{ skills|safe }} act as placeholders for the JSON data.
When the page is loaded or an HTML template is generated, this JSON data is inserted into the template with the help of the Django server in a dynamic way.
- Remember we have talked about the word serialization in the above steps in the view called ‘AutocompleteSearchFilter’. where you serialized the data locations and skills and passed them to the template ‘index.html’ as context data.
- If you don’t serialize the data, which means you are passing the Queryset which is not interpretable within the javascript code. So after the serialization of data, you passed the data as the list containing JSON string value.
- To get that data into the javascript code, you have used the template tag {{ locations|safe }} and {{ skills|safe }} and converted the data into JSON objects using the javascript function JSON.parse().
The code at lines 32 and 33 ‘var filterField = document.getElementById(“filter”);’, referencing to the html element <select> or drop-down filter with id equal to ‘filter’ and ‘var searchInput = document.getElementById(“search_data”);’, referencing to the html element <input> with id equal to ‘serach_data’.
Look at the line numbers from 35 to 52, where the event listener is defined on the element ‘filterfield’ which is the drop-down or <select> tag ‘filterField.addEventListener(“change”, function () { … });’.
The event is triggered when the user selects any option such as Location or Skill from the drop-down filter which is defined by the tag <select>, In reality, the event listener listens for any changes that occur within the drop-down filter.
Based on any changes made by the user, it takes certain actions, in this case, the event listener calls a function that performs the following actions.
- First, it extracts the value from the filterField and assigns that value to the variable filterValue using this code ‘var filterValue = filterField.value;’.
- Then it uses the conditional statement to check whether the value is location or skill, and based on the conditional check it uses the map() function on the loc and skill array variable to map each item in it respectively.
- It then receives the values from the property of location and skill_name and stores them in the variable ‘availableLocation’ and ‘availableSkill’ respectively.
- Then input field is cleared using the (searchInput.value = “”). After this, the function autocompleteSetup() is called by passing the array data of ‘availableLocation’ and ‘availableSkill’ respectively.
- The autocompleteSetup() performs the autocompletion functionality for the typed character or word in the input field based on the selected filter Location and Skill.
Now let’s look at the second part of the code which is the function autocompleteSetup().
Look at line numbers 54 to 98, the autocompleteSetup(source) function is defined by passing an argument ‘source’, and this source can be the array of data availablelocation and availableSkill.
At the start of the function an event listener is defined on the element ‘searchInput’, this event listener listens to the input field for the input event. when any user types something in the input field (especially in the search box field that you have created), the input event is generated.
Based on that input event, the event listener performs the following operation:
- First, it gets the current value typed by the user in the input field using the code, ‘var inputValue = this.value;’ and stores it in the variable inputValue.
- Then the source array is filtered using the source.filter(function (item)) function, for each item that starts with the typed character or words in the input field, and assigns the matching item with that character or words as an array to the variable matches.
From line numbers 61 to 84, the conditional statement is used to check if there are any matches exist using the code ‘if (matches.length > 0)’. If any matches exist, then it performs the following operations:
It creates unorder list element dynamically using the code ‘var autocompleteList = document.createElement(“ul”);’ with class id equal to ‘autocomplete-list’ using the ‘autocompleteList.classList.add(“autocomplete-list”);’
- Then for each matched item in the array matches are iterated using the matches.forEach(), a new list item HTML element is created using the ‘var listItem = document.createElement(“li”);’. The content of each list item is set to the matched value using ‘listItem.textContent = match;’.
- At last, the list item (listItem) is added to the unordered list element (autocompleteList) using the ‘autocompleteList.appendChild(listItem);’.
After this the code is self-explainable, you can understand what’s happening. Now save the files and run the server again, you will see the following page in the browser.
In the above picture, the filter ‘Skill’ is selected and within the search bar when the word ‘Py’ is typed, the autocomplete list appears related to that word below the search bar as you can see in the above output.
Now you have created the autocomplete search with filter Location and Skill, but if you see the according to the designing or styling perspective, the page or the search bar doesn’t look good.
Let’s work on the designing or styling part of the page, for that, you will use the CSS. Add the tag ‘<style></style>’ to include the CSS code in the ‘index.html’ file. Within that tag, add the following code.
<style>
body {
background-color: #85cff7;
font-family: 'Helvetica', 'Arial', sans-serif;
}
.container {
width: 100%;
min-height: 100vh;
padding: 5%;
background-position: center;
background-size: cover;
display: flex;
align-items: center;
justify-content: center;
}
.search_filter {
width: 100%;
max-width: 700px;
background: rgba(255, 255, 255, 0.2);
position: relative;
display: flex;
align-items: center;
padding: 10px 20px;
border-radius: 60px;
}
.search_filter input {
background: transparent;
outline: none;
flex: 1;
border: 0;
padding: 25px 20px;
font-size: 20px;
}
.search_filter button {
background: transparent;
border: none;
padding: 0%;
width: 100px;
color: #ffffff;
text-align: center;
border-radius: 10px;
font-size: 20px;
cursor: pointer;
}
.search_filter select {
background: transparent;
border: none;
padding: 0%;
width: 100px;
text-align: center;
border-radius: 10px;
font-size: 20px;
cursor: pointer;
}
.autocomplete-list {
position: absolute;
z-index: 1;
top: calc(100% + 5px);
left: 0;
min-width: calc(100% - 40px);
margin: 0;
padding: 0;
list-style: none;
background: #fff;
color: #333;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.autocomplete-list li {
padding: 0.5em;
cursor: pointer;
}
.autocomplete-list li:hover {
background: #f2f2f2;
}
</style>
Again save the file ‘index.html’ and refresh the browser, select the Skill from the filter, and type any word related to the skill.
Now you have successfully implemented the Search Autocomplete with Filter in Django.
Conclusion
In this Python tutorial, you learned how to implement autocomplete search bar with a filter Location and skill in Django. Additionally, you learned how to serialize the data and use it in javascript to implement the autocomplete functionality.
You may like to read:
- Python Django search with dropdown filter
- Django CRUD with Bootstrap Template
- How to Create Card with Button in Django
I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.