在Django的.values()方法中获取Seller模型对象的方法
问题描述
我有如下销售数据:
| 卖家名称 | 销售日期 |
|---|---|
| Alice | 2023/05/01 |
| Bob | 2023/05/03 |
| Alice | 2023/05/15 |
| Alice | 2023/05/31 |
| Carl | 2023/05/01 |
| Carl | 2023/05/04 |
| Bob | 2023/05/20 |
| Carl | 2023/05/21 |
我用下面的Django查询集获取每个卖家的首次和末次销售日期:
sales = (Sale.objects.filter(created_at__year=2023, created_at__month=5) .values('seller') .annotate(min_date=Min('created_at'), max_date=Max('created_at')) .order_by() )
查询能正常运行,但返回结果里的seller字段是ID,不是Seller模型对象:
{'seller': 1, 'min_date': datetime.datetime(2023, 5, 1, 3, 52, 12, tzinfo=<UTC>), 'max_date': datetime.datetime(2023, 5, 31, 7, 32, 1, tzinfo=<UTC>)} {'seller': 2, 'min_date': datetime.datetime(2023, 5, 3, 4, 34, 11, tzinfo=<UTC>), 'max_date': datetime.datetime(2023, 5, 20, 5, 52, 1, tzinfo=<UTC>)} {'seller': 3, 'min_date': datetime.datetime(2023, 5, 1, 11, 50, 5, tzinfo=<UTC>), 'max_date': datetime.datetime(2023, 5, 21, 10, 21, 1, tzinfo=<UTC>)}
怎么修改查询集才能获取到Seller模型对象?
更新:我的models.py相关代码如下:
class Seller(models.Model): name = models.CharField(max_length=100, blank=True, default='') class Sale(models.Model): seller = models.ForeignKey(Seller, on_delete=models.CASCADE, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True)
解决方案
方法1:从Seller模型反向查询(推荐)
直接从Seller模型出发,关联Sale表并做聚合统计,返回的就是完整的Seller对象,同时带上聚合的日期字段:
from django.db.models import Min, Max sellers_with_dates = Seller.objects.filter( sale__created_at__year=2023, sale__created_at__month=5 ).annotate( min_date=Min('sale__created_at'), max_date=Max('sale__created_at') ).distinct()
使用时直接访问对象属性即可:
for seller in sellers_with_dates: print(seller.name) print(seller.min_date) print(seller.max_date)
这种方式只需要一次数据库查询,效率最高,代码也最简洁。
方法2:保留Sale查询并获取卖家完整信息
如果坚持从Sale模型发起查询,可以在values中指定卖家的具体字段,结合select_related预加载关联数据,返回包含卖家信息的字典:
sales = (Sale.objects.filter(created_at__year=2023, created_at__month=5) .select_related('seller') .values('seller__id', 'seller__name') .annotate(min_date=Min('created_at'), max_date=Max('created_at')) .order_by() )
返回结果示例:
{'seller__id': 1, 'seller__name': 'Alice', 'min_date': ..., 'max_date': ...}
你可以直接使用字典中的卖家信息,或者手动组装成Seller对象。
方法3:遍历结果时补全卖家对象(不推荐)
如果不想修改原查询结构,可以在遍历结果时根据ID查询卖家,但这种方式会触发N+1次数据库查询,仅适合数据量极小的场景:
for item in sales: item['seller_obj'] = Seller.objects.get(id=item['seller'])
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

