2014-07-14 12 views
5

Otrzymuję następujący błąd podczas drugiego odświeżania strony: DetachedInstanceError: Instancja nie jest związana z sesją; przypisują odświeżyć operacja nie może przystąpićPython (framework Pyramid) utrzymuje dane między żądaniami i nie mogę zrozumieć, dlaczego

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> 

Problem wydaje się być jakiś podział pamięci podręcznej danych pomiędzy żądaniami. Chodzi o to, że to ma tylko być buforowane lokalnie (czyli wszystko ponowne zapytanie do następnego życzenie)

Odpowiednia sekcja szablonu jest:

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

Moja DBSession jest zadeklarowany tylko raz, w modelach .py (jeśli to ma znaczenie):

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

Gdybym zatrzymać buforowanie to rozwiązuje go, co oznacza, że ​​wystarczy, aby go nie buforują pomiędzy żądaniami, które nie wiem jak to zrobić.

To mój meta_refs funkcja:

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 

Warto również zauważyć, że meta() funkcja buforuje również i nie ma ten sam problem - myślę, że prawdopodobnie Kluczową różnicą jest to, że buforuje dyktować ciągi zamiast obiektów ORM.

Używam pserve jej służyć, a ja jestem jej rozwój (także jeśli to robi różnicę)

Tymczasowe obejście w moim kodu, używając sqlalchemy.inspect, działa, ale ja naprawdę chcę dane po prostu nie trwają (tzn. Base._meta_refs powinny być równe None za pierwszym razem, gdy uzyskuję do nich dostęp w 100% przypadków).

Ktoś ma jakieś pomysły? Jeśli jest to buforowane między żądaniami, jestem pewien, że jest jeszcze coś innego, a to jest zbyt duży potencjał dla nieoczekiwanego zachowania.

Odpowiedz

3

Zakładając, że baza jest klasą, użyjesz jej atrybutu _meta_refs do przechowywania instancji MetadataRef i skutecznie utrzymasz je między żądaniami.

Jeśli SQLAlchemy Session identity map, która w wielu przypadkach działa jak pamięć podręczna, nie wystarczy, można użyć obiektu żądania do przechowywania tych obiektów i wiedzieć, że będą one zachowane tylko przez całe życie żądania.

A ja uprościć meta_refs sposób jak następuje:

@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

Chociaż jestem za uproszczeniem, muszę wszystkie dane przez 1 zapytaniu SQL (i nie chce im się utrzymywać między wniosków). Aby pobrać wszystkie wiersze lub średnio 3,8 ms, potrzeba 4,3 ms średniej, co jest mało wydajne, jeśli pobieram więcej niż jeden model, który jest niezbędny dla większości żądań. Rzędy są małe, co ma wpływ na to. W każdym razie, w jaki sposób chciałbym przechowywać dane w obiekcie żądania? Brzmi to wykonalnie, ale nie mogę znaleźć dokumentacji na temat robienia czegoś takiego. –

+0

Nie wiedziałem, że potrzebujesz MetadataRef dla więcej niż jednego modelu podczas serwowania pojedynczego żądania, ale moim celem było to, że twój kod jest zbyt skomplikowany dla tego, co wydaje się robić. Kwerenda SQLAlchemy jest świetnym narzędziem i nie ma żadnego powodu, aby nie używać go na rzecz pętli i "ręcznego" filtrowania. –

+0

W każdym razie odpowiedź na twoje pytanie jest w pierwszym zdaniu mojego pierwotnego postu.Prosimy o zignorowanie reszty. –

Powiązane problemy