Hi,

I'm trying to implement soft deletion in my app. I'd like to keep things 
atomic so that if an error happens during the soft deletion process, I 
don't get half deleted stuff.

Here is a simplified view of my code (full working example 
here: https://gist.github.com/4329926):

class User(Base):
    __tablename__ = 'users_test_transaction'
 
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(200))
    password = Column(Unicode(200))
 
    def delete_password(self):
        self.password = ""
        # Not sure if this is right, but my code has a lot of commit in a
        # model's method. This prevents me from using begin_nested().
        Session.commit()
 
    def delete(self):
        self.delete_password()
        raise TypeError("Nothing")
        self.name = "Deleted"
        Session.commit()
 
 
# Fake view
def delete_user(user):
    # I would like this function to happen in a transaction, so that if
    # any exception is raised in delete, it rolls back everything. Problem 
is,
    # user.delete() might directly or inderectly call commit().
    try:
        user.delete()
    except:
        # Because there's a commit in delete_password, this rollback will do
        # nothing.
        Session.rollback()

So the problem here is that the rollback does nothing, because there's a 
commit in the delete_password method (in this example there's only method 
called, in reality I have multiple methods). I think having stuff being 
committed in a model's method is antipattern (is it?) but I'd like to know 
if there's a simple way to get the desired behavior. I tried:

   1. Manually setting a savepoint (Session.execute("SAVEPOINT soft_delete")). 
   But commit in delete_password closes the connection so the savepoint is 
   lost.
   2. Using begin_nested and begin(subtransactions=True), but commit in 
   delete_password closes the context.
   3. I thought of using autocommit=True but it seems it's not recommended 
   (and I'm not sure to understand all the implications of this), or creating 
   an external 
transaction<http://docs.sqlalchemy.org/en/latest/orm/session.html#joining-a-session-into-an-external-transaction>
 but 
   the example in the doc is only for tests so I'm not sure if it's 
   antipattern too. Also, getting access to the engine would require quite a 
   small refactor of my app.

Any idea? Should I get rid of the commit in the model's methods (or add a 
do_not_commit arg to the function)? I wish there were a simple solution 
that do not require changing the model methods.

Thanks,

Charles

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/sqlalchemy/-/Mgi5ByBSuLMJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to