Django序列化器:实现单店最新价格展示及商品最低价排序
解决方案
针对你的两个需求,我们可以通过子查询筛选最新价格和数据库注释实现按最低价排序来解决,无需修改数据库关联关系,具体步骤如下:
1. 修改序列化器,补充商店信息(贴合需求)
先在PriceSerializer中添加商店名称字段,让返回的价格数据明确对应所属商店:
# serializers.py from .models import Article, Price, Shop from rest_framework import serializers class PriceSerializer(serializers.ModelSerializer): shop_name = serializers.CharField(source='shop.name') # 新增商店名称字段 class Meta: model = Price fields = ['shop_name', 'name', 'date'] # 调整字段顺序,显示更清晰 class ArticleSerializer(serializers.ModelSerializer): prices = PriceSerializer(many=True) class Meta: model = Article fields = ['name', 'prices']
2. 调整视图集,筛选最新价格并实现排序
在ArticleViewSet中,通过子查询获取每个商品-商店组合的最新价格,同时计算商品的最低价格用于排序:
# views.py from .models import Shop, Article, Price from rest_framework import viewsets, permissions, filters from apitest.serializers import ArticleSerializer, PriceSerializer, ShopSerializer from django.db.models import Prefetch, Subquery, OuterRef, Max, Min, Func, F, Value, Q from django.db.models.fields import FloatField class ArticleViewSet(viewsets.ModelViewSet): serializer_class = ArticleSerializer permission_classes = [permissions.IsAuthenticated] filter_backends = [filters.SearchFilter, filters.OrderingFilter] search_fields = ['^name'] def get_queryset(self): # 子查询:获取每个(商品, 商店)组合的最新价格日期 latest_date_subquery = Price.objects.filter( article=OuterRef('article'), shop=OuterRef('shop') ).values('article', 'shop').annotate(max_date=Max('date')).values('max_date') # 筛选出所有符合最新日期的价格记录 latest_prices = Price.objects.filter(date=Subquery(latest_date_subquery)) # 预加载每个商品的最新价格,避免N+1查询 queryset = Article.objects.prefetch_related( Prefetch('prices', queryset=latest_prices) ) # 注释每个商品的最低价格:将字符串格式的价格(如"12,99")转为浮点数后取最小值 queryset = queryset.annotate( min_price=Min( Func( F('prices__name'), Value(','), Value('.'), function='replace', output_field=FloatField() ), filter=Q(prices__isnull=False) ) ) # 按最低价格升序排序,无价格的商品默认排在最后 return queryset.order_by('min_price', 'name') # 其余视图集代码保持不变 class PriceViewSet(viewsets.ModelViewSet): queryset = Price.objects.all() serializer_class = PriceSerializer permission_classes = [permissions.IsAuthenticated] class ShopViewSet(viewsets.ModelViewSet): queryset = Shop.objects.all() serializer_class = ShopSerializer permission_classes = [permissions.IsAuthenticated]
代码说明
子查询筛选最新价格:
- 通过
latest_date_subquery获取每个商品-商店组合的最大日期(即最新价格的日期) - 再用该日期过滤Price表,得到每个商品对应每个商店的唯一最新价格记录
- 用
Prefetch预加载这些记录,避免序列化时触发重复数据库查询
- 通过
按最低价排序:
- 调用数据库
replace函数,把价格字符串中的逗号替换为点,转成可计算的浮点数 - 用
Min函数计算每个商品所有最新价格中的最小值,作为排序依据 - 查询集按
min_price升序排序,无价格的商品因min_price为null自动排在末尾
- 调用数据库
最终效果
修改后返回的JSON格式如下:
[ { "name": "Flashlight", "prices": [ { "shop_name": "Shop A", "name": "12,99", "date": "2022-12-22" }, { "shop_name": "Shop B", "name": "14,99", "date": "2022-12-22" } ] }, { "name": "Cup", "prices": [] } ]
内容的提问来源于stack exchange,提问作者h lucas
相关产品推荐
相关产品推荐

