2014-07-14 2 views
5

Ricevo il seguente errore sul secondo aggiornamento di una pagina: DetachedInstanceError: l'istanza non è associata a una sessione; operazione di aggiornamento attributo non può procederePython (framework Pyramid) sta conservando i dati tra le richieste e non riesco a capire perché

DetachedInstanceError: Instance <MetadataRef at 0x107b2a0d0> is not bound to a Session; attribute refresh operation cannot proceed 

- Expression: "result.meta_refs(visible_search_only=True)" 
- Filename: ... ects/WebApps/PYPanel/pypanel/templates/generic/search.pt 
- Location: (line 45: col 38) 
- Source:  ... meta_ref result.meta_refs(visible_search_only=True)" tal:omi ... 
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
- Arguments: repeat: {...} (0) 
       renderer_name: templates/home.pt 
       models: <list - at 0x1069e4d88> 
       results: <list - at 0x107c30d40> 
       renderer_info: <RendererHelper - at 0x1069b5650> 
       active_models: <list - at 0x107b69050> 
       query: 
       pagination: <NoneType - at 0x104dd5538> 
       req: <Request - at 0x107b4e690> 
       title: <NoneType - at 0x104dd5538> 
       generic: <NoneType - at 0x104dd5538> 
       request: <Request - at 0x107b4e690> 
       context: <RootFactory - at 0x107b12090> 
       page: 1 
       view: <Page - at 0x107b128d0> 

Il problema sembra essere una condivisione di dati memorizzati nella cache tra le richieste. Il fatto è che si suppone solo per essere nella cache locale (cioè tutto ri-query per la richiesta successiva)

La sezione dedicata del modello è:

 <div tal:repeat="meta_ref result.meta_refs(visible_search_only=True)" tal:omit-tag="True"> 
      <div tal:define="meta result.meta(meta_ref.key, None)" tal:condition="meta is not None"> 
       <div>${meta_ref.name} = ${meta}</div> 
      </div> 
     </div> 

mio DBSession è dichiarato solo una volta, nei modelli .py (se questo fa la differenza):

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) 

Se smetto di caching che la fissa, il che significa che solo bisogno di fare non cache tra le richieste, che non so come fare.

Questa è la mia funzione di meta_refs:

def meta_refs(self, visible_only=False, visible_search_only=False): 
    model = self.__class__.__name__ 
    if Base._meta_refs is None: 
     Base._meta_refs = {} 
     try: 
      for result in DBSession.query(MetadataRef): 
       if result.model not in Base._meta_refs: 
        Base._meta_refs[result.model] = [] 
       Base._meta_refs[result.model].append(result) 
     except DBAPIError: 
      pass 
    if model not in Base._meta_refs: 
     return [] 
    results = [] 
    for result in Base._meta_refs[model]: 
     #@TODO: Remove temporary workaround 
     if inspect(result).detached: 
      Base._meta_refs = None 
      return self.meta_refs(visible_only, visible_search_only) 
     #END of workaround 
     if visible_only and result.visible is False: 
      continue 
     if visible_search_only and result.visible_search is False: 
      continue 
     results.append(result) 
    return results 

E 'anche interessante notare che il meta() la funzione memorizza nella cache anche e non ha lo stesso problema - credo che probabilmente la differenza fondamentale è che si memorizza nella cache una dict di stringhe invece di oggetti ORM.

sto usando pserve di servire mentre sto sviluppando esso (anche se questo fa la differenza)

La soluzione temporanea nel mio codice, utilizzando sqlalchemy.inspect, funziona, ma ho davvero voglia i dati per non persistere (cioè Base._meta_refs dovrebbe essere uguale a Nessuno la prima volta che accedo al 100% delle volte).

Qualcuno ha qualche idea? Se questo viene memorizzato nella cache tra le richieste, sono sicuro che ci sono anche altre cose, e questo è troppo potenziale per un comportamento inaspettato.

risposta

3

Supponendo Base è una classe, si utilizza l'attributo _meta_refs per memorizzare le istanze MetadataRef e in modo efficace mantenerle permanenti tra le richieste.

Se SQLAlchemy Session identity map che in molti casi funziona come una cache non è sufficiente, è possibile utilizzare l'oggetto richiesta per archiviare tali oggetti e sapere che verranno mantenuti per tutta la durata della richiesta.

E mi piacerebbe semplificare meta_refs metodo come segue:

@classmethod 
def meta_refs(cls, visible_only=False, visible_search_only=False): 
    q = DBSession.query(MetadataRef).filter(MetadataRef.model==cls.__name__) 
    if visible_only: 
     q = q.filter(MetadataRef.visible==True) 
    if visible_search_only: 
     q = q.filter(MetadataRef.visible_search==True) 

    # It might be worth returning q rather than q.all() 
    return q.all() 
+0

Mentre sono tutti per la semplificazione, ho bisogno di tutti i dati tramite query SQL 1 (e non li vogliono a persistere tra le richieste). .. Richiede 4.3 ms avg per recuperare tutte le righe o 3.8 ms avg per recuperarle per un singolo modello, quindi inefficiente se sto recuperando più di un modello che è necessario per la maggior parte delle richieste. Le file sono minuscole, il che fa la differenza in questo. In ogni caso, come andrei a memorizzare i dati nell'oggetto richiesta? Sembra fattibile, ma non riesco a trovare la documentazione per fare qualcosa del genere. –

+0

Non sapevo che fosse necessario MetadataRef per più di un modello durante l'esecuzione di una singola richiesta, ma il mio punto era che il codice è troppo complicato per quello che sembra fare. SQL Query è un ottimo strumento e non ci sono buoni motivi per non usarlo a favore di cicli e filtri "manuali". –

+0

Ad ogni modo, la risposta alla tua domanda è nella primissima frase del mio post originale.Non esitate a trascurare il resto. –