2010-06-14 11 views
36

Acabo de empezar a usar SQLAlchemy y obtener un DetachedInstanceError y no puedo encontrar mucha información sobre esto en ninguna parte. Estoy usando la instancia fuera de una sesión, por lo que es natural que SQLAlchemy no pueda cargar ninguna relación si no están cargados, sin embargo, el atributo al que estoy accediendo no es una relación, de hecho este objeto no tiene ninguna relación. Encontré soluciones como la carga ansiosa, pero no puedo aplicarme a esto porque esta no es una relación. Incluso intenté "tocar" este atributo antes de cerrar la sesión, pero aún así no impide la excepción. ¿Qué podría estar causando esta excepción para una propiedad no relacional incluso después de haber sido accedida con éxito una vez antes? Se agradece cualquier ayuda para depurar este problema. Mientras tanto intentaré obtener un escenario independiente reproducible y actualizar aquí.SQLAlchemy DetachedInstanceError con el atributo regular (no es una relación)

Actualización: Este es el mensaje de excepción real con algunas pilas:

File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 159, in __get__ 
    return self.impl.get(instance_state(instance), instance_dict(instance)) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 377, in get 
    value = callable_(passive=passive) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/state.py", line 280, in __call__ 
    self.manager.deferred_scalar_loader(self, toload) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/mapper.py", line 2323, in _load_scalar_attributes 
    (state_str(state))) 
DetachedInstanceError: Instance <ReportingJob at 0xa41cd8c> is not bound to a Session; attribute refresh operation cannot proceed 

El modelo parcial se ve así:

metadata = MetaData() 
ModelBase = declarative_base(metadata=metadata) 

class ReportingJob(ModelBase): 
    __tablename__ = 'reporting_job' 

    job_id   = Column(BigInteger, Sequence('job_id_sequence'), primary_key=True) 
    client_id  = Column(BigInteger, nullable=True) 

Y client_id campo es lo que está causando esta excepción con una el uso como el siguiente:

consulta:

jobs = session \ 
      .query(ReportingJob) \ 
      .filter(ReportingJob.job_id == job_id) \ 
      .all() 
    if jobs: 
     # FIXME(Hari): Workaround for the attribute getting lazy-loaded. 
     jobs[0].client_id 
     return jobs[0] 

Esto es lo que desencadena la excepción más adelante fuera del ámbito de sesión:

 msg = msg + ", client_id: %s" % job.client_id 
+0

Esto puede ayudar: http://stackoverflow.com/a/25694346/134904 – kolypto

Respuesta

53

He encontrado la causa fundamental al tratar de restringir el código que provocó la excepción. Coloqué el mismo código de acceso de atributo en diferentes lugares después de cerrar la sesión y encontré que definitivamente no causa ningún problema inmediatamente después del cierre de la sesión de consulta. Resulta que el problema comienza a aparecer después de cerrar una nueva sesión que se abre para actualizar el objeto. Una vez que entendí que el estado del objeto no se puede usar después de una sesión cerrada, pude encontrar este thread que discutió este mismo problema. Dos soluciones que salen de la rosca son:

  • Mantener una sesión abierta (que es obvio)
  • Especificar expire_on_commit=False-sessionmaker().

La tercera opción es establecer manualmente a expire_on_commitFalse en la sesión una vez que se crea, algo así como: session.expire_on_commit = False. Verifiqué que esto resuelve mi problema.

+4

¿Ayuda 'session.expunge_all()' en absoluto? – Sardathrion

+0

¿Cómo ayuda eso? Parece que realmente borra la sesión de todos los objetos cargados. – haridsv

+0

'session.expire_on_commit = False' es malo, ya que si la base de datos ha convertido los valores a algo, nunca lo sabrá – kolypto

8

Estábamos obteniendo errores similares, incluso con expire_on_commit establecido en False. Al final, fue causado por tener dos sessionmaker s que se estaban acostumbrando a hacer sesiones en diferentes solicitudes. Realmente no entiendo lo que estaba pasando, pero si ve esta excepción con expire_on_commit=False, asegúrese de que no tiene dos sessionmaker inicializados.

0

En cuanto a mí (novato), cometí un error en la sangría y cerré la sesión dentro de mi ciclo, en la que bucle cada fila, realizo algunas operaciones y me comprometo cada vez.

Así que para los novatos como yo, revise su código antes de configurar cosas como expire_on_commit=False, puede llevarlo a otra trampa.

Cuestiones relacionadas