J'ai un modèle avec json column
. Exemple de modèle et de données:
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()
Maintenant, j'essaie de trouver des enregistrements où id == 1
:
query = db.session.query(Example).filter(Example.json_field['id'] == 1)
print(query.all())
Et je reçois la prochaine erreur:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) n'existe pas: json = entier LIGNE 3: OERE (exemple.json_field -> 'id') = 1
La raison. Regardez la requête générée:
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
Mais dans mon cas, la requête correcte devrait être comme ceci:
SELECT * FROM example WHERE CAST(json_field->>'id' AS INTEGER) = 1;
Comment faire?
J'ai essayé d'utiliser cast , mais sans succès:
print(
db.session.query(Example).filter(
cast(Example.json_field['id'], Integer) == 1
).all()
)
L'erreur:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) ne peut pas convertir le type json en entier LIGNE 3: OERE CAST ((exemple.json_field -> 'id') COMME INTEGER) = 1
Comme vous pouvez le voir where clause
toujours incorrecte. Je dois également utiliser la plage (>
, <=
etc.) conditions. Merci pour l'aide.
Objet SQLAlchemy
de Flask-SQLAlchemy - communément appelé db
- donne accès aux fonctions etc. de sqlalchemy
et sqlalchemy.orm
, Etc. db.JSON
Est le type générique JSON
qui ne fournit pas les opérateurs spécifiques à Postgresql. Vous devriez plutôt utiliser 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)
Avec le type approprié en place, vous devez d'abord convertir explicitement le JSON en texte puis convertir en un entier:
db.session.query(Example).\
filter(Example.json_field['id'].astext.cast(Integer) == 1)
Cela produit le prédicat souhaité
CAST(json_field->>'id' AS INTEGER) = 1
La même chose s'applique à tous les types qui ne peuvent pas être directement convertis à partir de json
. SQLAlchemy offrait un raccourci pour la combinaison de astext
et cast()
, mais il a été supprimé dans les versions 1.1 et supérieures:
Modifié dans la version 1.1: L'opérateur
ColumnElement.cast()
sur les objetsJSON
nécessite désormais que le modificateurJSON.Comparator.astext
Soit appelé explicitement, si le transtypage fonctionne uniquement à partir d'une chaîne textuelle.
Vous pouvez également utiliser SQL brut dans le filtre
from sqlalchemy import text
db.session.query(Example).filter(text("CAST(json_field->>'id' AS INTEGER) = 1")
si vous utilisez uniquement filter(json_obj["key"] ... )
il se convertira en sql comme model_name.json_obj -> 'key'
, qui est toujours un objet json,
si vous utilisez filter(json_obj["key"].astext ...)
, le sql sera model_name.json_obj ->> 'key'
, le résultat est un objet chaîne.