升级后,原始sql查询在postgres上以字符串形式返回json字段

2024-05-09 01:00:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在将Django应用程序从2.2.7升级到3.1.3。该应用程序使用Postgres 12&;psycopg2 2.8.6

我按照说明将所有django.contrib.postgres.fields.JSONField引用更改为django.db.models.JSONField,并进行迁移并运行迁移。这不会对我的模式产生任何更改(这很好)

但是,当我执行一个原始查询时,这些jsonb列的数据会在某个时候以文本形式返回,或者转换为文本。当直接使用Model.objects.get(...)查询模型时,我没有看到这个问题

import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "big_old_project.settings")
django.setup()

with connection.cursor() as c:
    c.execute("select name, data from tbl where name=%s", ("rex",))
    print(c.description)
    for row in c.fetchall():
        for col in row:
            print(f"{type(col)} => {col!r}")

(Column(name='name', type_code=1043), Column(name='data', type_code=3802))
<class 'str'> => 'rex'
<class 'str'> => '{"toy": "bone"}'

[编辑]使用原始连接可获得预期结果:

conn = psycopg2.connect("dbname=db user=x password=z")
with conn.cursor() as c:
    ...
<class 'str'> => 'rex'
<class 'dict'> => {'toy': 'bone'}

尝试“注册”适配器的老把戏是行不通的,无论如何也不需要

import psycopg2.extras
psycopg2.extras.register_json(oid=3802, array_oid=3807, globally=True)

这个应用程序有很多历史,所以可能有什么东西在踩psycopg2的脚?到目前为止,我找不到任何东西,而且我已经把所有与此相关的东西都注释掉了

浏览发行说明没有帮助。我确实使用了其他postgres字段,因此无法从模型中删除对contrib.postgres.fields的所有引用

任何关于为什么会发生这种情况的想法都将不胜感激


Tags: djangoname文本应用程序fieldsdbtypecol
3条回答

为了补充@Andrew Backer有用的答案,这显然是有意的。从3.1.1 release notes开始:

Fixed a QuerySet.order_by() crash on PostgreSQL when ordering and grouping by JSONField with a custom decoder (#31956). As a consequence, fetching a JSONField with raw SQL now returns a string instead of pre-loaded data. You will need to explicitly call json.loads() in such cases.

令人惊讶的是,在bugfix版本中发现了一个API不兼容的变更作为旁白。现在,我将添加json.loads()调用,因为正如前面提到的,不能保证::json解决方案也不会中断

谢谢你这篇有用的帖子!我对此错误的解决方案如下所示:

def call_ux_database(query, vars=None):
    conn = connections[settings.UX_DATABASE_NAME]
    conn.ensure_connection()
    with conn.connection.cursor() as cursor:
        psycopg2.extras.register_default_jsonb(conn_or_curs=cursor)
        cursor.execute(query, vars)
        row = cursor.fetchall()
    return row

好的,这似乎是他们出于某种原因在Django3.1.1中引入的一个更改,以修复其他一些bug。它从底层连接中注销jsonb转换器,这在IMO中很糟糕

Django问题:第一个“修复”破坏了这个基本功能,同时拒绝了原始sql的用例,第二个是宣布破坏无效

  1. QuerySet.order_by() chained with values() crashes on JSONField
  2. TypeError loading data in JSONField if DB has native JSON support

要解决这个问题,您可以创建自己的原始游标(django不会使用它),或者在原始sql中强制转换字段。至少,直到他们也打破它

SELECT 
    data::json,    db type is jsonb
    other_fields
FROM
    table

相关问题 更多 >