Problemas con scoped_session en sqlalchemy: ¿cómo funciona?

No estoy realmente seguro de cómo funciona scoped_session, aparte de que parece ser una envoltura que oculta varias sesiones reales, manteniéndolas separadas para diferentes solicitudes. ¿Hace esto con los locales de hilo?

De todos modos el problema es el siguiente:

S = elixir.session # = scoped_session(...) f = Foo(bar=1) S.add(f) # ERROR, f is already attached to session (different session) 

No estoy seguro de cómo f terminó en una sesión diferente, no he tenido problemas con eso antes. En otros lugares tengo un código que se ve así, pero en realidad funciona. Como puedes imaginar, me parece muy confuso.

Simplemente no sé nada aquí, parece que f se agrega mágicamente a una sesión en el constructor, pero parece que no tengo ninguna referencia a la sesión que utiliza. ¿Por qué terminaría en una sesión diferente? ¿Cómo puedo conseguir que termine en la sesión correcta? ¿Cómo funciona esta cosa scoped_session de todos modos? Simplemente parece funcionar a veces, y otras veces simplemente no funciona.

Definitivamente estoy muy confundido.

La sesión con ámbito crea un objeto proxy que mantiene un registro de (por defecto) los objetos de sesión de subproceso creados a pedido de la fábrica de sesión pasada. Cuando accede a un método de sesión como ScopedSession.add , encuentra la sesión correspondiente al hilo actual y devuelve el método de add vinculado a esa sesión. La sesión activa se puede eliminar utilizando el método ScopedSession.remove() .

ScopedSession tiene algunos métodos convenientes, uno es query_property que crea una propiedad que devuelve un objeto de consulta vinculado a la sesión con ámbito en la que se creó y la clase a la que se accedió. El otro es ScopedSession.mapper que agrega un __init__(**kwargs) predeterminado __init__(**kwargs) y, de manera predeterminada, agrega los objetos creados a la sesión con ámbito en la que se creó el asignador. Este comportamiento puede ser controlado por el argumento de palabra clave save_on_init al asignador. ScopedSession.mapper está en desuso debido al problema exacto que se encuentra en la pregunta. Este es un caso donde la filosofía de Python “explícito es mejor que implícita” realmente se aplica. Desafortunadamente, el elixir aún usa por defecto ScopedSession.mapper .

Resulta que los conjuntos de elixir save-on-init = True en los mapeadores creados. Esto puede ser deshabilitado por:

 using_mapper_options(save_on_init=False) 

Esto resuelve el problema. Felicitaciones a stepz en #sqlalchemy por averiguar lo que estaba sucediendo de inmediato. Aunque sigo sintiendo curiosidad por cómo realmente funciona el scoped_session, así que si alguien responde eso, obtendrán crédito por responder la pregunta.