Django查询:获取每个产品对应各卖家的最新价格
解决Django比价应用中「每个产品对应各卖家最新价格」的问题
嘿,这个需求我之前做电商比价系统的时候刚好碰到过!咱们核心要解决的就是去重(每个卖家仅显示一次)+ 取最新价格的问题,直接遍历所有价格记录肯定会重复,得用Django ORM的子查询和预取来高效实现。
思路分析
我们需要先筛选出「每个产品+每个卖家」组合下的最新一条价格记录,然后把这些记录和产品关联起来,避免模板里出现重复的卖家。
具体实现步骤
1. 优化查询集(核心代码)
用Subquery和Prefetch来高效获取每个产品对应的最新价格集合,避免N+1查询:
from django.db.models import Max, OuterRef, Subquery, Prefetch # 第一步:子查询 - 找出每个产品下,每个卖家的最新价格的添加时间 latest_added_per_seller = Price.objects.filter( part=OuterRef('pk') # 关联外部的Part对象 ).values('seller').annotate( latest_added=Max('added') # 每个卖家对应这个产品的最新时间 ).values('latest_added') # 只取时间值,用于后续过滤 # 第二步:预取每个产品对应的所有最新价格记录(每个卖家仅一条) parts = Part.objects.prefetch_related( Prefetch( 'partprice', # 关联Part模型的related_name queryset=Price.objects.filter( added__in=Subquery(latest_added_per_seller) # 筛选出最新时间的价格 ).order_by('-added'), # 按时间从新到旧排序 to_attr='latest_prices' # 把结果存到自定义属性里,方便模板调用 ) ).all()
2. 修改模板代码
现在模板里可以直接遍历预取好的latest_prices,每个卖家只会出现一次:
{% for part in parts %} <h3>{{ part.name }}</h3> {% for price in part.latest_prices %} <a href="{{ part.seller1URL }}"> <p>${{ price.price }} - {{ price.seller.name }}</p> </a> {% empty %} <p>该产品暂无任何价格记录</p> {% endfor %} {% endfor %}
3. 进阶:显示所有卖家(无价格则留空)
如果你的需求是不管产品有没有某卖家的价格,都要显示该卖家(无价格时留空),可以按以下方式处理:
第一步:获取所有卖家列表
# 在视图里先拿到所有卖家 sellers = Seller.objects.all() # 把sellers传到模板上下文 context = {'parts': parts, 'sellers': sellers}
第二步:自定义模板过滤器
在你的app下创建templatetags/custom_filters.py(如果没有templatetags文件夹就新建一个,记得加__init__.py):
from django import template register = template.Library() @register.filter def get_latest_price(price_list, seller): """根据卖家从价格列表中找到对应的最新价格""" for price in price_list: if price.seller == seller: return price return None
第三步:修改模板
{% load custom_filters %} <!-- 先加载自定义过滤器 --> {% for part in parts %} <h3>{{ part.name }}</h3> {% for seller in sellers %} {% with price=part.latest_prices|get_latest_price:seller %} {% if price %} <a href="{{ part.seller1URL }}"> <p>${{ price.price }} - {{ seller.name }}</p> </a> {% else %} <p>{{ seller.name }}: 暂无价格</p> {% endif %} {% endwith %} {% endfor %} {% endfor %}
为什么这么做?
- 用
Subquery和Prefetch把查询次数降到最低,避免了模板里遍历所有价格记录的性能问题 - 确保每个卖家仅显示一次,且是最新的价格
- 进阶方案可以覆盖「显示所有卖家」的需求,无价格时友好提示
内容的提问来源于stack exchange,提问作者Android Mod
相关产品推荐
相关产品推荐

