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

Django中QuerySet无items属性报错,求实现指定输出格式

问题描述

我需要API输出如下格式的数据:

{  
    "E": [  
         {  
             "value": 50,  
             "label": "company1"  
         },  
         {  
             "value": 0,  
             "label": "company2"  
         }  
    ],  
    "P": [  
         {  
             "value": 50,  
             "label": "company1"  
         },  
         {  
             "value": 0,  
             "label": "company2"  
         }  
    ],  
    "C": [  
         {  
             "value": 50,  
             "label": "company1"  
         },  
         {  
              "value": 0,  
              "label": "company2"  
         }  
    ]  
}

尝试用serializers.DictField实现时,触发错误:AttributeError: 'QuerySet' object has no attribute 'items'。推测是模型方法返回的QuerySet不具备items属性,但这些方法在Jupyter单独运行正常。

以下是代码详情:

Model

class TblCorporation(models.Model):  
    contractcorporationid = models.AutoField(db_column='ContractCorporationID', primary_key=True)   
    contractid = models.ForeignKey(TblContract, related_name="Contract_Corporation",
                                   on_delete=models.CASCADE, db_column='ContractID')  
    companyid = models.ForeignKey(TblCompany, related_name="Company_Corporation",   
                                  on_delete=models.CASCADE, db_column='CompanyID')  
    e_percent = models.FloatField(db_column='E_Percent')  
    p_percent = models.FloatField(db_column='P_Percent')  
    c_percent = models.FloatField(db_column='C_Percent')  
    
    def __str__(self) -> str:  
        return '%s: e=%s, p=%s, c=%s' % (self.companyid.company, self.e_percent,   
                                         self.p_percent, self.c_percent)  
  
    def E(self):  
        e = TblCorporation.objects.filter(contractid__exact=self.contractid).values(  
            'companyid__company', 'e_percent').annotate( value = F('e_percent'),
                                                        label = F('companyid__company')).values(
                                                        'value', 'label')  
        return (e)  
    
    def P(self):  
        p = TblCorporation.objects.filter(contractid__exact=self.contractid).values(  
            'companyid__company', 'p_percent').annotate( value = F('p_percent'),
                                                        label = F('companyid__company')).values(
                                                        'value', 'label')  
        return (p)  
  
    def C(self):  
        c = TblCorporation.objects.filter(contractid__exact=self.contractid).values(  
            'companyid__company', 'c_percent').annotate( value = F('c_percent'),
                                                        label = F('companyid__company')).values(
                                                        'value', 'label')    
        return (c)  
  
    def company(self):  
        return self.companyid.company  
    
    class Meta:  
        db_table = 'tbl_Corporation'  
        verbose_name = 'Contract_Corporation'  
        verbose_name_plural = 'Contract_Corporations'

Serializer

class EPCDictField(serializers.DictField):  
    def to_internal_value(self, data):  
        if set(data.keys()) != {"value", "label"}:  
            raise serializers.ValidationError("Invalid keys for dictionary.")  
        return super().to_internal_value(data)  
    
    def to_representation(self, value):  
        return super().to_representation(value)  
        
class ContractCorporationSerializer(serializers.ModelSerializer):  
    E = EPCDictField()  
    P = EPCDictField()  
    C = EPCDictField()  
 
    class Meta:  
        model = TblCorporation  
        fields = ('E', 'P', 'C')

API

def get_ContractCorporation(request, id):  
    try:  
        contractCorporations = TblCorporation.objects.filter(contractid__exact=id)  
        serializer = ContractCorporationSerializer(contractCorporations, many=True)  
        return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)  
    except Exception as e:  
        return Response({"status": "error", "data": str(e)},
                         status=status.HTTP_500_INTERNAL_SERVER_ERROR)
错误原因
  1. 模型中的E()/P()/C()方法返回的是QuerySet对象,但DictField要求输入是字典类型,QuerySet是可迭代集合,没有字典的items()方法,因此触发报错。
  2. 视图中用many=True序列化整个QuerySet,会返回数组结构,但目标输出是单个对象包含E/P/C三个数组,结构不匹配。
解决方案

步骤1:优化模型方法(移除冗余逻辑)

模型中的E/P/C方法存在重复查询,每个实例都会执行相同的筛选操作,建议直接移除这些方法,在序列化器中统一处理数据:

class TblCorporation(models.Model):  
    contractcorporationid = models.AutoField(db_column='ContractCorporationID', primary_key=True)   
    contractid = models.ForeignKey(TblContract, related_name="Contract_Corporation",
                                   on_delete=models.CASCADE, db_column='ContractID')  
    companyid = models.ForeignKey(TblCompany, related_name="Company_Corporation",   
                                  on_delete=models.CASCADE, db_column='CompanyID')  
    e_percent = models.FloatField(db_column='E_Percent')  
    p_percent = models.FloatField(db_column='P_Percent')  
    c_percent = models.FloatField(db_column='C_Percent')  
    
    def __str__(self) -> str:  
        return '%s: e=%s, p=%s, c=%s' % (self.companyid.company, self.e_percent,   
                                         self.p_percent, self.c_percent)  
  
    def company(self):  
        return self.companyid.company  
    
    class Meta:  
        db_table = 'tbl_Corporation'  
        verbose_name = 'Contract_Corporation'  
        verbose_name_plural = 'Contract_Corporations'

步骤2:自定义序列化器适配目标格式

改用ListField配合子序列化器来处理数组结构,直接在序列化器中查询指定contract下的所有数据并构造输出:

from rest_framework import serializers
from .models import TblCorporation

class EPCItemSerializer(serializers.Serializer):
    value = serializers.FloatField()
    label = serializers.CharField()

class ContractCorporationSerializer(serializers.Serializer):
    E = serializers.ListField(child=EPCItemSerializer())
    P = serializers.ListField(child=EPCItemSerializer())
    C = serializers.ListField(child=EPCItemSerializer())

    def to_representation(self, contract_id):
        # 查询指定contract下的所有企业数据
        queryset = TblCorporation.objects.filter(contractid=contract_id)
        # 构造E/P/C对应的数组数据
        e_data = [{'value': item.e_percent, 'label': item.companyid.company} for item in queryset]
        p_data = [{'value': item.p_percent, 'label': item.companyid.company} for item in queryset]
        c_data = [{'value': item.c_percent, 'label': item.companyid.company} for item in queryset]
        
        return {
            'E': e_data,
            'P': p_data,
            'C': c_data
        }

步骤3:修改视图逻辑

视图不再序列化整个QuerySet,而是直接传入contract的id,让序列化器处理数据组装:

from rest_framework.response import Response
from rest_framework import status
from .serializers import ContractCorporationSerializer

def get_ContractCorporation(request, id):
    try:
        # 传入contract id给序列化器
        serializer = ContractCorporationSerializer(instance=id)
        return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({"status": "error", "data": str(e)},
                         status=status.HTTP_500_INTERNAL_SERVER_ERROR)

验证输出

修改后调用API,将返回目标格式的数据:

{
    "status": "success",
    "data": {
        "E": [
            {"value": 50, "label": "company1"},
            {"value": 0, "label": "company2"}
        ],
        "P": [
            {"value": 50, "label": "company1"},
            {"value": 0, "label": "company2"}
        ],
        "C": [
            {"value": 50, "label": "company1"},
            {"value": 0, "label": "company2"}
        ]
    }
}

内容的提问来源于stack exchange,提问作者Ali Sajadian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 10:22:36