sqlalchemy фильтр по полю json - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть модель с 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 = целое число 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

Но в моем случае правильный запрос должен выглядеть следующим образом:

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 все еще неверно.Также мне нужно использовать диапазон (>, <= и т. Д.) Условия.Спасибо за помощь.

Ответы [ 2 ]

0 голосов
/ 19 июня 2019

, если вы используете только filter(json_obj["key"] ... ), он преобразуется в sql как model_name.json_obj -> 'key', который по-прежнему является объектом json,

, если вы используете filter(json_obj["key"].astext ...), sql будет model_name.json_obj ->> 'key', результатявляется строковым объектом.

0 голосов
/ 12 ноября 2018

Объект Flask-SQLAlchemy SQLAlchemy - обычно именуемый db - , предоставляет доступ к функциям и т. Д. Из sqlalchemy и sqlalchemy.orm, поэтому db.JSON является универсальным 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 в текст , а затем привести к целому числу:

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

Это дает желаемый предикат

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

То же самое относится ко всем типам, которые не могут быть напрямую отлиты из json. SQLAlchemy раньше предлагал ярлык для комбинации astext и cast(), но он был удален в версии 1.1 и выше:

Изменено в версии 1.1: оператор ColumnElement.cast() для объектов JSON теперь требует явного вызова модификатора JSON.Comparator.astext, если приведение выполняется только из текстовой строки.

...