sqlalchemy filter by json field
if you only use filter(json_obj["key"] ... )
it will convert to sql like
model_name.json_obj -> 'key'
, which is still a json object,
if you use filter(json_obj["key"].astext ...)
, the sql will be
model_name.json_obj ->> 'key'
, the result is a string object.
Flask-SQLAlchemy's SQLAlchemy
object – commonly named db
– gives access to functions etc. from sqlalchemy
and sqlalchemy.orm
, and so db.JSON
is the generic JSON
type that does not provide the Postgresql specific operators. You should instead use 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)
With the proper type in place you must explicitly convert the JSON to text first and then cast to an integer:
db.session.query(Example).\
filter(Example.json_field['id'].astext.cast(Integer) == 1)
This produces the desired predicate
CAST(json_field->>'id' AS INTEGER) = 1
The same applies to all types that cannot be directly cast from json
. SQLAlchemy used to offer a shortcut for the combination of astext
and cast()
, but it has been removed in version 1.1 and above:
Changed in version 1.1: The
ColumnElement.cast()
operator onJSON
objects now requires that theJSON.Comparator.astext
modifier be called explicitly, if the cast works only from a textual string.
You can also use raw sql in filter
from sqlalchemy import text
db.session.query(Example).filter(text("CAST(json_field->>'id' AS INTEGER) = 1")