Django如何对已建立索引的varchar字段执行分组查询?
问题描述
现有如下Django的Product模型:
class Product(models.Model): manufacturer = models.CharField(max_length=32, null=True, blank=True) product_model = models.CharField(max_length=32, null=True, blank=True) description = models.CharField(max_length=32, null=True, blank=True)
其中manufacturer字段已建立索引(非主键),需要实现类似Product.objects.all().group_by(manufacturer)的分组查询,最终在Django Rest Framework中返回以manufacturer为标识,对应其下所有Product对象数组的结构。
方法一:Python层面分组(简单直接)
直接拉取所有产品数据,用Python内置的itertools.groupby做分组,注意必须先按manufacturer排序——因为groupby只会对连续相同的元素分组:
from itertools import groupby from rest_framework.response import Response from .models import Product def get_grouped_products(request): # 先按厂商字段排序,保证同厂商产品连续 products = Product.objects.order_by('manufacturer').all() grouped_data = {} # 分组时处理null值,统一命名为「未指定」(可按需调整) for manu_key, product_group in groupby(products, key=lambda p: p.manufacturer or '未指定'): # 把每组产品序列化为需要的字段结构 grouped_data[manu_key] = [ {'product_model': p.product_model, 'description': p.description} for p in product_group ] return Response(grouped_data)
这种方法代码量少,逻辑清晰,适合数据量不算特别大的场景。
方法二:DRF结构化返回(REST风格)
如果需要返回标准的数组式分组结构(每个元素包含厂商和对应产品列表),可以用嵌套序列化器实现:
from rest_framework import serializers, viewsets from rest_framework.response import Response from .models import Product # 定义产品基础序列化器 class ProductSerializer(serializers.ModelSerializer): class Meta: model = Product fields = ['product_model', 'description'] # 定义厂商-产品分组序列化器 class ManufacturerGroupSerializer(serializers.Serializer): manufacturer = serializers.CharField() products = ProductSerializer(many=True) class GroupedProductView(viewsets.ViewSet): def list(self, request): # 获取所有非空的厂商(去重) valid_manufacturers = Product.objects.values_list( 'manufacturer', flat=True ).distinct().exclude(manufacturer__isnull=True) grouped_list = [] # 逐个拉取对应厂商的产品并序列化 for manu in valid_manufacturers: products = Product.objects.filter(manufacturer=manu) grouped_list.append({ 'manufacturer': manu, 'products': ProductSerializer(products, many=True).data }) # 处理厂商为null的产品 null_products = Product.objects.filter(manufacturer__isnull=True) if null_products.exists(): grouped_list.append({ 'manufacturer': '未指定', 'products': ProductSerializer(null_products, many=True).data }) return Response(grouped_list)
返回的结构是数组,每个元素包含manufacturer和products字段,更符合REST API的设计习惯。
注意:Django ORM的GROUP_BY特性
Django ORM的values()+annotate()组合对应SQL的GROUP BY,但它主要用于聚合统计(比如统计每个厂商的产品数量),如果要获取完整的产品对象列表,这种方式并不适用,示例如下(仅作统计参考):
from django.db.models import Count # 统计每个厂商的产品数量 manufacturer_stats = Product.objects.values('manufacturer').annotate(product_count=Count('id')).order_by('manufacturer')
内容的提问来源于stack exchange,提问作者Olivier Pons
相关产品推荐
相关产品推荐

