Trobles to load a partial for a dependent drop-down list in a Django template

  django, django-models, django-views, jquery, python

I’m trying to implement a dependent drop down list between countries and cities, but the document responsible to load the cities doesn’t load. I’m trying to solve this problem since 3 days and I don’t get a clue. The database has no problem because I tested it with non-ajax requests. And it is not a database problem, because the entire partial doesn’t load.

The most probable cause of the problem is that I based some code on online tutorial which probably use old versions either of JQuery or Django. But, here’s the code

Models.py

from django.db import models

class Country(models.Model):
    class Meta:
        db_table = 'countries'
    
    name = models.CharField(max_length=300)

    def __str__(self):
        return self.name

class City(models.Model):
    class Meta:
        db_table = 'cities'

    name = models.CharField(max_length=300)
    state_id = models.BigIntegerField(null=True)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)
    
    def __str__(self):
        return self.name
    
class Person(models.Model):
    class Meta:
        db_table = 'people'
    
    name = models.CharField(max_length=300)
    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

Forms.py

from django import forms
from .models import Person, City

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = ('name', 'birthdate', 'country', 'city')
    
    """ right now we are overriding the default init method, and setting the queryset of the city field to an empty list of cities: """
    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:
            pass
            # self.fields['city'].queryset = self.instance.country.city_set.order_by('name')

urls.py from project

from django.urls import path, include
from django.views.generic import RedirectView
from django.conf.urls.i18n import i18n_patterns
import debug_toolbar
from django.conf import settings

urlpatterns = i18n_patterns(
    path('', RedirectView.as_view(pattern_name='person_changelist'), name='home'),
    path('countries/', include('countries.urls')),
    path('__debug__/', include(debug_toolbar.urls)),
    prefix_default_language=False
)

urls.py from Coutries app

from django.urls import include, path
from . import views

urlpatterns = [
    path('', views.PersonListView.as_view(), name='person_changelist'),
    path('add/', views.PersonCreateView.as_view(), name='person_add'),
    path('<int:pk>/', views.PersonUpdateView.as_view(), name='person_change'),
    path('ajax/load-cities/', views.load_cities, name='ajax_load_cities'),
    path('man/load-cities/<int:pk>/', views.load_cities2, name='man_load_cities'),
]

Views.py

from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView
from django.urls import reverse_lazy
from .models import Person, City
from .forms import PersonForm


class PersonListView(ListView):
    model = Person
    context_object_name = 'people'


class PersonCreateView(CreateView):
    model = Person
    form_class = PersonForm
    success_url = reverse_lazy('person_changelist')


class PersonUpdateView(UpdateView):
    model = Person
    form_class = PersonForm
    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, 'countries/city_dropdown_list_options.html', {'cities': cities})

def load_cities2(request, pk):
    # country_id = request.GET.get('country')
    country_id = pk
    cities = City.objects.filter(country_id=country_id).order_by('name')
    return render(request, 'countries/city_dropdown_list_options.html', {'cities': cities})

person_form.html

{% extends 'base.html' %}
{% load i18n %}
{# NOTE: templates must be defined into a folder having the same name than 
the application, like here countries/person_list.html, because Django 
automatically search from <template_folder_in_app_folder/app_name/template.html #}
{% block content %}
    <h2>{% trans "Person Form" %}</h2>
    <form method="post" novalidate>
        {% csrf_token %}
        <table>
            {{ form.as_table }}
        </table>
        <button type="submit">Save</button>
        <a href="{% url 'person_changelist' %}">Nevermind</a>
    </form>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
    $("#id_country").change(function (){
        // getting the url of the load_cities view
        var url = $("#personForm").attr("data-cities-url");
        // get the selected country id from the html element
        var countryId = $(this).val(); 

        $.ajax({
            url: url,
            data: {
                // adding the countryId to GET parameters
                'country': countryId
            },
            // data is the return value of the 'load_cities' function
            success: function(data){
                // replace the contents of the city input with the data 
                // that came from the server. 
                // console.log($("#id_city").html(data));
                $("#id_city").html(data); 
            }
        });
    });
</script>
{% endblock %}

And finally the template that doesn’t load:
city_dropdown_list_options.html

<option value="">----city list----</option>
<!-- THIS DOCUMENT DOESN'T LOAD EVEN IF I COMMENT THE FOLLOWING I DON'T EVEN GET THE <option value="">----city list----</option> -->
{% for city in cities %}
    <option value="{{ city.pk }}">{{ city.name }}</option>
{% endfor %}
#>

Do you have any clue?

Source: Python Questions

LEAVE A COMMENT