通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询
Posted
技术标签:
【中文标题】通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询【英文标题】:Insert key in a MySQL JSON field through sqlachemy mapped class without doing a query 【发布时间】: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=1
和 data='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
...
对此有什么想法吗?如果我可以同时更新多行,则可以加分
【问题讨论】:
【参考方案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
【讨论】:
对我来说很好用,谢谢!目前,我正在遍历所有要更新的行以执行此查询,但这很慢。您是否知道如何使用此查询一次更新多行(使用不同的值)?以上是关于通过 sqlalchemy 映射类在 MySQL JSON 字段中插入键而不进行查询的主要内容,如果未能解决你的问题,请参考以下文章
MySQL表结构映射为Python中的对象 python (SQLAlchemy)