In this tutorial we are going to learn dynamic dependent select box using jquery, ajax Country and City.
This type of feature mostly use if you have use Country State City or you have working with Category
and you want to load Sub Category of particular category. models.py
#models.py
from django.db import models
class Country(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Meta:
# managed = True
db_table = 'myapp_country'
class City(models.Model):
country = models.ForeignKey(Country, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Meta:
# managed = True
db_table = 'myapp_city'
class Person(models.Model):
name = models.CharField(max_length=100)
birthdate = models.DateField(null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True)
city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class Meta:
# managed = True
db_table = 'myapp_person'
Database Table
CREATE TABLE myapp_country (
id INTEGER NOT NULL
PRIMARY KEY AUTOINCREMENT,
name VARCHAR (30) NOT NULL
);
INSERT INTO myapp_country (id, name) VALUES (1, 'Philippines');
INSERT INTO myapp_country (id, name) VALUES (2, 'United States');
INSERT INTO myapp_country (id, name) VALUES (3, 'Russia');
INSERT INTO myapp_country (id, name) VALUES (4, 'Brazil');
INSERT INTO myapp_country (id, name) VALUES (5, 'United Kingdom');
CREATE TABLE myapp_city (
id INTEGER NOT NULL
PRIMARY KEY AUTOINCREMENT,
name VARCHAR (30) NOT NULL,
country_id INTEGER NOT NULL
REFERENCES myapp_country (id) DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO myapp_city (id, name, country_id) VALUES (1, 'Olongapo City', 1);
INSERT INTO myapp_city (id, name, country_id) VALUES (2, 'Angeles City', 1);
INSERT INTO myapp_city (id, name, country_id) VALUES (3, 'Davao City', 1);
INSERT INTO myapp_city (id, name, country_id) VALUES (4, 'Pasay City', 1);
INSERT INTO myapp_city (id, name, country_id) VALUES (5, 'Manila City', 1);
INSERT INTO myapp_city (id, name, country_id) VALUES (6, 'New York', 2);
INSERT INTO myapp_city (id, name, country_id) VALUES (7, 'San Francisco', 2);
INSERT INTO myapp_city (id, name, country_id) VALUES (8, 'Los Angeles', 2);
INSERT INTO myapp_city (id, name, country_id) VALUES (9, 'Chicago', 2);
INSERT INTO myapp_city (id, name, country_id) VALUES (10, 'Seattle', 2);
INSERT INTO myapp_city (id, name, country_id) VALUES (11, 'Moscow', 3);
INSERT INTO myapp_city (id, name, country_id) VALUES (12, 'Saint Petersburg', 3);
INSERT INTO myapp_city (id, name, country_id) VALUES (13, 'Yekaterinburg', 3);
INSERT INTO myapp_city (id, name, country_id) VALUES (14, 'Kazan', 3);
INSERT INTO myapp_city (id, name, country_id) VALUES (15, 'Krasnodar', 3);
INSERT INTO myapp_city (id, name, country_id) VALUES (16, 'Sao Paulo', 4);
INSERT INTO myapp_city (id, name, country_id) VALUES (17, 'Rio de Janeiro', 4);
INSERT INTO myapp_city (id, name, country_id) VALUES (18, 'Belo Horizonte', 4);
INSERT INTO myapp_city (id, name, country_id) VALUES (19, 'Curitiba', 4);
INSERT INTO myapp_city (id, name, country_id) VALUES (20, 'Recife', 4);
INSERT INTO myapp_city (id, name, country_id) VALUES (21, 'London', 5);
INSERT INTO myapp_city (id, name, country_id) VALUES (22, 'Huddersfield', 5);
INSERT INTO myapp_city (id, name, country_id) VALUES (23, 'Glasgow', 5);
INSERT INTO myapp_city (id, name, country_id) VALUES (24, 'Edinburgh', 5);
INSERT INTO myapp_city (id, name, country_id) VALUES (25, 'Cambridge', 5);
CREATE TABLE myapp_person (
id INTEGER NOT NULL
PRIMARY KEY AUTOINCREMENT,
name VARCHAR (100) NOT NULL,
birthdate DATE,
city_id INTEGER REFERENCES myapp_city (id) DEFERRABLE INITIALLY DEFERRED,
country_id INTEGER REFERENCES myapp_country (id) DEFERRABLE INITIALLY DEFERRED
);
urls.py
#urls.py
from django.contrib import admin
from django.urls import path
from myapp import views
from django.conf.urls import url
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.PersonListView.as_view(), name='person_changelist'),
path('add/', views.PersonCreateView.as_view(), name='person_add'),
path('/', views.PersonUpdateView.as_view(), name='person_change'),
path('ajax/load-cities/', views.load_cities, name='ajax_load_cities'),
]
views.py
#views.py
from django.shortcuts import render, redirect
from django.views.generic import ListView, CreateView, UpdateView
from django.urls import reverse_lazy
from myapp.models import Person, City
from myapp.forms import PersonForm
class PersonListView(ListView):
model = Person
context_object_name = 'people'
template_name = 'person_list.html'
class PersonCreateView(CreateView):
model = Person
form_class = PersonForm
template_name = 'person_form.html'
success_url = reverse_lazy('person_changelist')
class PersonUpdateView(UpdateView):
model = Person
form_class = PersonForm
template_name = 'person_form.html'
success_url = reverse_lazy('person_changelist')
def load_cities(request):
country_id = request.GET.get('country')
cities = City.objects.filter(country_id=country_id).order_by('name')
return render(request, 'city_dropdown_list_options.html', {'cities': cities})
def load_cities(request): This view will be used via AJAX requests.forms.py
#forms.py
from django import forms
from myapp.models import Person, City
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('name', 'birthdate', 'country', 'city')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['city'].queryset = City.objects.none()
if 'country' in self.data:
try:
country_id = int(self.data.get('country'))
self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty City queryset
elif self.instance.pk:
self.fields['city'].queryset = self.instance.country.city_set.order_by('name')
templates/person_list.html
//templates/person_list.html
{% extends 'base.html' %}
{% block title %}Django Chained Dropdown List - Select Box using jQuery Ajax Country City{% endblock %}
{% block content %}
<table class="table table-striped custab">
<thead>
<a href="{% url 'person_add' %}" class="btn btn-primary btn-xs pull-right"><b>+</b> Add new Person</a>
<tr>
<th>Name</th>
<th>Birthdate</th>
<th>Country</th>
<th>City</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
{% for person in people %}
<tr>
<td><a href="{% url 'person_change' person.pk %}">{{ person.name }}</a></td>
<td>{{ person.birthdate }}</td>
<td>{{ person.country.name }}</td>
<td>{{ person.city.name }}</td>
<td class="text-center"><a class='btn btn-info btn-xs' href="#"><span class="glyphicon glyphicon-edit"></span> Edit</a> <a href="#" class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-remove"></span> Del</a></td>
</tr>
{% empty %}
<tr>
<td colspan="4">No person in the database. <a href="{% url 'person_add' %}">Add the first person</a>.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
templates/person_form.html
//templates/person_form.html
{% extends 'base.html' %}
{% block content %}
{% load widget_tweaks %}
<h2>Person Form</h2>
<form method="post" id="personForm" data-cities-url="{% url 'ajax_load_cities' %}" novalidate>
{% csrf_token %}
<table>
<!-- {{ form.as_table }} -->
</table>
{% for field in form.visible_fields %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field|add_class:'form-control' }}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-success">Save</button>
<a href="{% url 'person_changelist' %}">Home</a>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_country").change(function () {
var url = $("#personForm").attr("data-cities-url");
var countryId = $(this).val();
$.ajax({
url: url,
data: {
'country': countryId
},
success: function (data) {
$("#id_city").html(data);
}
});
});
</script>
{% endblock %}
templates/city_dropdown_list_options.html
//templates/city_dropdown_list_options.html
<option value="">---------</option>
{% for city in cities %}
<option value="{{ city.pk }}">{{ city.name }}</option>
{% endfor %}
templates/base.html
//templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
<title>{% block title %}Django Chained Dropdown List - Select Box using jQuery Ajax Country City{% endblock %}</title>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand">Cairocoders</a>
</div>
</nav>
<div class="container">
<div class="col-md-12 well">
<h3 class="text-primary">Django Chained Dropdown List - Select Box using jQuery Ajax Country City</h3>
<hr style="border-top:1px dotted #ccc;"/>
{% block content %} {% endblock %}
</div>
</div>
<style>
.custab{
border: 1px solid #ccc;
padding: 5px;
margin: 5% 0;
box-shadow: 3px 3px 2px #ccc;
transition: 0.5s;
}
.custab:hover{
box-shadow: 3px 3px 0px transparent;
transition: 0.5s;
}
</style>
</body>
</html>
settings.py
INSTALLED_APPS = [
'myapp',
'widget_tweaks', #pip install django-widget-tweaks https://pypi.org/project/django-widget-tweaks/
]
