Search code examples
djangoorm

Django: count duoble nested revers Foreignkey


my models:

class Course(models.Model):
    ...


class Section(models.Model):
    course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='sections')
    ...


class SubSection(models.Model):
    section = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='subsections')
    registration_required = models.BooleanField(default=True)

now I want to know if less than 5 subsections of a course are registration_required = False. something like:

def validate(self, attrs):
    course = self.instance
    
    if course.sections.subsections.filter(registration_required=False).count() < 5:
        # do something ...

what is the best way to do that ??


Solution

  • You can check for a single Course object course with:

    Subsection.objects.filter(
        registration_required=False, section__course=my_course
    ).count() < 5

    or with an aggregate:

    from django.db.models import Count, Q
    
    my_course.sections.aggregate(
        total=Count(
            'subsections',
            filter=Q(subsections__registration_required=False),
        )
    )['total'] < 5

    If you want to do this in bulk, you use:

    from django.db.models import Count, Q
    
    Course.objects.alias(
        nsubsec=Count(
            'sections__subsections',
            filter=Q(sections__subsections__registration_required=False),
        )
    ).filter(nsubsec__lt=5)

    Here we thus first count the number of SubSections with registration_required=False, and then we filter the Courses on that number (nsubsec) such that it is less than five.