如何在Django ORM中查询指定客户的Product时关联获取对应Bill的属性
如何在Django ORM中查询指定客户的Product时关联获取对应Bill的属性
嘿,这个需求完全可以用Django ORM轻松实现,我给你几个实用的方案,你可以根据自己的情况选择:
首先先优化下你现有的查询逻辑——不用先查bills再传入,直接通过bill__customer=customer就能关联过滤,这样查询更高效,也更简洁。
方案一:给Product对象添加Bill属性(推荐)
如果你想让查询返回的每个Product实例直接带上对应的bill_id和bill_date,可以用annotate结合Subquery来实现,这样能保留Product的所有原有属性,同时新增你需要的Bill字段:
首先需要导入相关工具:
from django.db.models import Subquery, OuterRef
然后编写查询代码:
# 子查询:获取当前Product关联的第一个Bill(因为你说每个Product只对应一个Bill) bill_subquery = Bill.objects.filter(products=OuterRef('pk')).values('id', 'date')[:1] results = Product.objects.filter( bill__customer=customer, sku__icontains=search_str ).annotate( bill_id=Subquery(bill_subquery.values('id')), bill_date=Subquery(bill_subquery.values('date')) )
之后遍历results时,你就可以直接通过product.bill_id和product.bill_date拿到对应的Bill属性,完全不影响Product原有属性的使用。
方案二:直接查询所需字段(性能最优)
如果你的场景只需要特定字段,不需要完整的Product对象,可以用values()直接指定要查询的字段,返回字典列表,性能会更好:
from django.db.models import F results = Product.objects.filter( bill__customer=customer, sku__icontains=search_str ).annotate( # 给Bill字段重命名,让返回的字典键更友好 bill_id=F('bill__id'), bill_date=F('bill__date') ).values( # 列出你需要的Product字段,比如'id', 'sku'等 'id', 'sku', 'bill_id', 'bill_date' ).distinct() # 多对多关联可能产生重复,用distinct去重
返回的结果会是类似这样的字典:
{'id': 1, 'sku': 'RXT00887', 'bill_id': 5, 'bill_date': datetime.datetime(2024, 5, 20, 10, 30)}
方案三:预加载关联Bill(适合需要完整Bill对象的场景)
如果你需要完整的Bill对象而不只是几个字段,可以用prefetch_related预加载关联数据,避免N+1查询:
results = Product.objects.filter( bill__customer=customer, sku__icontains=search_str ).prefetch_related('bill_set').distinct() # 遍历的时候获取Bill for product in results: bill = product.bill_set.first() # 因为每个Product只对应一个Bill,取第一个即可 bill_id = bill.id if bill else None bill_date = bill.date if bill else None # 这里就可以同时使用product和bill的属性了
额外建议:优化模型关系
你提到每个Product只关联一个Bill或无,那当前用ManyToManyField其实不太贴合实际业务逻辑,建议把模型改成外键关联,这样查询会更简单:
class Product(models.Model): sku = models.CharField(unique=True) # 用外键关联Bill,允许为空表示无关联 bill = models.ForeignKey(Bill, on_delete=models.SET_NULL, null=True, blank=True)
改完模型后,查询就更简洁高效了,用select_related直接左连接获取Bill数据:
results = Product.objects.filter( bill__customer=customer, sku__icontains=search_str ).select_related('bill') # 遍历直接用 for product in results: if product.bill: bill_id = product.bill.id bill_date = product.bill.date
备注:内容来源于stack exchange,提问作者Jalaj Kumar
相关产品推荐
相关产品推荐

