sqlalchemy过滤器由json字段

Dan*_*har 4 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()
Run Code Online (Sandbox Code Playgroud)

现在我尝试找到以下记录id == 1:

query = db.session.query(Example).filter(Example.json_field['id'] == 1)
print(query.all())
Run Code Online (Sandbox Code Playgroud)

然后我收到了下一个错误:

sqlalchemy.exc.ProgrammingError:(psycopg2.ProgrammingError)运算符不存在:json = integer LINE 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
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM example WHERE CAST(json_field->>'id' AS INTEGER) = 1;
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

我尝试过使用强制转换,但没有成功:

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

错误:

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

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

Ilj*_*ilä 8

Flask-SQLAlchemy的SQLAlchemy对象 - 通常命名db- 允许sqlalchemy访问函数等sqlalchemy.orm,因此db.JSON是不提供Postgresql特定运算符的泛型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)
Run Code Online (Sandbox Code Playgroud)

使用适当的类型,您必须先将JSON显式转换为文本,然后转换为整数:

db.session.query(Example).\
    filter(Example.json_field['id'].astext.cast(Integer) == 1)
Run Code Online (Sandbox Code Playgroud)

这产生了所需的谓词

CAST(json_field->>'id' AS INTEGER) = 1
Run Code Online (Sandbox Code Playgroud)

这同样适用于无法直接投射的所有类型json.SQLAlchemy的用于提供的组合的快捷方式astextcast(),但它已在1.1及以上的版本被删除:

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

  • 首先感谢您的时间和回答。但是我更早尝试过这种方式。我得到了AttributeError:'BinaryExpression'对象和'Comparator'对象都没有属性'astext'。我的环境是:``python 3.6.1'',``flask-sqlalchemy 2.3.2''。 (4认同)

Rya*_*der 5

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

from sqlalchemy import text

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