python - sqlalchemy filter by json field
问题描述
I have model with json column
. Example of model and data:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://...'
db = SQLAlchemy()
db.init_app(app)
app.app_context().push()
class Example(db.Model):
id = db.Column(db.Integer(), nullable=False, primary_key=True, )
json_field = db.Column(db.JSON())
db.create_all()
db.session.add(Example(json_field={'id': None}))
db.session.add(Example(json_field={'id': 1}))
db.session.add(Example(json_field={'id': 50}))
db.session.add(Example(json_field={}))
db.session.commit()
Now I try to find records where id == 1
:
query = db.session.query(Example).filter(Example.json_field['id'] == 1)
print(query.all())
And I getting the next error:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) operator does not exist: json = integer LINE 3: WHERE (example.json_field -> 'id') = 1
The reason. Look at generated query:
SELECT example.id AS example_id, example.json_field AS example_json_field
FROM example
WHERE (example.json_field -> %(json_field_1)s) = %(param_1)s
But in my case correct query should be like this:
SELECT * FROM example WHERE CAST(json_field->>'id' AS INTEGER) = 1;
How can I do this?
I have tried use cast, but unsuccessfully:
print(
db.session.query(Example).filter(
cast(Example.json_field['id'], Integer) == 1
).all()
)
The error:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) cannot cast type json to integer LINE 3: WHERE CAST((example.json_field -> 'id') AS INTEGER) = 1
As you can see where clause
still wrong. Also I need to use range (>
, <=
etc.) conditions. Thanks for help.
解决方案
Flask-SQLAlchemy 的SQLAlchemy
对象 - 通常命名db
-提供对函数等的访问sqlalchemy
和sqlalchemy.orm
,不提供 Postgresql 特定运算符db.JSON
的泛型JSON
类型也是如此。您应该改为使用sqlalchemy.dialects.postgresql.JSON
:
from sqlalchemy.dialects.postgresql import JSON
class Example(db.Model):
id = db.Column(db.Integer(), nullable=False, primary_key=True, )
json_field = db.Column(JSON)
使用适当的类型,您必须先将JSON 显式转换为文本,然后再转换为整数:
db.session.query(Example).\
filter(Example.json_field['id'].astext.cast(Integer) == 1)
这会产生所需的谓词
CAST(json_field->>'id' AS INTEGER) = 1
这同样适用于所有不能直接从json
. SQLAlchemy 曾经为astext
and的组合提供快捷方式cast()
,但在 1.1 及更高版本中已将其删除:
在 1.1 版更改:对象
ColumnElement.cast()
上的运算符JSON
现在要求JSON.Comparator.astext
显式调用修饰符,如果强制转换仅适用于文本字符串。
推荐阅读
- spring - 如何使用 Spring Boot 安全性设置手动身份验证用户
- javascript - 提示框出现 10 秒后如何刷新?
- c# - Newtonsoft JsonConvert.DefaultSettings 奇怪的行为
- docker - 无法访问我主机上服务上的已发布端口
- assembly - 如何比较输入并检查数字是负数、正数还是零
- python - 如何将dataFrame csv/excel文件导出到特定目录
- c# - 如何仅在添加范围内将 T 函数、布尔函数的表达式添加到列表?
- encryption - CA证书通常是什么样的?
- python - 如何使用 Keras 在二进制图像上使用卷积神经网络?
- android - 按钮单击无法正常工作 - 如何解决?