优化Django中多过滤与排除语句的QuerySet性能咨询
优化方案:提升查询效率
你的这段代码确实有优化空间,主要可以从减少查询次数、优化数据库过滤逻辑这两个方向入手,下面是具体的实现和说明:
优化后的代码实现
from django.db.models import Q, Exists, OuterRef def get_queryset(self): current_user = self.request.user # 定义拉黑用户的子查询:匹配当前用户拉黑的、且与Profile关联的用户 blocked_subquery = Blocked.objects.filter( user=current_user, blocked_user=OuterRef('user') ) # 定义已挥手用户的子查询:匹配当前用户已挥手的、且与Profile关联的用户 waved_subquery = UserWave.objects.filter( user=current_user, waved_user=OuterRef('user') ) # 一次过滤完成所有条件:排除自己、未被拉黑、未挥手过的用户 return Profile.objects.filter( ~Q(user=current_user), ~Exists(blocked_subquery), ~Exists(waved_subquery) )
关键优化点说明
- 用Exists子查询替代values+user__in:原代码会先执行两次独立查询(获取拉黑用户ID列表、已挥手用户ID列表),再传给Profile的exclude,总共3次SQL查询。改用Exists后,只会执行1次Profile查询,两个子查询会作为WHERE条件的一部分在数据库层面完成过滤,减少了数据传输和查询开销。
- 合并过滤条件:把多次exclude合并成一个filter操作,用
~Q()和~Exists()组合条件,让SQL语句更简洁,数据库能生成更高效的执行计划。 - 直接使用用户对象关联:避免手动提取user.id,Django会自动处理外键关联逻辑,代码更简洁且不易出错。
额外性能优化建议:添加数据库索引
因为你的查询频繁基于user、blocked_user、waved_user这些字段过滤,给这些字段添加索引能大幅提升查询速度:
修改Blocked模型
class Blocked(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_thats_blocking", db_index=True) blocked_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="blocked_user", db_index=True) class Meta: # 添加联合索引,针对同时过滤user和blocked_user的场景 indexes = [ models.Index(fields=['user', 'blocked_user']), ]
修改UserWave模型
class UserWave(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="current_user", db_index=True) waved_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="waved_user", db_index=True) class Meta: # 添加联合索引,针对同时过滤user和waved_user的场景 indexes = [ models.Index(fields=['user', 'waved_user']), ]
Profile模型(可选)
OneToOneField默认会创建索引,但可以显式声明确保:
class Profile(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.OneToOneField(User, on_delete=models.CASCADE, db_index=True) bio = models.TextField(null=True, blank=True)
内容的提问来源于stack exchange,提问作者rafaelHTML
相关产品推荐
相关产品推荐

