8

Here are my models

class Student:
    user  = ForeignKey(User)
    department = IntegerField()
    semester = IntegerField()
    ...

class Attendance:
    student = ForeignKey(Student)
    subject = ForeignKey(Subject)
    month = IntegerField()
    year = IntergerField()
    present = IntegerField() 
    total = IntegerField()

students = Student.objects.filter(semester=semester)

How can I perform a right join between Student and Attendance models, so that I can get a queryset with all of the students and attendances` if exists for a student, else null?

The documentation mentions left joins but not right joins.

rjv
  • 6,058
  • 5
  • 27
  • 49

3 Answers3

12

change left join for table subject

queryset.query.alias_map['subject'].join_type = "RIGHT OUTER JOIN"
madjardi
  • 5,649
  • 2
  • 37
  • 37
4

You can use such query:

queryset = Student.objects.all().select_related('attendance_set')
student = queryset[0]
# all attendances for the student
attendances = student.attendance_set.all() 

With select_related you JOIN'ing Attendance table. Django does not have explicit join ORM method, but it does JOIN internally when you call select_related. Resulting queryset will contain all Student's with joined attendances, and when you will call attencande_set.all() on each student - no additional queries will be performed. Check the docs for _set feature.

If you want to query only those students who has at least one attendance, you can use such query:

from django.models import Count
(Student.objects.all()
                .select_related('attendance_set')
                .annotate(n_attendances=Count('attendance_set'))
                .filter(n_attendances__gt=0))
Gill Bates
  • 14,330
  • 23
  • 70
  • 138
  • 10
    Watch out as this won't work - you can't select_related on a related manager (`attendance_set`) and the field name will actually be `attendance` for the `Count`. You need to use `prefetch_related` instead: ```Student.objects.all().prefetch_related('attendance_set').annotate(n_attendances=Count('attendance')).filter(n_attendances__gt=0)``` – Ben Feb 17 '17 at 02:27
0

The right way is using fk_set__isnull=false as show below:

class Father(Model):
    name = ...

class Child(Model):
    father = ForeignKey('Father', on_delete=..., related_name='childs')

>>> fathers_with_childs = Father.objects.filter(childs__isnull=False).all()

this will gave you a query like:

"SELECT * FROM father INNER JOIN child ON (father.id = child.father_id) WHERE child.id IS NOT NULL"