pytest-flask-sqlalchemy
pytest-flask-sqlalchemy copied to clipboard
Instance XXX has been deleted. Use the make_transient() function to send this object back to the transient state. Again.
just like in #5 this happens again with latest versions, unfortunately #6 did not resolve this completely.
When I delete an object via a client call I receive the notorious error:
self = <sqlalchemy.orm.session.SignallingSession object at 0x7f67f0d99510>
state = <sqlalchemy.orm.state.InstanceState object at 0x7f67f0cb6390>
revert_deletion = False
def _update_impl(self, state, revert_deletion=False):
if state.key is None:
raise sa_exc.InvalidRequestError(
"Instance '%s' is not persisted" % state_str(state)
)
if state._deleted:
if revert_deletion:
if not state._attached:
return
del state._deleted
else:
raise sa_exc.InvalidRequestError(
"Instance '%s' has been deleted. "
"Use the make_transient() "
"function to send this object back "
> "to the transient state." % state_str(state)
)
E sqlalchemy.exc.InvalidRequestError: Instance '<YYY at 0x7f67f0cb6a50>' has been deleted. Use the make_transient() function to send this object back to the transient state.
flask-sqlalchemy==2.4.1
pytest-flask-sqlalchemy==1.0.2
sqlalchemy==1.3.11
sqlalchemy-continuum==1.3.9
sqlalchemy-utils==0.36.0
@jeancochrane could you please have a look at it?
Also suffering from this. It wasn't a problem until i started using begin_nested()
in my codebase.
So, in teardown_transaction
, the call to session.remove()
is leading to SQLAlchemy to call expunge_all()
, which detaches objects from the session. That's great.
Except, reyhydrate_object
is listening for those detachments, and trying to re-add them to the session that is currently being destroyed.
https://github.com/jeancochrane/pytest-flask-sqlalchemy/blob/master/pytest_flask_sqlalchemy/fixtures.py#L70-L84
Do I have this right? Is there a way to remove those event listeners before the call to session.remove()
?
(I'm looking into this.)
Yes, I was able to fix this issue by updating teardown_transaction
to look like:
@request.addfinalizer
def teardown_transaction():
# Delete the session
sa.event.remove(session, 'persistent_to_detached', rehydrate_object)
sa.event.remove(session, 'deleted_to_detached', rehydrate_object)
session.remove()
# Rollback the transaction and return the connection to the pool
transaction.force_rollback()
connection.force_close()
return connection, transaction, session
I am not sure whether this is a sane solution, or if it will have other side effects, though. It'd be great to have some feedback.
I've just run into this problem myself. In particular if you're using Flask's test_client()
to make requests and you hit something that deletes a row then commits it, things break in this way.
I'm not sure I understand the case for adding deleted things back into the session anyway. I literally removed the line
@sa.event.listens_for(session, 'deleted_to_detached')
So that this code only fixed on persistent_to_detached
and that definitely solved the issue for me. Does anyone know a good use case for having that behaviour left in there?