如何在Django Filter查询中按需应用条件查找?
Django数据库查询优化:条件化筛选已付费顾客可访问的食物
模型定义
class Customer(models.Model): food_type = models.CharField() fruit_id = models.ForeignKey(Fruit, null=True) vegetable_id = models.ForeignKey(Vegetable, null=True) is_paid = models.BooleanField() class Food(models.Model): fruit_id = models.ForeignKey(Fruit, null=True) vegetable_id = models.ForeignKey(Vegetable, null=True)
原实现方式
q = Food.objects.all() if Customer.objects.filter(id=(id of customer), food_type='fruit', is_paid=True).exists(): q = q.filter(fruit_id__in=Customer.objects.filter(id=(id of customer), food_type='fruit', is_paid=True).values_list('fruit_id', flat=True)) if Customer.objects.filter(id=(id of customer), food_type='vegetable', is_paid=True).exists(): q = q.filter(vegetable_id__in=Customer.objects.filter(id=(id of customer), food_type='vegetable', is_paid=True).values_list('vegetable_id', flat=True))
优化方案:减少数据库查询次数
原代码每个判断逻辑都会触发2次数据库查询,可通过以下方式大幅降低查询量:
方法1:一次性获取权限后内存处理(仅1次查询)
先拉取该顾客所有已付费的食物关联记录,再在内存中整理筛选条件:
# 仅执行1次查询,获取顾客所有已付费的食物权限 customer_paid_entries = Customer.objects.filter( id=customer_id, is_paid=True ).values_list('food_type', 'fruit_id', 'vegetable_id') q = Food.objects.all() fruit_ids = [] vegetable_ids = [] # 内存中整理符合条件的ID for entry in customer_paid_entries: food_type, fruit_id, veg_id = entry if food_type == 'fruit' and fruit_id: fruit_ids.append(fruit_id) elif food_type == 'vegetable' and veg_id: vegetable_ids.append(veg_id) # 构建或条件筛选 filters = Q() if fruit_ids: filters |= Q(fruit_id__in=fruit_ids) if vegetable_ids: filters |= Q(vegetable_id__in=vegetable_ids) if filters: q = q.filter(filters)
方法2:子查询+Exists实现(仅1次查询)
利用Django的子查询能力,直接在数据库层面完成关联筛选,全程仅发1条SQL:
from django.db.models import Subquery, Q, Exists, OuterRef # 定义顾客已付费水果的子查询逻辑 paid_fruits = Customer.objects.filter( id=customer_id, food_type='fruit', is_paid=True, fruit_id=OuterRef('fruit_id') ) # 定义顾客已付费蔬菜的子查询逻辑 paid_vegetables = Customer.objects.filter( id=customer_id, food_type='vegetable', is_paid=True, vegetable_id=OuterRef('vegetable_id') ) # 合并筛选条件:匹配已付费水果或已付费蔬菜 q = Food.objects.filter( Q(Exists(paid_fruits)) | Q(Exists(paid_vegetables)) )
关于使用When()条件表达式的可行性
When()主要用于字段值的条件赋值(比如annotate或update场景),并不适配这种动态筛选需求。如果硬要实现,写法会冗余且性能不如上述方案:
from django.db.models import Case, When, BooleanField, Value, Subquery q = Food.objects.annotate( is_allowed=Case( When( fruit_id__in=Subquery(Customer.objects.filter(id=customer_id, food_type='fruit', is_paid=True).values('fruit_id')), then=Value(True) ), When( vegetable_id__in=Subquery(Customer.objects.filter(id=customer_id, food_type='vegetable', is_paid=True).values('vegetable_id')), then=Value(True) ), default=Value(False), output_field=BooleanField() ) ).filter(is_allowed=True)
这种方式会生成更复杂的SQL,性能和可读性都不如Q对象+Exists的方案,不推荐使用。
内容的提问来源于stack exchange,提问作者Pooja Kumari
相关产品推荐
相关产品推荐

