article

Saturday, June 6, 2020

Django Chained Dropdown List - Select Box using jQuery Ajax Country City

Django Chained Dropdown List - Select Box using jQuery Ajax Country City
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/
]

Related Post