Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
338 views
in Technique[技术] by (71.8m points)

django - Filtering object with multiple related models with M2M and ForeignKey

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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...