Djangoでアップロードしたファイルを非同期に削除する
Django の FileField を通してアップロードしたファイルを非同期に削除したい。
本質的にはモデルが削除されると同時にファイルも削除したいというものなんだけど、 そこを同期処理しているとやってられないので、非同期で削除しようという話。
案1
- Django のシグナルを使って、モデルが削除されたときに非同期でファイルを削除する タスクを呼び出す。
私はシグナルにいい思い出がなかったので、この方法はちょっと疎遠したかった。
案2
- カスタムのストレージを書いて、ファイルの削除を非同期に行わせる。
これを採用。
残念な思い込み
FileField を持つモデルのインスタンスが delete されるときに、ファイルの delete も走ると思っていた。 でも実際はそうじゃなくてモデルが delete されても、ファイルの delete (ストレージの delete) は走らなかった(まぁ勝手に削除されるのはこわいか)。
明示的に FieldFile インスタンスの delete を呼ぶ必要があった:
class Test(models.Model):
file = models.FileField(....)
test.file.delete() # まぁこんなかんじ。
(ここでモデルのインスタンスにある file 属性は django.db.models.fields.FieldFile のインスタンス で、こいつの delete がストレージの delete を呼び出す)
書いたもの
非同期にファイルを削除するストレージを書いている。
でもまぁ結局 UploadedFile モデルの delete をオーバーライドして、ファイルも削除するようにしている。 これなら案1のように、 delete のシグナルでファイル削除用の非同期タスクを走らせても大差なかった。
非同期処理の書き方については自分のブログ記事が役に立った:
まとめ
残念
Djangoのチケット18481についての雑記
Django に投げていた パッチが昨夜取り込まれた のでその話。
前に取り込まれた チケット 18558 以来。
チケット 18481 について
このチケットは、別のチケット 17277 に由来してる。 17277 は POST の body 読み込み時にエラーがあれば、それ専用のエラーをあげるよう修正するというもの。
17277 以前は例外処理が入っていないので、例外があれば IOError が投げられていた。 でもこれってわかりにくいから、 IOError を継承した UnreadablePostError を投げるよう修正された。
でもこのチケット 17277 には漏れがあって、 FILES の場合はそのままだった。 なので今回取り込まれたチケット 18481 では、FILES で例外が発生するときも UnreadablePostError を 投げるよう修正しましょうというもの。
やったこと
私がこのチケットをみたときには、すでにバグ修正のチケットがあったのでテストのパッチを書いた (KyleMac に報告されて、 edevil が実装の修正を追加していた)。
owner もついていなかったしチケットも 2 ヶ月ほど放置されていたので、 ここはテストのパッチを書いてやるかと思った次第。
チケット 17277 で追加されたチケットを参考に、 FILES で例外が発生するようにしたテストを書いた。
取り込んでくれたのは claudep 、また彼にお世話になった。