sqlalchemy按json字段过滤

时间:2018-11-12 14:16:37

标签: python sqlalchemy flask-sqlalchemy

我有json column的模特。模型和数据示例:

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()

现在我尝试在id == 1处查找记录:

query = db.session.query(Example).filter(Example.json_field['id'] == 1)
print(query.all())

我收到下一个错误:

  

sqlalchemy.exc.ProgrammingError:(psycopg2.ProgrammingError)运算符   不存在:json =整数第3行:WHERE(example.json_field->   'id')= 1

原因。查看生成的查询:

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

但是在我的情况下,正确的查询应该是这样的:

SELECT * FROM example WHERE CAST(json_field->>'id' AS INTEGER) = 1;

我该怎么做?

我尝试使用cast,但未成功:

print(
    db.session.query(Example).filter(
        cast(Example.json_field['id'], Integer) == 1
    ).all()
)

错误:

  

sqlalchemy.exc.ProgrammingError:(psycopg2.ProgrammingError)无法   将类型json转换为整数第3行:WHERE CAST(((example.json_field->   'id')AS INTEGER)= 1

您可以看到where clause仍然是错误的。另外,我需要使用范围(><=等)条件。感谢您的帮助。

3 个答案:

答案 0 :(得分:7)

Flask-SQLAlchemy的SQLAlchemy对象–通常命名为dbgives accesssqlalchemysqlalchemy.orm等的函数等,db.JSONgeneric JSON type,不提供特定于Postgresql的运算符。您应该改用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 to text,然后将其转换为整数:

db.session.query(Example).\
    filter(Example.json_field['id'].astext.cast(Integer) == 1)

这将产生所需的谓词

CAST(json_field->>'id' AS INTEGER) = 1

这同样适用于所有不能直接从json强制转换的类型。 SQLAlchemy曾经为astextcast()的组合提供快捷方式,但是在1.1版及更高版本中已将其删除:

  

版本1.1中的更改​​:ColumnElement.cast()对象上的JSON运算符现在要求显式调用JSON.Comparator.astext修饰符(如果强制转换仅从文本字符串起作用)。

答案 1 :(得分:4)

您还可以在过滤器中使用原始sql

from sqlalchemy import text

db.session.query(Example).filter(text("CAST(json_field->>'id' AS INTEGER) = 1")

答案 2 :(得分:0)

如果仅使用filter(json_obj["key"] ... ),它将转换为sql model_name.json_obj -> 'key'(仍然是json对象)

如果您使用filter(json_obj["key"].astext ...),则SQL将为 model_name.json_obj ->> 'key',结果是一个字符串对象。