5

我在第二次刷新页面时收到以下错误: DetachedInstanceError: Instance is not bound to a Session; 属性刷新操作无法进行

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>

问题似乎是请求之间共享缓存数据。问题是它只应该在本地缓存(即为下一个请求重新查询所有内容)

模板的相关部分是:

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

我的 DBSession 仅在 models.py 中声明一次(如果有区别的话):

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

如果我停止缓存它会修复它,这意味着我只需要让它不在请求之间缓存,我不知道该怎么做。

这是我的 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

还值得注意的是 meta() 函数也缓存并且没有相同的问题——我认为可能的关键区别在于它缓存字符串的字典而不是 ORM 对象。

我在开发它时使用 pserve 来服务它(如果这有所作为)

我的代码中使用 sqlalchemy.inspect 的临时解决方法确实有效,但我真的只是希望数据不持久(即 Base._meta_refs 在我第一次 100% 访问它时应该等于 None)。

有人有想法么?如果这在请求之间被缓存,我敢肯定还有其他东西也是如此,这对于意外行为的可能性太大了。

4

1 回答 1

3

假设 Base 是一个类,您可以使用它的_meta_refs属性来存储MetadataRef实例并有效地在请求之间保持它们的持久性。

如果在许多情况下像缓存一样工作的 SQLAlchemy 会话身份映射还不够,您可以使用请求对象来存储这些对象并知道它们只会在请求的整个生命周期内持续存在。

我会简化meta_refs如下方法:

@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()
于 2014-07-18T09:57:00.570 回答