Django 视图:以 JSON 格式返回查询集

Posted

技术标签:

【中文标题】Django 视图:以 JSON 格式返回查询集【英文标题】:Django View: Return Queryset in JSON Format 【发布时间】: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,所以该列表是包含可序列化字段的字典列表。 【参考方案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 保持相同的格式,那么您可以为所有模型使用相同的自定义管理器。

【讨论】:

以上是关于Django 视图:以 JSON 格式返回查询集的主要内容,如果未能解决你的问题,请参考以下文章

Django 以 JSON 格式返回单个记录

Django Rest Framework:默认以 JSON 格式输出到浏览器

如何在视图 Django 中更改渲染以返回 json [重复]

django 怎么将查询到的数据以json形式返回

python测试开发django-118.json 解析查询数据库 datetime 格式问题

Django:使用 ajax 查询(字符串/Json)时出现格式问题