I've actually never encountered this error before:
sqlalchemy.exc.InvalidRequestError: stale association proxy, parent object has gone out of scope
After doing some research, it looks like its because the parent object is being garbage collected while the association proxy is working. Fantastic.
However, I'm not sure where it's happening.
Relevant code:
# models.pyclass 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 appreturn TagSchema(many=True).dump(Artist.query.get(id).tags).data
I know it's an old question, but I haven't found a clear solution to a similar problem anywhere on the web, so I've decided to reply here.
The key here is to assign the object that holds the association proxy to a variable before performing any further operations on them. Association proxies aren't regular object properties which would force the GC to hold the reference to the parent object. Actually, the call in form of:
tags = association_proxy('_tags', 'tag', creator=lambda t: ArtistTag(tag=t))
will result in creation of a new AssociationProxy
class object, with a weak reference to the target's collection. In low memory conditions, GC may try to collect Artist.query.get(id)
result, leaving just the result's tags
collection (being a AssociationProxy
class object), but it's required that the object having a association proxy to be present, due to SQLAlchemy's implementation (lazy loading mechanism precisely, I believe).
To fix this situation, we need to make sure that the Artist
object returned from Artist.query.get(id)
call is assigned to a variable, so that the reference count to that object is explicitly of non-zero value. So this:
class ListArtistTag(Resource):def get(self, id):# much safer in actual appreturn TagSchema(many=True).dump(Artist.query.get(id).tags).data
becomes this:
class ListArtistTag(Resource):def get(self, id):artist = Artist.query.get(id)return TagSchema(many=True).dump(artist.tags).data
And it will work as expected. Simple, right?