【问题标题】:Django View: Return Queryset in JSON FormatDjango 视图:以 JSON 格式返回查询集
【发布时间】:2020-01-21 17:55:27
【问题描述】:

我正在尝试使用return JsonResponse() 使以下视图最终正常工作:

def get_data(request):
    full_data = Fund.objects.all()
    data = {
    "test2": full_data.values('investment_strategy').annotate(sum=Sum('commitment')),
    }
    return JsonResponse(data)

但是,我收到一条错误消息,提示“QuerySet 类型的对象不是 JSON 可序列化的”。 当我把上面的 Queryset 放在一个最后带有 return render() 的视图中时:

def get_more_data(request):
    full_data = Fund.objects.all()
    data = {"test2": full_data.values('investment_strategy').annotate(sum=Sum('commitment'))}
    return render (request, 'test.html', data)

我得到以下结果:<QuerySet [{'investment_strategy': 'Buyout', 'sum': 29}, {'investment_strategy': 'Growth', 'sum': 13}, {'investment_strategy': 'Miscellaneous', 'sum': 14}, {'investment_strategy': 'Venture Capital', 'sum': 23}, {'investment_strategy': 'n/a', 'sum': 36}]>

所以查询集工作正常,我只是不知道如何以正确的 Json 格式返回数据(我需要使用数据 charts.js)

我查看了类似问题的答案,例如: TypeError: object is not JSON serializable in DJango 1.8 Python 3.4 Output Django queryset as JSON 等等。 但无法为我的问题找到有意义的解决方案。

任何帮助将不胜感激!

【问题讨论】:

  • JsonResponse(list(data)) 将评估 Queryset(实际执行对数据库的查询)并将其转换为可以传递给 JsonResponse 的列表。这是因为您使用了valuesannotate,所以该列表是包含可序列化字段的字典列表。

标签: json django django-views django-queryset


【解决方案1】:

所以我设法找到了一个对我有用的解决方案 - 以防其他人遇到同样的问题。我改变了看法:

def get_data(request):
    full_data = Fund.objects.all()
    full_data_filtered = full_data.values('investment_strategy').annotate(sum=Sum('commitment'))

labels = []
values = []

for d in full_data_filtered:
    labels.append(d['investment_strategy'])
    values.append(d['sum'])

data = {
    "labels": labels,    
    "values": values,
    }

return JsonResponse(data)

所以基本上我遍历查询集并将我需要的值分配给列表,这些值可以传递给 JsonResponse。我不知道这是否是最优雅的方式(当然不是),但它有效,我可以在charts.js 中呈现我的数据

【讨论】:

    【解决方案2】:

    JsonResponse(list(data)) 将评估 Queryset(实际上执行对数据库的查询)并将其转换为可以传递给 JsonResponse 的列表。

    这是有效的,因为您使用了valuesannotate,因此该列表是包含可序列化字段的字典列表。

    在您提到的示例中,它不起作用,因为 Queryset 只是返回一个模型实例列表,因此包装 list() 是不够的。如果您没有添加 values,您将拥有一个不可序列化的 Fund 实例列表。

    【讨论】:

      【解决方案3】:

      我发现最好的方法是创建一个自定义的 QuerySet 和 Manager,它的代码不多而且可以重复使用!

      我首先创建了custom QuerySet

      # managers.py but you can do that in the models.py too
      from django.db import models
      
      class DictQuerySet(models.QuerySet):
      
          def dict(self):
              values = self.values()
              result = {}
              for value in values:
                  id = value['id']
                  result[id] = value # you can customize the content of your dictionary here
              return result
      

      然后我创建了一个custom Manager,它是optional,但我更喜欢这种方式。

      # managers.py, also optional
      class DictManager(models.Manager):
      
          def get_queryset(self):
              return DictQuerySet(self.model, using=self._db)
      

      更改模型中的默认管理器:

      # models.py
      from .managers import DictManager
      
      class Fund(models.Model):
          # ...
          objects =  DictManager()
          # ...
      

      现在您可以从查询中调用 dict() 方法

      # views.py 
      
      def get_data(request):
          full_data = Fund.objects.all().dict()
          return JsonResponse(full_data)
      

      响应将是 full_data 作为字典的字典,每个键都是相应对象的主键。

      如果您打算为 JSON 保持相同的格式,那么您可以为所有模型使用相同的自定义管理器。

      【讨论】:

        猜你喜欢
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2014-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多