DjangoのAdminサイトのリスト表示で論理削除したレコードを表示しない方法
・・・がよくわかんなかったんだけどそれなりにできてしまって、このやり方に全く自信がないという話です。
-
- -
地域と店舗があって多対1の関係とします。「地域 has many 店舗」です。
テーブルのカラムにdeleted_atというDateTime型のカラムを持たせてこれに値が入っていたら削除されたレコードだと見なします。
これを「論理削除」ということにします。
この場合、削除されてなかったらNULLになるので、削除されていないレコードをすべて取得したいときは以下のようなSQLを使います。
SELECT * FROM shops WHERE deleted_at IS NULL
一方で本当に削除する場合は「物理削除」ということにします。
-
- -
さて。
DjangoのAdminサイトでは物理削除が行われるので、この挙動を変更したいです。
これは以下のようにモデルクラスでdelete()メソッドをオーバーライドすれば良さそうです。
class Shop(models.Model): def delete(self): self.deleted_at = datetime.datetime.now() self.save()
これで「削除」するとdeleted_atに現在時刻が入るようになります。
次に、レコードを取得するときにはデフォルトでdeleted_at IS NOT NULLなレコードを除外したいです。
http://michilu.com/django/doc-ja/model-api/を読むと、モデルにデータベースアクセスのインターフェースを提供しているのはマネージャと呼ばれるクラスであり、これは拡張可能なようです。
Manager.get_query_set() メソッドをオーバライドすれば、 Manager のベー スの QuerySet をオーバライドできます。 get_query_set() は必要なプロ パティを備えた QuerySet を返さねばなりません。
例えば、以下のモデルには 二つの マネジャがあります。片方は全てのオブジェ クトを返し、もう一方は Roald Dahl の書いた本だけを返します:
こんな風に書いてあるので、コレのまねをしてget_query_set()メソッドをオーバーライドしたいと思います。
class ShopManager(models.Manager): def get_query_set(self): return super(ShopManager, self).get_query_set().filter( deleted_at__isnull=True, area__deleted_at__isnull=True ) class Shop(models.Model): objects = ShopManager()
こんな感じにしました。
店舗とそれが属する地域が「論理削除」されていなければ可視なレコードです。
なんか、すごくいい感じです。
これでAdminサイトを見てみると・・・ふつーに表示されてる!!
論理削除されたはずのレコードがふつーにリスト表示されています。
なぜ??
クリックして編集用の画面を表示しようとすると404になるのでこの挙動は問題なさそうです。
でもリストには表示されてる。
うーん。
小1時間悩んだ末、しょうがないのでDjangoのソースを見てみることにしました。
たぶん該当箇所はdjango.admin.views.main.pyのChangeList()だと見当をつけました。change_list()の中で呼ばれてる関数です。
class ChangeList(object): def __init__(self, request, model): self.model = model self.opts = model._meta self.lookup_opts = self.opts self.manager = self.opts.admin.manager (中略) self.query_set = self.get_query_set()
get_query_set()はさっきオーバーライドしたばかりなので気になります。
で、self.managerはself.opts.admin.managerです。で、self.optsはmodel._metaです。
ここは野生の勘で、「きっとShopクラスの中のAdminクラスのmanagerをカスタムマネージャにすればいいんじゃないか?」と思いました。
class Shop(models.Model): class Admin: manager = ShopManager()
これでAdminサイトを見てみると・・・リストから消えてる!!
論理削除したレコードがリストから消えてます。まさに思い描いていた動作です。
でも、、これでいいのか?
全く自信がありません。。