【问题标题】:"stale association proxy, parent object has gone out of scope" with Flask-SQLAlchemyFlask-SQLAlchemy 的“陈旧的关联代理,父对象已超出范围”
【发布时间】:2015-05-05 04:04:51
【问题描述】:

其实我以前从来没有遇到过这个错误:

sqlalchemy.exc.InvalidRequestError: stale association proxy, parent object has gone out of scope

在做了一些研究之后,看起来是因为在关联代理工作时父对象正在被垃圾收集。太棒了。

但是,我不确定发生在哪里

相关代码:

# models.py

class Artist(db.Model):
    # ...
    tags = association_proxy('_tags', 'tag', 
        creator=lambda t: ArtistTag(tag=t))
    # ...

class Tag(db.Model):
    # ...
    artist = association_proxy('_artists', 'artist', 
        creator=lambda a: ArtistTag(artist=a))
    # ...

class ArtistTag(db.Model):
    # ...
    artist_id = db.Column(db.Integer, ForeignKey('artists.id'))
    artist = db.relationship('Artist', backref='_tags')
    tag_id = db.Column(db.Integer, ForeignKey('tags.id'))
    tag = db.relationship('Tag', backref='_artists')

# api/tag.py
from flask.ext.restful import Resource
from ..
class ListArtistTag(Resource):
    def get(self, id):
        # much safer in actual app
        return TagSchema(many=True)
               .dump(Artist.query.get(id).tags)
               .data

【问题讨论】:

    标签: python flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    我知道这是一个老问题,但我在网络上的任何地方都没有找到类似问题的明确解决方案,所以我决定在这里回复。

    这里的关键是在对变量执行任何进一步操作之前,将保存关联代理的对象分配给变量。关联代理不是常规的对象属性,它会强制 GC 持有对父对象的引用。实际上,调用形式为:

    tags = association_proxy('_tags', 'tag', creator=lambda t: ArtistTag(tag=t))
    

    将导致创建一个新的AssociationProxy 类对象,该对象具有对目标集合的弱引用。在内存不足的情况下,GC 可能会尝试收集 Artist.query.get(id) 结果,只留下结果的 tags 集合(作为 AssociationProxy 类对象),但由于 SQLAlchemy 的原因,需要具有关联代理的对象存在实现(我相信正是延迟加载机制)。

    要解决这种情况,我们需要确保从Artist.query.get(id) 调用返回的Artist 对象被分配给一个变量,以便对该对象的引用计数显式地为非零值。所以这个:

    class ListArtistTag(Resource):
        def get(self, id):
            # much safer in actual app
            return TagSchema(many=True)
                   .dump(Artist.query.get(id).tags)
                   .data
    

    变成这样:

    class ListArtistTag(Resource):
        def get(self, id):
            artist = Artist.query.get(id)
            return TagSchema(many=True)
                   .dump(artist.tags)
                   .data
    

    它会按预期工作。很简单吧?

    【讨论】:

    • 很好的发现。在 CPython 中,您甚至不需要低内存条件,因为临时查询结果的 refcount 变为 0,访问后将立即收集。 SQLA 的身份映射也很弱,所以它也不会让实例保持活动状态。
    • 我将不得不重新审视这个项目,但你所说的一切都是有道理的。
    • 遇到了同样的问题,这解决了。在我的情况下,我直接将属性值分配给一个变量,该变量在其他地方使用。我不得不复制内容 (list(*self.associated_proxy_list))
    猜你喜欢
    • 2019-01-23
    • 2017-08-30
    • 2019-04-04
    • 1970-01-01
    • 2021-03-08
    • 1970-01-01
    • 2011-11-23
    • 2015-01-26
    • 1970-01-01
    相关资源
    最近更新 更多