I am using django and postgresql. I'm dealing with a 3-way dependent drop-down list. After adding the country selection, the province area is automatically updated depending on the selected country. After selecting a province, the county field opens only when the page is refreshed. I would be glad if you help me in this regard.
from django.db import models
class Country(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
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 District(models.Model):
city = models.ForeignKey(City, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
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)
district = models.ForeignKey(District, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
from django import forms
from .models import Person, Country, City, District
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('name', 'birthdate', 'country', 'city', 'district')
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')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['district'].queryset = District.objects.none()
if 'city' in self.data:
try:
city_id = int(self.data.get('city'))
self.fields['district'].queryset = District.objects.filter(city_id=city_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty District queryset
elif self.instance.pk:
self.fields['district'].queryset = self.instance.city.district_set.order_by('name')
def load_cities(request):
country_id = request.GET.get('country')
cities = City.objects.filter(country_id=country_id).order_by('name')
return render(request, 'neighbor/city_dropdown_list_options.html', {'cities': cities})
def load_districts(request):
city_id = request.GET.get('city')
districts = District.objects.filter(city_id=city_id).order_by('name')
return render(request, 'neighbor/district_dropdown_list_options.html', {'districts': districts})
<h2>Person Form</h2>
<form method="post" id="personForm" data-cities-url="{% url 'ajax_load_cities' %}" 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 () {
var url = $("#personForm").attr("data-cities-url");
var countryId = $(this).val();
$.ajax({
url: url,
data: {
'country': countryId
},
success: function (data) {
$("#id_city").html(data);
}
});
});
$("#id_city").change(function () {
var url = $("#personForm").attr("data-districts-url");
var cityId = $(this).val();
$.ajax({
url: url,
data: {
'city': cityId
},
success: function (data) {
$("#id_district").html(data);
}
});
});
</script>
I'm referring to the example here.
In your templates.html file, you are calling the same URL hence the same view for country and city fields change. you need to make a new URL to handle the request in load_district view.
make the following changes
$("#id_city").change(function () { var url = $("#personForm").attr("data-districts-url"); var cityId = $(this).val(); $.ajax({ url: '{% url 'ajax_load_districts' %}', data: { 'city': cityId }, success: function (data) { $("#id_district").html(data); } }); });
you don't need to modify init() method twice, just remove these 2 lines from froms.py. (line number 22 and 23)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
add a new URL for handling ajax request for district
path('ajax/load-district/', views.load_districts, name='ajax_load_districts'),
you can clone this example, i have used Country, City, Vanue... https://github.com/masbhanoman/django_3way_chained_dropdown_list
Thank you very much for your answer and the link to GitHub. It works perfectly with 3 dependent dropdown lists. You can still slightly improve your JS code:
var url = $("#personForm").attr("data-districts-url");
can be deleted as you don't use the variable and directly set an url, which, by the way, makes your code quite readable.
One addition point here is regarding the id formatting, so in my case was necessary to use {{ value|safe }} on dropdown, so:
<option value="">---------</option>
{% for city in cities %}
<option value="{{ city.pk|safe }}">{{ city.name }}</option>
{% endfor %}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.