Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance XXX has been deleted. Use the make_transient() function to send this object back to the transient state. Again. #25

Open
materemias opened this issue Dec 12, 2019 · 4 comments

Comments

@materemias
Copy link

materemias commented Dec 12, 2019

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?

@tomthorogood
Copy link

Also suffering from this. It wasn't a problem until i started using begin_nested() in my codebase.

@tomthorogood
Copy link

tomthorogood commented Feb 20, 2020

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

@tomthorogood
Copy link

tomthorogood commented Feb 20, 2020

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.

@martynsmith
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants