You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:24:20