【问题标题】:Why doesn't JsonResponse automatically serialize my Django model?为什么 JsonResponse 不会自动序列化我的 Django 模型?
【发布时间】:2019-09-24 11:48:50
【问题描述】:

我有这些模型:

class BaseModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class User(AbstractUser):
    username = models.CharField(max_length=255, unique=True)
    email = models.EmailField(unique=True, null=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


class Post(BaseModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content_url = models.URLField(null=False)

我希望能够获取数据库中的所有模型。当我这样做时:

urlpatterns = [
    path("", views.get_all_posts, name="get_all_posts")]

def get_all_posts(request):
    return JsonResponse({"posts": Post.objects.all()})

我收到关于 JSON 序列化的错误。所以,根据其他帖子的建议,我这样做:

def get_all_posts(request):
    posts = serializers.serialize('json', Post.objects.all())
    return JsonResponse({"posts": json.loads(posts)})

然后我得到一个这种形状的物体:

('[{"model": "api.post", "pk": 1, "fields": {"created_at": '
 '"2019-05-06T20:22:43.928Z", "updated_at": "2019-05-06T20:22:43.928Z", '
 '"user": 1, "content_url": "tmp/posts/None.md"}}]')

你知道这有多可笑吗?要获取帖子的实际内容,我需要将每个“响应”映射到其fields 属性,即使这样,我仍然缺少主键!!

我想知道为什么 JSON 响应接口没有这么简单:

def get_all_posts(request):
    return JsonResponse({"posts": Post.objects.all()})

似乎要向客户端提供序列化的 JSON,我必须跳过一系列环节,老实说,这些环节不应该存在于生产 Web 框架中。

我做错了什么?

【问题讨论】:

  • 您可以创建自己的serializer 来指定如何序列化模型。事实上,您准确地回答了您的问题:因为有很多方法可以序列化模型,例如,您可能不想包含用户模型的散列密码。事实上,如果你做一个 REST api,你最好使用ViewSets,因此不要自己构造各种视图。
  • 我的意思是,序列化的一个子集是如此普遍,以至于它们应该开箱即用
  • 有些模型不能直接序列化为 JSON。如何处理多对多关系?外键?日期和日期时间字段?使用哪种格式? Django Rest Framework 为您提供了许多情况下的序列化器,使工作更容易,但查看代码以了解序列化对象并不明显。您想要 JSON 的方式与其他人想要的 JSON 不同。
  • 我没有使用 DRF,也不打算使用。我希望只用 Django 就可以做到这一点。诚然,有很多方法可以进行序列化,但应该提供一种开箱即用的通用方法,并在需要时提供扩展选项。
  • @dopatraman:如果你在模型中添加额外的属性会怎样?例如,如果那是一个 HTTP 会话(不是真正“可序列化”的东西)怎么办。 Linux 的禅意是:“让每个工具都做一项工作,并且把这项工作做得很好”。通常最好用可以有效重用的非常小的元素来设计一个系统。

标签: json django python-3.x


【解决方案1】:

使用querysetvalues()/values_list()方法进行序列化:

def get_all_posts(request):
    return JsonResponse({"posts": Post.objects.values_list()})

它还支持spanned fields,所以不会有嵌套序列化,容易出错:

def get_all_posts(request):
    return JsonResponse({
        "posts": Post.objects.values('content_url', 'user__username')
    })

但对于一些复杂的 fleid 值,它也可能会失败。在这种情况下,在理解循环中将复杂类型转换为简单的 Python 类型,或者使用第三方序列化程序,如 DRF 或tastepie。

【讨论】:

    【解决方案2】:

    serialize() 函数中使用 format='python' 作为:

    def get_all_posts(request):
        posts = serializers.serialize('python', Post.objects.all())
        return JsonResponse({"posts": posts})

    【讨论】:

    • 这是一个更好的步骤,但它仍然将主键与模型实例的字段分开:('[{"model": "api.post", "pk": 1, "fields": {"created_at": ' '"2019-05-06T20:22:43.928Z", "updated_at": "2019-05-06T20:22:43.928Z", ' '"user": 1, "content_url": "tmp/posts/None.md"}}]')
    • Django 序列化程序有它自己的限制。我想你可以使用values() 方法
    • 最近几天我有机会回答你的问题。我觉得你的观点有问题。 ☹️我说这不是为了冒犯你,如果我这样做了,我真的很抱歉......我的感觉是,你正在查看一些内置方法并说 “它不起作用我想要的方式” ,并且您对维护者更改内置方法原始源代码非常感兴趣,而不是调整结果。 @dopatraman
    • 没有冒犯。你是对的......我宁愿让我的工具按照我想要的方式工作,而不是调整我认为是直观的行为。我看不出应该有什么办法才能将 JSON 传递给客户端。这类似于给某人一把坏了的锤子,并告诉他们与其修理锤子,不如“以不同的方式使用它”。
    • 不幸的是,有时您应该说出来。因为,我们现在别无选择。目前没有开箱即用的方法来满足您的要求....那么下一步呢???要么你应该提交一个 PR 给 Django 核心维护者,然后等待接受和下一次发布,或者根据你的需要调整结果并继续
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 2023-03-29
    • 1970-01-01
    • 2014-10-05
    • 2013-02-14
    • 2017-04-21
    • 1970-01-01
    相关资源
    最近更新 更多