J'utilise SQLAlchmey comme ORM pour un projet Python. J'ai créé peu de modèles/schémas et cela fonctionne bien. Maintenant, j'ai besoin d'interroger une base de données mysql existante, pas d'insertion/mise à jour juste l'instruction select.
Comment créer un wrapper autour des tables de cette base de données existante? J'ai brièvement passé en revue les documents sqlalchemy et SO, mais je n'ai rien trouvé de pertinent. Toutes suggèrent une méthode d'exécution, où j'ai besoin d'écrire les requêtes sql brutes, alors que je veux utiliser la méthode de requête SQLAlchmey de la même manière que j'utilise avec les modèles SA.
Par exemple, si la base de données existante a le nom de table User, je souhaite l'interroger à l'aide de la commande dbsession (uniquement l'opération select, probablement avec join).
Vous semblez avoir l'impression que SQLAlchemy ne peut fonctionner qu'avec une structure de base de données créée par SQLAlchemy (utilisant probablement MetaData.create_all()
) - cela n'est pas correct. SQLAlchemy peut parfaitement fonctionner avec une base de données préexistante, il vous suffit de définir vos modèles pour faire correspondre les tables de base de données. Une façon de le faire est d'utiliser la réflexion, comme le suggère Ilja Everilä:
class MyClass(Base):
__table__ = Table('mytable', Base.metadata,
autoload=True, autoload_with=some_engine)
(ce qui, à mon avis, conviendrait parfaitement pour des scripts uniques, mais peut conduire à des bogues incroyablement frustrants dans une application "réelle" si la structure de la base de données risque de changer avec le temps)
Une autre solution consiste simplement à définir vos modèles comme d'habitude en prenant soin de les définir pour correspondre aux tables de la base de données, ce qui n'est pas si difficile. L'avantage de cette approche est que vous ne pouvez mapper qu'un sous-ensemble de tables de base de données sur vos modèles et même qu'un sous-ensemble de colonnes de table sur les champs de votre modèle. Supposons que vous ayez 10 tables dans la base de données mais que vous êtes uniquement intéressé par la table users
à partir de laquelle vous avez uniquement besoin des champs id
, name
et email
:
class User(Base):
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
email = sa.Column(sa.String)
(Notez que nous n'avons pas eu besoin de définir certains détails qui sont uniquement nécessaires pour émettre le DDL correct, tels que la longueur des champs String ou le fait que le champ email
a un index)
SQLAlchemy n'émettra pas de requêtes INSERT/UPDATE sauf si vous créez ou modifiez des modèles dans votre code. Si vous voulez vous assurer que vos requêtes sont en lecture seule, vous pouvez créer un utilisateur spécial dans la base de données et ne lui accorder que les privilèges SELECT. Alternativement, vous pouvez également essayer d’annuler la transaction dans votre code d’application.
Avec un exemple de code:
from sqlalchemy.sql import select
from sqlalchemy import create_engine, MetaData, Table
CONN_STR = '…'
engine = create_engine(CONN_STR, echo=True)
metadata = MetaData()
cookies = Table('cookies', metadata, autoload=True,
autoload_with=engine)
cols = cookies.c
with engine.connect() as conn:
query = (
select([cols.created_at, cols.name])
.order_by(cols.created_at)
.limit(1)
)
for row in conn.execute(query):
print(row)
Vous pouvez accéder à une table existante à l'aide du code suivant:
from sqlalchemy.orm import Session
Base = automap_base()
Base.prepare(engine, reflect=True)
Users = Base.classes.users
session = Session(engine)
res = session.query(Users).first()