Zloycate
Zloycate

Reputation: 13

Django rest framework How to fix many-to-many n+1 error?

I have models:

class Group(models.Model):
    number = models.CharField(choices=CLASS_NUMBERS,
                              max_length=2,
                              verbose_name="Номер класса")
    name = models.CharField(max_length=20,
                            verbose_name="Название класса")
    group_teacher = models.ForeignKey('users.Teacher',
                                      on_delete=models.CASCADE,
                                      verbose_name="Классный руководитель",
                                      related_name='group_teacher')
    students = models.ManyToManyField('users.Student',
                                      verbose_name="Ученики", blank=True,
                                      related_name='group_students')
    code = models.CharField(max_length=8, unique=True)

class Teacher(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE,
                                verbose_name="Преподаватель", related_name='teacher_user')
class Student(models.Model):
    user = models.OneToOneField(User,
                                on_delete=models.CASCADE,
                                verbose_name="Ученик", related_name='student_user')

and serializers:

class TeacherSerializer(ModelSerializer):
    class Meta:
        model = Teacher
        fields = [
            'user'
        ]

class StudentsSerializer(ModelSerializer):
    class Meta:
        model = Student
        fields = [
            'user'
        ]

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group
        fields = [
            'id',
            'number',
            'name',
            'code',
            'group_teacher',
            'students',
        ]

When I make an appeal to the api I get warnings:

Potential n+1 query detected on Teacher.user Potential n+1 query detected on Teacher.user Potential n+1 query detected on Student.user Potential n+1 query detected on Student.user Potential n+1 query detected on Student.user Potential n+1 query detected on Student.user

Queries

my view set:

class GroupModelViewSet(viewsets.ModelViewSet):
    serializer_class = GroupSerializer

    def get_queryset(self):
        return Group.objects.all().prefetch_related('students')

How to fix these errors n+1, select_related does not let me select the model of students and teachers

Upvotes: 1

Views: 633

Answers (1)

Elabbasy00
Elabbasy00

Reputation: 678

The N+1 query problem happens when the data access framework executed N additional SQL statements to fetch the same data that could have been retrieved when executing the primary SQL query.

You are trying to reach the same database table from all serializers

in your case, this will work

class TeacherSerializer(ModelSerializer):
    class Meta:
        model = Teacher
        fields = "__all__"

class StudentsSerializer(ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group
        fields = [
            'id',
            'number',
            'name',
            'code',
            'group_teacher',
            'students',
        ]

class GroupModelViewSet(viewsets.ModelViewSet):
    serializer_class = GroupSerializer

    def get_queryset(self):
        return Group.objects.all().prefetch_related('group_teacher', 'group_students')
       

Using the related_name allows you to specify a simpler or more legible name to get the reverse relation. In this case, if you specify user = models.ForeignKey(User, related_name='tech_user'), the call would then be User.tech_user.all()

prefetch_related('tech_user')

Upvotes: 1

Related Questions