I want to filter university programs in list view. I have near to 200000 objects. When I do the query in ProgramSearchView I get double the number of objects because of "subject__parent__title". I am very bad at queries. How can I write the most efficient query here and which query do you think suites the most?
Note: I've shortened fields in some models in order to decrease complexity here.
My Model:
class Program(models.Model):
slug = models.SlugField(null=False, blank=False, unique=True, max_length=255)
program_id = models.IntegerField(_("ID"))
title = models.CharField(max_length=199)
date_added = models.CharField(max_length=99, blank=True, null=True)
requirements = JSONField(blank=True, null=True)
details = JSONField(blank=True, null=True)
academic_award = models.ManyToManyField("program.AcademicAward", verbose_name=_("Academic award"))
subject = models.ManyToManyField("program.Subject", verbose_name=_("Subject"), blank=True)
school = models.ManyToManyField("program.School", related_name='school', blank=True)
class AcademicAward(models.Model):
title = models.CharField(_("title"), max_length=250) # for ex. M.Sc., B.Sc.
description = models.CharField(_("about"), max_length=450, null=True, blank=True)
category = models.CharField(_("Category"), max_length=150, blank=True, null=True) # for ex. Degree, Certificate or Diploma
class School(models.Model):
title = models.CharField(_("Title"), max_length=950)
description = models.CharField(_("About"), max_length=950, blank=True, null=True)
campus = models.ForeignKey("program.Campus", verbose_name=_("Campus"), on_delete=models.CASCADE, max_length=255)
class Campus(models.Model):
title = models.CharField(_("title"), max_length=150)
description = models.CharField(_("About"), max_length=950, null=True, blank=True)
city = models.CharField(_("city"), max_length=150, null=True, blank=True)
country = models.CharField(_("country"), max_length=150, null=True, blank=True)
university = models.ForeignKey("program.University", verbose_name=_("university"), on_delete=models.CASCADE, max_length=255)
class University(models.Model):
title = models.CharField(_("Title"), max_length=250, null=True, blank=True)
description = models.CharField(_("Description"), max_length=950, null=True, blank=True)
accreditations = JSONField(null=True, blank=True)
logo = models.URLField(_("Logo"), max_length=900, null=True, blank=True)
class Subject(models.Model):
title = models.CharField(_("title"), max_length=250)
description = models.CharField(_("Description"), max_length=950, blank=True, null=True)
parent = models.ManyToManyField("program.SubjectField")
class SubjectField(models.Model):
title = models.CharField(_("title"), max_length=250)
description = models.CharField(_("Description"), max_length=950, null=True, blank=True)
child = models.ManyToManyField("program.Subject")
My view:
class ProgramListView(TemplateView):
template_name = "program/program_list.html"
def ProgramSearchView(request):
academic_award = request.GET.getlist('award', None)
subject_field = request.GET.getlist('subject_field', None)
country = request.GET.getlist('country', None)
delivery = request.GET.getlist('delivery', None)
ielts_min = request.GET.get('ielts_min', None)
ielts_max = request.GET.get('ielts_max', None)
def int_or_float(s):
try:
return int(s)
except ValueError:
return float(s)
programs = Program.objects.values('academic_award__title', 'pk', 'title', 'details__tuition_fee', 'details__attendance', 'school__campus__city', 'school__campus__country', 'school__campus__university__title', 'requirements__language', 'subject__parent__title')
if academic_award:
programs = programs.filter(academic_award__title__in=academic_award)
if subject_field:
programs = programs.filter(subject__parent__title__in=subject_field)
if country:
programs = programs.filter(school__campus__country__in=country)
if delivery:
for x in delivery:
programs = programs.filter(details__attendance__icontains=x)
if ielts_min:
programs = programs.filter(requirements__language__ielts__gte=int_or_float(ielts_min))
if ielts_max:
programs = programs.filter(requirements__language__ielts__lte=int_or_float(ielts_max))
paginator = Paginator(programs, 7)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
# Sending get_absolute_url() of every object to JSON and also deleting PK.
[ obj.update({'slug': Program.objects.get(pk = obj.pop('pk')).get_absolute_url()}) for obj in page_obj.object_list ]
list(page_obj)
data={ # https://stackoverflow.com/questions/44343425/how-to-show-total-pages-in-json-response-in-django-pagination
'results': page_obj.object_list,
'total_records': paginator.count,
'total_pages': paginator.num_pages,
'page': page_obj.number,
'has_next': page_obj.has_next(),
'has_prev': page_obj.has_previous(),
}
return JsonResponse(data, safe=False)