I have a SQLAlchemy model that represents a file and thus contains the path to an actual file. Since deletion of the database row and file should go along (so no orphaned files are left and no rows point to deleted files) I added a delete()
method to my model class:
def delete(self):
if os.path.exists(self.path):
os.remove(self.path)
db.session.delete(self)
This works fine but has one huge disadvantage: The file is deleted immediately before the transaction containing the database deletion is committed.
One option would be committing in the delete()
method - but I don't want to do this since I might not be finished with the current transaction. So I'm looking for a way to delay the deletion of the physical file until the transaction deleting the row is actually committed.
SQLAlchemy has an after_delete
event but according to the docs this is triggered when the SQL is emitted (i.e. on flush) which is too early. It also has an after_commit
event but at this point everything deleted in the transaction has probably been deleted from SA.
delete() is invoked upon an object and the Session is flushed, the row is deleted from the database.
commit() commits (persists) those changes to the database. flush() is always called as part of a call to commit() (1). When you use a Session object to query the database, the query will return results both from the database and from the flushed parts of the uncommitted transaction it holds.
SQLAlchemy execute() return ResultProxy as Tuple, not dict.
When using SQLAlchemy in a Flask app with Flask-SQLAlchemy it provides a models_committed signal which receives a list of (model, operation)
tuples. Using this signal doing what I'm looking for is extremely easy:
@models_committed.connect_via(app)
def on_models_committed(sender, changes):
for obj, change in changes:
if change == 'delete' and hasattr(obj, '__commit_delete__'):
obj.__commit_delete__()
With this generic function every model that needs on-delete-commit code now simply needs to have a method __commit_delete__(self)
and do whatever it needs to do in that method.
It can also be done without Flask-SQLAlchemy, however, in this case it needs some more code:
after_delete
event.after_commit
event.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With