使用Flask-SQLAlchemy"陈旧关联代理,父对象已超出范围"

jus*_*anr 6 python sqlalchemy flask flask-sqlalchemy

我以前从未遇到过这个错误:

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
Run Code Online (Sandbox Code Playgroud)

syn*_*p15 10

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

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

tags = association_proxy('_tags', 'tag', creator=lambda t: ArtistTag(tag=t))
Run Code Online (Sandbox Code Playgroud)

将导致创建一个新的AssociationProxy类对象,并对目标的集合进行弱引用.在低内存条件下,GC可能会尝试收集 Artist.query.get(id)结果,只留下结果的tags集合(作为AssociationProxy类对象),但由于SQLAlchemy的实现(精确的延迟加载机制,我相信)需要具有关联代理的对象存在).

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

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

成为这个:

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

它将按预期工作.简单吧?

  • 很好找.在CPython中,你甚至不需要低mem条件,因为临时查询结果的引用计数变为0并且在访问后立即收集它.SQLA的身份映射也很弱,因此它也不会使实例保持活动状态. (2认同)