ionecum
ionecum

Reputation: 121

I can't load a Django template partial from a JQuery ajax call

I need to send a list from an ajax call using JQuery, and send a response with Django {{ variables }} inside them, I tried everything, but the JQuery always loads the entire current page instead of loading the html file that contains the template partial, which is just

<!-- city_dropdown_list_options.html -->
<option value="">----city list----</option>
{% for city in cities %}
    <option value="{{ city.pk }}">{{ city.name }}</option>
{% endfor %}

Here's the template where the JQuery is located

person_form.html

{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{# 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>
    <div id="test"></div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
{# <script src="{% static 'js/test.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(data);
                $("#id_city").html(data); 
                // $('#id_city').html(jQuery(data).find('#id_city').html());  
            }            
        });
    });
</script>
{% endblock %}

The 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})

The urls.py at project level

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
)

The urls.py at app level:

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('load-cities/', views.load_cities, name='ajax_load_cities'),
    path('man/load-cities/<int:pk>/', views.load_cities2, name='man_load_cities'),
]

As I said, I receive the entire person_form.html as a response while I expect city_dropdown_list_options.html as a response. Notice that the response must not necessarily be an included html partial, it could also be a simple output generated by the view.

I'm not very good in JQuery and probably there is something I'm doing wrong or missing. Any clue?

Upvotes: 0

Views: 718

Answers (1)

ionecum
ionecum

Reputation: 121

I found a solution

a line like

path('load-cities/', views.load_cities, name='ajax_load_cities'),

was missing in urls.py (application level). This means that a route was not defined to be called by the JQuery ajax call. Therefore the call was

http://127.0.0.1:8000/countries/add/?country=56

which is the same url than the form and this was not properly managed.

I found that the best solution for this kind of problem was to create a dedicated route for the ajax call as I did so the cal become

http://127.0.0.1:8000/countries/add/load-cities/?country=56

Also this like made no sense at all

var url = $("#personForm").attr("data-cities-url");

I replaced it with a direct and clear:

var url = "/countries/load-cities/";

And now everything is fine.

The moral of the story: When you use JQuery and you make ajax calls inside a Django template, never foget to define a dedicated route in urls.py. Remember, you are using a framework with a built-in routing system. PHP_SELF equivalents no longer work here.

3 days of hard labour but I finally got it. I hope that this answer to my own question would help someone else facing the same problem.

Upvotes: 1

Related Questions