You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:31:10