これで、アプリの実装からソリューションが完成します。一部のコードは、LWN の回答からのものです。
データが削除される 4 つの状況があります。
- SQL クエリ
delete()
Model インスタンスの呼び出し:project.delete()
- QuerySet
delete()
インスタンスの呼び出し:Project.objects.all().delete()
- 他モデルのForeignKeyフィールドで削除
最初のケースでできることはあまりありませんが、他の 3 つのケースはきめ細かく制御できます。アドバイスの 1 つは、ほとんどの場合、データ自体は決して削除しないでください。これらのデータは、アプリケーションの履歴と使用状況を反映しているためです。active
代わりに、ブール フィールドでの設定が優先されます。
Model インスタンスを防ぐdelete()
には、Model 宣言でサブクラス化delete()
します。
def delete(self):
self.active = False
self.save(update_fields=('active',))
QuerySetインスタンスでは、 LWN の回答のdelete()
ように、カスタム オブジェクト マネージャーを使用して少しセットアップする必要があります。
これを再利用可能な実装にまとめます。
class ActiveQuerySet(models.QuerySet):
def delete(self):
self.save(update_fields=('active',))
class ActiveManager(models.Manager):
def active(self):
return self.model.objects.filter(active=True)
def get_queryset(self):
return ActiveQuerySet(self.model, using=self._db)
class ActiveModel(models.Model):
""" Use `active` state of model instead of delete it
"""
active = models.BooleanField(default=True, editable=False)
class Meta:
abstract = True
def delete(self):
self.active = False
self.save()
objects = ActiveManager()
使用法、クラスをサブクラス化するだけActiveModel
です:
class Project(ActiveModel):
...
それでも、ForeignKey フィールドのいずれかが削除された場合でも、オブジェクトを削除できます。
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager')
>>> manager.delete() # this would cause `project` deleted as well
これは、Model フィールドのon_delete 引数を追加することで防ぐことができます。
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager',
on_delete=models.PROTECT)
のデフォルトはon_delete
、(のサブクラス)を発生させる代わりにCASCADE
使用することにより、インスタンスが削除されます。これのもう 1 つの目的は、データの ForeignKey を参照として保持することです。PROTECT
ProtectedError
IntegrityError