web-dev-qa-db-fra.com

SQLAlchemy: moteur, connexion et différence de session

J'utilise SQLAlchemy et il y a au moins trois entités: engine, session et connection, qui ont la méthode execute, donc si je p. Ex. veux sélectionner tous les enregistrements de table je peux le faire

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

et ça

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

et même cela

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

- les résultats seront les mêmes.

Si je comprends bien, si quelqu'un utilise engine.execute il crée connection, ouvre session (Alchemy s'en occupe pour vous) et exécute la requête. Mais existe-t-il une différence globale entre ces trois manières de réaliser une telle tâche?

88
ololobus

Un aperçu d'une ligne:

Le comportement de execute() est identique dans tous les cas, mais il existe 3 méthodes différentes, dans les classes Engine, Connection et Session.

En quoi consiste exactement execute():

Pour comprendre le comportement de execute(), nous devons examiner la classe Executable. Executable est une superclasse pour tous les types d’objets "instruction", y compris select (), delete (), update (), insert (), text () - en termes simples, un Executable est une construction d'expression SQL prise en charge dans SQLAlchemy.

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


Pour clarifier davantage (uniquement à des fins de clarification conceptuelle, ce n'est 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 le execute() directement sur tout Executable construit. La classe Executable a sa propre implémentation de execute() - Selon la documentation officielle, une description de la fonction de la fonction execute() est " Compilez et exécutez ce Executable ". Dans ce cas, nous devons explicitement lier la Executable (construction d’expression SQL) à un objet Connection ou à un objet Engine (qui obtient implicitement un objet Connection ), donc la execute() saura où exécuter le SQL.

L'exemple suivant le montre bien - à partir 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 ie Connection.execute() - passage du texte SQL ou de l'expression SQL construite à la méthode execute() 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 ie Engine.execute() - passage du texte SQL ou de l'expression SQL construite directement à la méthode execute() de Moteur:

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

Exécution implicite ie Executable.execute() - est également sans connexion et appelle la méthode execute() de la Executable, c’est-à-dire qu’elle appelle la méthode execute() directement sur la construction d’expression SQL (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 - ce mode d’exécution est vivement déconseillé - conformément à docs :

L '"exécution implicite" est un modèle d'utilisation très ancien qui, dans la plupart des cas, crée plus de confusion qu'il n'est utile, et son utilisation est découragée. Les deux modèles semblent encourager l'utilisation excessive de "raccourcis" opportuns dans la conception des applications, ce qui entraîne des problèmes ultérieurement.


Vos questions:

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

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

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

Mais existe-t-il une différence globale entre ces trois manières de réaliser une telle tâche?

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

  • Exécution directe - Utilisation de Engine.execute() ou Connection.execute()
  • Avec sessions - traite efficacement les transactions comme une seule unité de travail, facilement via session.add(), session.rollback(), session.commit(), session.close(). C'est le moyen d'interagir avec la base de données dans le cas d'ORM, c'est-à-dire des tables mappées. Fournit identity_map pour obtenir instantanément les objets déjà consultés ou nouvellement créés/ajoutés au cours d'une seule demande.

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

Un extrait du docs :

Il est important de noter que lors de l'utilisation de SQLAlchemy ORM, ces objets ne sont généralement pas utilisés. à 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, Engine et Connection sont roi (et reine?) - à lire.

92
Nabeel Ahmed

La réponse de Nabeel couvre beaucoup de détails et est utile, mais j'ai trouvé cela déroutant à suivre. Dans la mesure où il s’agit actuellement du premier résultat Google relatif à ce problème, j’ajoute ce que je comprends aux futurs utilisateurs qui trouveront cette question:

Lancer .execute ()

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

Les différences entre ces trois objets deviennent importantes en fonction du contexte dans lequel l'instruction SELECT est utilisée ou, plus généralement, lorsque vous souhaitez effectuer d'autres tâches telles que INSERT, DELETE, etc.

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

  • Le moteur est l'objet de niveau le plus bas utilisé par SQLAlchemy. Il maintient un pool de connexions utilisable chaque fois que l’application doit communiquer avec la base de données. .execute() est une méthode pratique qui appelle d'abord conn = engine.connect(close_with_result=True) puis 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 tilisation de base .

  • Connection est (comme nous l'avons vu ci-dessus) la chose qui exécute réellement le travail d'exécution d'une requête SQL. Vous devriez le faire chaque fois que vous souhaitez contrôler davantage les attributs de la connexion, la fermer, etc. Par exemple, un exemple très important de cela est un Transaction , qui vous permet de décider quand modifications à la base de données. En utilisation normale, les modifications sont auto-validées. Avec l'utilisation de transactions, vous pouvez (par exemple) exécuter plusieurs instructions SQL différentes et, en cas de problème, 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 si l'une d'elles échouait, 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 avez besoin de contrôle, utilisez des connexions

  • Les sessions sont utilisées pour l'aspect ORM (Object Relationship Management) de SQLAlchemy (en fait, vous pouvez voir cela de la manière dont elles ont été 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 transmet à 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 directes non liées à des objets, il est probablement préférable d'utiliser directement des connexions.

73
Neal

Voici un exemple d’exécution de DCL (langage de contrôle de données) 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
0
Jie