Reputation: 57
I am using the following models and forms to build two dropdown menus. The City menu is dependent upon the Country menu. Right now my code does the filtering and ordering correctly. However, distinct() is not working. I've played around with the ordering of the queries for a while and it still doesn't work. How can I fix this?
models.py
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 Person(models.Model):
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 = ('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().distinct()
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')
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, 'hr/city_dropdown_list_options.html', {'cities': cities})
Upvotes: 2
Views: 1141
Reputation: 424
When the database server runs an ordered distinct query, it will:
WHERE
clause.ORDER BY
clause.DISTINCT
from the previous (ordered) emitted row.Since you have explicitly disabled ordering by not passing any field names to order_by()
, the database is free to emit rows in whatever order it chooses (typically in the order they retrieved from storage).
DISTINCT
filtering doesn't apply across the whole result set, it only applies against the preceding emitted row. And since your results are being emitted in an undefined (random) order, DISTINCT
isn't likely to filter any results.
You must order your results accordingly by specifying field names in the order_by(*fields)
part of your query.
Upvotes: 1