Django中通过中间表使用Prefetch并添加过滤条件的问题
Django多对多关联查询问题:预取活跃成员时出现重复表连接
模型定义
class Person(models.Model): name = models.CharField(max_length=255) class Group(models.Model): name = models.CharField(max_length=255) members = models.ManyToManyField(Person, through="Membership") class Membership(models.Model): group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name="membership") person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="membership") is_active = models.BooleanField(null=True, blank=True)
Membership模型中的is_active字段表示用户是否仍为群组成员。
尝试的查询代码
想要查询群组并预取所有活跃成员,使用了以下代码:
from sample.models import Person, Group from django.db.models import Prefetch from django.db import connection, reset_queries reset_queries() qs = Group.objects.prefetch_related( Prefetch( "members", queryset=Person.objects.filter(membership__is_active=True), ) ) print(list(qs.all())) print(len(connection.queries)) # 预期应该是2次查询
问题:生成的SQL出现重复表连接
生成的预取SQL中,sample_membership表被连接了两次,且只有一个应用了is_active过滤:
SELECT ("sample_membership"."group_id") AS "_prefetch_related_val_group_id", "sample_person"."id", "sample_person"."name" FROM "sample_person" INNER JOIN "sample_membership" ON ( "sample_person"."id" = "sample_membership"."person_id") INNER JOIN "sample_membership" T3 ON ( "sample_person"."id" = T3."person_id") WHERE "sample_membership"."is_active" AND T3."group_id" IN (1, 2)
补充:序列化器代码
serializers.py文件内容:
from rest_framework import serializers from .models import Person, Group class PersonSerializer(serializers.ModelSerializer): class Meta: model = Person fields = '__all__' class GroupSerializer(serializers.ModelSerializer): members = PersonSerializer(many=True) class Meta: model = Group fields = '__all__'
希望在GroupSerializer的输出中仅包含拥有活跃Membership的成员,测试代码如下:
from sample.models import Person, Group, Membership from sample.serializers import GroupSerializer from django.db.models import Prefetch from django.db import connection, reset_queries reset_queries() qs = Group.objects.prefetch_related( Prefetch( "membership", queryset=Membership.objects.filter(is_active=True).select_related("person"), ) ) for q in list(qs.all()): gs = GroupSerializer() gs.to_representation(instance=q)
内容的提问来源于stack exchange,提问作者willowherb
相关产品推荐
相关产品推荐

