Django Adminサイトの一覧画面からレコードが見えないケースもある

Django Adminサイトの一覧画面で見えない = レコードが無い訳では無い。

「あれ?レコードが無いから削除されているはずだよな・・・?」と勘違いしていたので再現方法を書く。

モデルを用意する

例として、所属部署と社員のテーブルを作成する。 社員側のテーブルのon_deletedb_constraintの設定値がポイント。

from django.db import models


class Department(models.Model):
    """所属部署"""
    name = models.CharField("名前", max_length=100)

    def __str__(self):
        return self.name


class Employee(models.Model):
    """社員"""
    name = models.CharField("名前", max_length=100)
    department = models.ForeignKey(
        Department, 
        on_delete=models.DO_NOTHING, 
        db_constraint=False,
    )

    def __str__(self):
        return self.name

レコードを登録する

そして、適当なレコードを登録する。

  • 営業部: KIRINJI
  • 仕入部: マカロニえんぴつ
  • 物流部: 平井堅
  • 商品企画部: nibu

と登録した。名前は単純に自分が直近再生していたアーティストの名前である。

部署を削除する

部署の商品企画部を削除する。 この時は今回の設定値だと、以下のような挙動になる。

一覧画面を見る

Employeesの一覧画面を見ると、さっきまで表示されていた

  • 商品企画部: nibu

が無くなっている。

しかし、よく見ると 画面の左下には 4 employees と記載されている。

そのためURLを直接操作して開くと、レコードがある事が確認できる。

ちなみに、Django Admin画面上からはdepartmentカラムの値が無さそうに見えるが、実際には4の値が入っている。

sqlite> SELECT * from company_employee;
1|KIRINJI|1
2|マカロニえんぴつ|2
3|平井堅|3
4|nibu|4

以上より、Django Adminサイト上から「ぱっと見」無かったとしてもレコードが削除されたとは言い切れない事がわかる。

ちゃんとDBを見て、レコードを確認する必要がある。

余談

今回のようなケースだと、on_delete=CASCADEを利用したり、そもそもデフォルトの設定値であるdb_constraint=Trueを利用することが多いと思っている(ここは正直まだあまり詳しくない)。

db_constraintについては公式リファレンスにもデフォルトはTrueが望ましいと書かれている。

デフォルトは True で、ほとんどの場合それが望ましい設定です。

また、続けて有効なケースとして以下の例が挙げられている。

無効なレガシーデータを持っている場合。 データベースをシャーディング(分割)している場合。

自分としては記録系のその時に起こった内容を正確に記録する必要があるテーブルだと、こういう設定をする必要があると個人的には考えている。データベース設計は難しい。

参考: 47:トランザクションデータは正確に記録しよう