【问题标题】:Insert key in a MySQL JSON field through sqlachemy mapped class without doing a query通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询
【发布时间】:2020-02-07 23:31:12
【问题描述】:

让我们从定义我的映射类开始

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import JSON
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy import Column, Integer

base = declarative_base()

class MinimalExample(Base):
    __tablename__ = 'users'
    id = Column(Integer)
    data = Column(MutableDict.as_mutable(JSON))

假设我的表中有一行 id=1data={'key_0': 1}

我可以很容易地通过session 对象请求它

first_row = session.query(MinimalExample)\
                   .filter(MinimalExample.id==1)\
                   .first()

我可以从那里以任何我想要的方式修改 JSON 字段并更新数据库:

first_row.data['key_2'] = 1
session.commit()

上面的代码有效(因为我们将 JSON 字段标记为可变字典,我们不必手动将其标记为脏)

session.query(MinimalExample.data)\
                   .filter(MinimalExample.id==1)\
                   .first()

>>> {'key_0': 1, 'key_2': 1}

但这意味着我必须在本地计算机上查询我想要修改的所有行,这太慢了/我没有足够的内存。

我正在寻找一种方法来使用sqlalchemy.update 函数来获得相同的结果

这是我已经尝试过的:

from sqlalchemy import update

stmt = update(MinimalExample)
           .where(MinimalExample.id=1)\
           .values(MinimalExample.data={'key_1': 1})

session.execute(stmt)
session.commit()

这只是用字典 {'key_1': 1} 替换我行中的 data 字段

类似的事情似乎是要走的路:

(编辑:找到了一种方法来更正我以前的代码,因此它不再引发错误,只需将对象放入字典中)

stmt = update(MinimalExample)
           .where(MinimalExample.id=1)\
           .values({MinimalExample.data['key_1']:1})

看这个语句的字符串好像还不错

str(stmt)

>>> 'UPDATE users SET data[:data_1]=:param_1 WHERE users.id = :id_1'

但是执行此语句会导致 ProgrammingError: (MySQLdb._exceptions.ProgrammingError) (1064, 'You have an error in your SQL syntax...

对此有什么想法吗?如果我可以同时更新多行,则可以加分

【问题讨论】:

    标签: python mysql sqlalchemy


    【解决方案1】:

    要更新 MySQL 中的 JSON 列,您需要使用 JSON_SET() 函数(有关详细信息,请参阅 here),例如,工作查询如下所示:

    UPDATE users SET data = JSON_SET(data, "$.key_1", 1) WHERE id = 1;
    

    在 SQLAlchemy 中进行模拟:

    qry = (
        update(MinimalExample)
        .where(MinimalExample.id == 1)
        .values(
            {"data": func.JSON_SET(MinimalExample.data, "$.key_1", 1)}
        )
    )
    s.execute(qry)
    s.commit()
    

    这是从创建表到提交的日志,您可以看到没有发出 SELECT:

    2019-10-11 09:37:01,672 INFO sqlalchemy.engine.base.Engine
    CREATE TABLE users (
            id INTEGER NOT NULL AUTO_INCREMENT,
            data JSON,
            PRIMARY KEY (id)
    )
    
    
    2019-10-11 09:37:01,684 INFO sqlalchemy.engine.base.Engine {}
    2019-10-11 09:37:01,708 INFO sqlalchemy.engine.base.Engine COMMIT
    2019-10-11 09:37:01,716 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
    2019-10-11 09:37:01,718 INFO sqlalchemy.engine.base.Engine INSERT INTO users (id, data) VALUES (%(id)s, %(data)s)
    2019-10-11 09:37:01,718 INFO sqlalchemy.engine.base.Engine {'id': 1, 'data': '{"key_0": 1}'}
    2019-10-11 09:37:01,720 INFO sqlalchemy.engine.base.Engine COMMIT
    2019-10-11 09:37:01,723 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
    2019-10-11 09:37:01,724 INFO sqlalchemy.engine.base.Engine UPDATE users SET data = JSON_SET(data, "$.key_1", 1) WHERE id = 1;
    2019-10-11 09:37:01,724 INFO sqlalchemy.engine.base.Engine {}
    2019-10-11 09:37:01,725 INFO sqlalchemy.engine.base.Engine COMMIT
    

    【讨论】:

    • 对我来说很好用,谢谢!目前,我正在遍历所有要更新的行以执行此查询,但这很慢。您是否知道如何使用此查询一次更新多行(使用不同的值)?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-30
    • 2020-01-30
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 2013-06-20
    相关资源
    最近更新 更多