sqlalchemy.exc.ResourceClosedError: esta conexión se cierra cuando se inserta después de seleccionar

Estoy haciendo un select() de una base de datos SQLite y luego un insert() :

 engine = create_engine('sqlite:///testdb.db') metadata = MetaData(bind=engine) test = Table('test', metadata, autoload=True) # Select all from pending_data sel = select([test]) res = engine.execute(sel) print res # do an insert into pending_data test.insert()\ .values(info='blah')\ .execute() 

Cuando mi código ejecuta la línea de inserción, aparece este error:

 sqlalchemy.exc.ResourceClosedError: This Connection is closed 

Sin embargo, si convierto mi resolución en una lista como esta:

 res = list(engine.execute(sel)) 

Mi código funciona bien. ¿Que está pasando aqui?

SQLAlchemy tiene dos conceptos que uno debe conocer: conexiones y motores. Un motor puede soportar muchas conexiones simultáneas. En tu ejemplo, vinculas la tabla a un motor. Ahora, cada vez que llame a .execute , se .execute una nueva conexión para cada consulta que ejecute. Pero sqlite3 solo permite 1 “conexión” simultánea.

La mejor manera de solucionar esto es crear la conexión y usarla explícitamente en lugar de las conexiones creadas automáticamente del motor; y usar la conexión con la instrucción with , que garantiza que la conexión se cerrará al final del bloque:

 engine = create_engine('sqlite:///testdb.db') metadata = MetaData(bind=engine) test = Table('test', metadata, autoload=True) with engine.connect() as connection: # Select all from pending_data sel = select([test]) res = connection.execute(sel) # do an insert into pending_data connection.execute(test.insert().values(info='blah')) 

Para comprender este comportamiento, se produce el error porque está sosteniendo un cursor activo dentro de una colección creada y mantenida implícitamente (esto se denomina la variable res ; hasta que lo consume, lo cierra o elimina las referencias, el cursor y así la conexión estará activa, y la base de datos estará bloqueada).

Cuando ejecuta la list(res) , está consumiendo el cursor y SQLAlchemy la cierra; lo mismo sucedería si el recuento de referencia del resultado cayera a 0.

También puedes probar lo siguiente para ver el punto, funcionarían como esperabas:

 res = engine.execute(sel) print(res) res.close() # close explicitly 

o

 res = engine.execute(sel) print(res) del res # drop the only reference to res 

Por lo tanto, siempre consum el ResultProxy por completo o ciérrelo explícitamente, o elimine las referencias cuando termine.

Y esto no es un problema si reutiliza la misma conexión ; solo cuando creas una nueva conexión a una base de datos sqlite3 (postgresql, mysql, oracle, etc. maneja esta multa también).