SQLAlchemy: moteur, connexion et différence de session

134

J'utiliser SQLAlchemy et il y a au moins trois entités: engine, sessionet connectionqui ont la executeméthode, donc si je veux par exemple sélectionner tous les enregistrements de tableje peux le faire

engine.execute(select([table])).fetchall()

et ça

connection.execute(select([table])).fetchall()

et même ça

session.execute(select([table])).fetchall()

- les résultats seront les mêmes.

Si je comprends bien, si quelqu'un l'utilise engine.executecrée connection, ouvre session(Alchemy s'en charge pour vous) et exécute la requête. Mais y a-t-il une différence globale entre ces trois façons d'accomplir une telle tâche?

ololobus
la source
Je pense que votre réponse est ici: hackersandslackers.com
...

Réponses:

123

Un aperçu en une ligne:

Le comportement de execute()est la même dans tous les cas, mais ils sont 3 méthodes différentes, dans Engine, Connectionet les Sessionclasses.

Qu'est-ce que c'est exactement execute():

Pour comprendre le comportement de execute()nous devons nous pencher sur la Executableclasse. Executableest une superclasse pour tous les types d'objets «instruction», y compris select (), delete (), update (), insert (), text () - en termes simples, une Executableest une construction d'expression SQL prise en charge dans SQLAlchemy.

Dans tous les cas, la execute()méthode prend le texte SQL ou l'expression SQL construite, c'est-à-dire l'une des différentes constructions d'expression SQL prises en charge dans SQLAlchemy et renvoie les résultats de la requête (a ResultProxy- Enveloppe un DB-APIobjet curseur pour faciliter l'accès aux colonnes de ligne.)


Pour le clarifier davantage (uniquement à des fins de clarification conceptuelle, pas une approche recommandée) :

En plus de Engine.execute()(exécution sans connexion), Connection.execute()et Session.execute(), il est également possible d'utiliser execute()directement sur n'importe quelle Executableconstruction. La Executableclasse a sa propre implémentation de execute()- Selon la documentation officielle, une ligne de description de ce que execute()fait est " Compiler et exécuter ceciExecutable ". Dans ce cas, nous devons lier explicitement la Executable(construction d'expression SQL) avec un Connectionobjet ou un Engineobjet (qui obtient implicitement un Connectionobjet), de sorte que le execute()saura où exécuter le SQL.

L'exemple suivant le montre bien - Compte tenu d'un tableau comme ci-dessous:

from sqlalchemy import MetaData, Table, Column, Integer

meta = MetaData()
users_table = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)))

Exécution explicite, c'est-à Connection.execute()- dire en passant le texte SQL ou l'expression SQL construite à la execute()méthode de Connection:

engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
    # ....
connection.close()

Exécution explicite sans connexion, c'est-à Engine.execute()- dire en passant le texte SQL ou l'expression SQL construite directement à la execute()méthode du moteur:

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
    # ....
result.close()

L'exécution implicite ie Executable.execute()- est également sans connexion, et appelle la execute()méthode du Executable, c'est-à-dire qu'elle appelle la execute()méthode directement sur la SQLconstruction d'expression (une instance de Executable) elle-même.

engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

Remarque: a indiqué l'exemple d'exécution implicite à des fins de clarification - cette méthode d'exécution n'est pas recommandée - comme indiqué dans la documentation :

«L'exécution implicite» est un modèle d'utilisation très ancien qui, dans la plupart des cas, est plus déroutant qu'utile, et son utilisation est déconseillée. Les deux modèles semblent encourager la surutilisation de «raccourcis» opportuns dans la conception des applications, ce qui entraîne des problèmes plus tard.


Vos questions:

Si je comprends bien, si quelqu'un utilise engine.execute, il crée une connexion, ouvre une session (Alchemy s'en soucie pour vous) et exécute la requête.

Vous avez raison pour la partie "si quelqu'un l'utilise engine.executecrée connection" mais pas pour "ouvre session(Alchemy s'en soucie pour vous) et exécute la requête" - En utilisant Engine.execute()et Connection.execute()est (presque) la même chose, en formel, l' Connectionobjet est créé implicitement , et dans un cas ultérieur, nous l'instancions explicitement. Ce qui se passe réellement dans ce cas est:

`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`

Mais y a-t-il une différence globale entre ces trois façons d'accomplir une telle tâche?

Au niveau de la couche DB, c'est exactement la même chose, tous exécutent du SQL (expression de texte ou diverses constructions d'expression SQL). Du point de vue de l'application, il existe deux options:

  • Exécution directe - Utilisation Engine.execute()ouConnection.execute()
  • L' utilisation sessions- gère efficacement transaction comme simple unité de travail, avec facilité via session.add(), session.rollback(), session.commit(), session.close(). C'est le moyen d'interagir avec la base de données en cas d'ORM, c'est-à-dire de tables mappées. Fournit identity_map pour obtenir instantanément des objets déjà accédés ou nouvellement créés / ajoutés au cours d'une seule demande.

Session.execute()utilise finalement la Connection.execute()méthode d'exécution de l'instruction afin d'exécuter l'instruction SQL. L'utilisation d' Sessionobjet est le moyen recommandé par SQLAlchemy ORM pour une application d'interagir avec la base de données.

Un extrait de la documentation :

Il est important de noter que lors de l'utilisation de l'ORM SQLAlchemy, ces objets ne sont généralement pas accessibles; à la place, l'objet Session est utilisé comme interface avec la base de données. Cependant, pour les applications construites autour de l'utilisation directe d'instructions SQL textuelles et / ou de constructions d'expression SQL sans implication des services de gestion de niveau supérieur de l'ORM, le moteur et la connexion sont roi (et reine?) - continuez à lire.

Nabeel Ahmed
la source
Le mot "sans connexion" implique qu'aucune connexion n'est créée, ce qui, selon la réponse de Neal, n'est pas le cas.
Atom
111

La réponse de Nabeel couvre beaucoup de détails et est utile, mais j'ai trouvé cela déroutant à suivre. Comme il s'agit actuellement du premier résultat Google pour ce problème, j'ajoute ma compréhension de celui-ci pour les futures personnes qui trouveront cette question:

Exécuter .execute ()

Comme le notent OP et Nabell Ahmed, lors de l'exécution d'un plaine SELECT * FROM tablename, il n'y a aucune différence dans le résultat fourni.

Les différences entre ces trois objets ne deviennent importants en fonction du contexte que la SELECTdéclaration est utilisée ou, plus souvent, lorsque vous voulez faire d' autres choses comme INSERT, DELETE, etc.

Quand utiliser Engine, Connection, Session en général

  • Engine est l'objet de niveau le plus bas utilisé par SQLAlchemy. Il maintient un pool de connexions disponibles chaque fois que l'application a besoin de parler à la base de données. .execute()est une méthode pratique qui appelle d'abord conn = engine.connect(close_with_result=True)et ensuite conn.execute(). Le paramètre close_with_result signifie que la connexion est fermée automatiquement. (Je paraphrase légèrement le code source, mais essentiellement vrai). edit: voici le code source de engine.execute

    Vous pouvez utiliser le moteur pour exécuter du SQL brut.

    result = engine.execute('SELECT * FROM tablename;')
    #what engine.execute() is doing under the hood
    conn = engine.connect(close_with_result=True)
    result = conn.execute('SELECT * FROM tablename;')
    
    #after you iterate over the results, the result and connection get closed
    for row in result:
        print(result['columnname']
    
    #or you can explicitly close the result, which also closes the connection
    result.close()

    Ceci est couvert dans la documentation sous l'utilisation de base .

  • La connexion est (comme nous l'avons vu ci-dessus) la chose qui effectue réellement le travail d'exécution d'une requête SQL. Vous devriez le faire chaque fois que vous voulez un meilleur contrôle sur les attributs de la connexion, quand elle est fermée, etc. Par exemple, un exemple très important de ceci est une Transaction , qui vous permet de décider quand valider vos modifications dans la base de données. En utilisation normale, les modifications sont automatiquement validées. Avec l'utilisation de transactions, vous pouvez (par exemple) exécuter plusieurs instructions SQL différentes et si quelque chose ne va pas avec l'une d'entre elles, vous pouvez annuler toutes les modifications en même temps.

    connection = engine.connect()
    trans = connection.begin()
    try:
        connection.execute("INSERT INTO films VALUES ('Comedy', '82 minutes');")
        connection.execute("INSERT INTO datalog VALUES ('added a comedy');")
        trans.commit()
    except:
        trans.rollback()
        raise

    Cela vous permettrait d'annuler les deux modifications en cas d'échec, comme si vous aviez oublié de créer la table d'enregistrement des données.

    Donc, si vous exécutez du code SQL brut et que vous avez besoin de contrôle, utilisez des connexions

  • Les sessions sont utilisées pour l'aspect Object Relationship Management (ORM) de SQLAlchemy (en fait, vous pouvez le voir à partir de la façon dont elles sont importées :) from sqlalchemy.orm import sessionmaker. Ils utilisent des connexions et des transactions sous le capot pour exécuter leurs instructions SQL générées automatiquement. .execute()est une fonction pratique qui passe par tout ce à quoi la session est liée (généralement un moteur, mais peut être une connexion).

    Si vous utilisez la fonctionnalité ORM, utilisez session; si vous ne faites que des requêtes SQL simples non liées à des objets, il vaut probablement mieux utiliser directement les connexions.

Neal
la source
1
Les instructions d'insertion ne devraient-elles pas être placées entre guillemets ""?
mingchau
2
@mingchau Oui, vous avez raison, mes guillemets simples se seraient interférés les uns avec les autres, les guillemets doubles sont beaucoup plus faciles pour éviter ce problème. Actualisé.
Neal
Étant donné la session créée, comment ma session est-elle liée à ma connexion PostgreSQL?
Raju yourPepe
@RajuyourPepe my_session.connection(). Documents: docs.sqlalchemy.org/en/13/orm/… .
Neal
Sérieusement ? L'objet 'Session' n'a pas d'attribut 'connect' ", c'est ce que j'ai trouvé
Raju yourPepe
0

Voici un exemple d'exécution de DCL (Data Control Language) tel que GRANT

def grantAccess(db, tb, user):
  import sqlalchemy as SA
  import psycopg2

  url = "{d}+{driver}://{u}:{p}@{h}:{port}/{db}".\
            format(d="redshift",
            driver='psycopg2',
            u=username,
            p=password,
            h=host,
            port=port,
            db=db)
  engine = SA.create_engine(url)
  cnn = engine.connect()
  trans = cnn.begin()
  strSQL = "GRANT SELECT on table " + tb + " to " + user + " ;"
  try:
      cnn.execute(strSQL)
      trans.commit()
  except:
      trans.rollback()
      raise
Jie
la source