SQLAlchemy: motor, conexión y diferencia de sesión

134

Puedo utilizar SQLAlchemy y hay al menos tres entidades: engine, sessiony connection, que tiene executemétodo, por lo que si por ejemplo, deseo seleccionar todos los registros de tablelo que puedo hacer esto

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

y esto

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

e incluso esto

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

- Los resultados serán los mismos.

Según tengo entendido, si alguien lo usa engine.executecrea connection, abre session(Alchemy se encarga de usted) y ejecuta la consulta. Pero, ¿existe una diferencia global entre estas tres formas de realizar una tarea así?

ololobus
fuente
Creo que su respuesta está aquí: hackersandslackers.com/…
SeF

Respuestas:

123

Una descripción general de una línea:

El comportamiento de execute()es igual en todos los casos, pero son 3 métodos diferentes, en Engine, Connectiony Sessionclases.

Qué es exactamente execute():

Para comprender el comportamiento de, execute()debemos mirar dentro de la Executableclase. Executablees una superclase para todos los tipos de objetos "enunciados", incluidos select (), delete (), update (), insert (), text (); en palabras más simples posibles, Executablees una construcción de expresión SQL compatible con SQLAlchemy.

En todos los casos, el execute()método toma el texto SQL o la expresión SQL construida, es decir, cualquiera de la variedad de construcciones de expresiones SQL compatibles con SQLAlchemy y devuelve los resultados de la consulta (a ResultProxy- Envuelve un DB-APIobjeto cursor para proporcionar un acceso más fácil a las columnas de fila).


Para aclararlo más (solo para aclaración conceptual, no es un enfoque recomendado) :

Además de Engine.execute()(ejecución sin conexión), Connection.execute()y Session.execute(), también es posible usar execute()directamente en cualquier Executableconstrucción. La Executableclase tiene su propia implementación de execute(): Según la documentación oficial, una descripción de una línea sobre lo que execute()hace es " Compilar y ejecutar estoExecutable ". En este caso, necesitamos vincular explícitamente la Executable(construcción de expresión SQL) con un Connectionobjeto o, Engineobjeto (que implícitamente obtiene un Connectionobjeto), para execute()que sepan dónde ejecutar el SQL.

El siguiente ejemplo lo demuestra bien: dada una tabla como la siguiente:

from sqlalchemy import MetaData, Table, Column, Integer

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

Ejecución explícita, es decir Connection.execute(), pasar el texto SQL o la expresión SQL construida al execute()método de Connection:

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

Ejecución explícita sin conexión, es decir Engine.execute(), pasar el texto SQL o la expresión SQL construida directamente al execute()método de Engine:

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

La ejecución implícita, es decir Executable.execute(), también no tiene conexión y llama al execute()método de Executable, es decir, llama al execute()método directamente en el SQLconstructo de expresión (una instancia de Executable) en sí mismo.

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

Nota: Expresé el ejemplo de ejecución implícita con el propósito de aclarar, esta forma de ejecución no es muy recomendable, según los documentos :

La “ejecución implícita” es un patrón de uso muy antiguo que en la mayoría de los casos es más confuso que útil y se desaconseja su uso. Ambos patrones parecen alentar el uso excesivo de "atajos" convenientes en el diseño de la aplicación que conducen a problemas más adelante.


Tus preguntas:

Según tengo entendido, si alguien usa engine.execute, crea conexión, abre sesión (Alchemy se preocupa por usted) y ejecuta la consulta.

Tienes razón para la parte "si alguien lo usa engine.executecrea connection" pero no para "abre session(Alchemy se preocupa por ti) y ejecuta la consulta" - Usando Engine.execute()y Connection.execute()es (casi) una misma cosa, formalmente, el Connectionobjeto se crea implícitamente , y en un caso posterior lo instanciamos explícitamente. Lo que realmente sucede en este caso es:

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

¿Pero hay una diferencia global entre estas tres formas de realizar tal tarea?

En la capa DB es exactamente lo mismo, todos ellos están ejecutando SQL (expresiones de texto o varias construcciones de expresiones SQL). Desde el punto de vista de la aplicación, hay dos opciones:

  • Ejecución directa: utilizando Engine.execute()oConnection.execute()
  • Utilizando sessions- gestiona de manera eficaz transacción como una sola unidad de trabajo, con facilidad a través de session.add(), session.rollback(), session.commit(), session.close(). Es la forma de interactuar con la base de datos en el caso de ORM, es decir, tablas mapeadas. Proporciona identity_map para obtener instantáneamente objetos ya accedidos o recién creados / agregados durante una sola solicitud.

Session.execute()en última instancia, utiliza el Connection.execute()método de ejecución de sentencias para ejecutar la sentencia SQL El uso de Sessionobjetos es la forma recomendada de SQLAlchemy ORM para que una aplicación interactúe con la base de datos.

Un extracto de los documentos :

Es importante tener en cuenta que cuando se usa el SQLAlchemy ORM, generalmente no se accede a estos objetos; en cambio, el objeto Session se usa como interfaz para la base de datos. Sin embargo, para las aplicaciones que se basan en el uso directo de sentencias SQL textuales y / o construcciones de expresiones SQL sin la participación de los servicios de gestión de nivel superior del ORM, el motor y la conexión son el rey (¿y la reina?). Siga leyendo.

Nabeel Ahmed
fuente
La palabra "sin conexión" implica que no se está creando ninguna conexión, lo que según la respuesta de Neal no es el caso.
Atom
111

La respuesta de Nabeel cubre muchos detalles y es útil, pero me pareció confuso seguirla. Dado que este es actualmente el primer resultado de Google para este problema, agrego mi comprensión para futuras personas que encuentren esta pregunta:

Ejecutando .execute ()

Como OP y Nabell Ahmed notan, cuando se ejecuta un plano SELECT * FROM tablename, no hay diferencia en el resultado proporcionado.

Las diferencias entre estos tres objetos no llegan a ser importantes en función del contexto que la SELECTinstrucción se utiliza en o, más comúnmente, cuando se quiere hacer otras cosas como INSERT, DELETE, etc.

Cuándo usar Engine, Connection, Session en general

  • El motor es el objeto de nivel más bajo utilizado por SQLAlchemy. Se mantiene un grupo de conexiones disponibles para su uso siempre que la aplicación necesita hablar con la base de datos. .execute()es un método de conveniencia que primero llama conn = engine.connect(close_with_result=True)y luego conn.execute(). El parámetro close_with_result significa que la conexión se cierra automáticamente. (Estoy parafraseando ligeramente el código fuente, pero esencialmente cierto). editar: Aquí está el código fuente de engine.execute

    Puede usar el motor para ejecutar SQL sin formato.

    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()

    Esto está cubierto en los documentos de uso básico .

  • La conexión es (como vimos anteriormente) lo que realmente hace el trabajo de ejecutar una consulta SQL. Debe hacer esto siempre que desee un mayor control sobre los atributos de la conexión, cuando se cierra, etc. Por ejemplo, un ejemplo muy importante de esto es una Transacción , que le permite decidir cuándo confirmar sus cambios en la base de datos. En uso normal, los cambios se confirman automáticamente. Con el uso de transacciones, podría (por ejemplo) ejecutar varias declaraciones SQL diferentes y si algo sale mal con una de ellas, podría deshacer todos los cambios a la vez.

    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

    Esto le permitirá deshacer ambos cambios si falla uno, como si olvidara crear la tabla de registro de datos.

    Entonces, si está ejecutando código SQL sin procesar y necesita control, use conexiones

  • Las sesiones se usan para el aspecto de Gestión de relaciones de objeto (ORM) de SQLAlchemy (de hecho, puede ver esto por cómo se importan:) from sqlalchemy.orm import sessionmaker. Usan conexiones y transacciones bajo el capó para ejecutar sus declaraciones SQL generadas automáticamente. .execute()es una función de conveniencia que pasa a lo que sea que esté vinculada la sesión (generalmente un motor, pero puede ser una conexión).

    Si está utilizando la funcionalidad ORM, use session; Si solo está haciendo consultas SQL directas no vinculadas a objetos, probablemente sea mejor que use conexiones directamente.

Neal
fuente
1
¿No deberían insertarse declaraciones entre comillas dobles ""?
mingchau
2
@mingchau Sí, tienes razón, mis comillas simples habrían interferido entre sí, las comillas dobles son mucho más fáciles de evitar. Actualizado.
Neal
Dada la sesión creada, ¿cómo se vincula mi sesión con mi conexión PostgreSQL?
Raju yourPepe
@RajuyourPepe my_session.connection(). Documentos: docs.sqlalchemy.org/en/13/orm/… .
Neal
Seriamente ? El objeto 'Session' no tiene el atributo 'connect' ", es lo que he encontrado
Raju yourPepe
0

Aquí hay un ejemplo de ejecución de DCL (lenguaje de control de datos) como 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
fuente