My environment is Django3.2.6+sqlite+Python3.8:
# crossover/models.py
from django.db import models
class SoftDeleteQuerySet(models.QuerySet):
def delete(self):
return self.update(deleted=True)
def force_delete(self):
return super().delete()
class FilteDeletedManager(models.Manager):
def datas(self):
return SoftDeleteQuerySet(self.model, self._db)
def get_queryset(self):
return SoftDeleteQuerySet(self.model, self._db).filter(deleted=False)
class AbsModel(models.Model):
objects = FilteDeletedManager()
deleted = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def delete(self, **kwargs):
self.deleted = True
return self.save(**kwargs)
def force_delete(self, **kwargs):
return super().delete(**kwargs)
def to_dict(self):
return {f.name: getattr(self, f.name) for f in self._meta.fields}
class Meta:
abstract = True
class Player(AbsModel):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
Usage::
>>> from crossover.models import *
>>> p1, p2 = Player.objects.create(name='LBJ'), Player.objects.create(name='KD')
>>> Player.objects.all()
<SoftDeleteQuerySet [<Player: LBJ>, <Player: KD>]>
>>> Player.objects.filter(id=1).delete()
1
>>> Player.objects.all()
<SoftDeleteQuerySet [<Player: KD>]>
>>> Player.objects.datas()
<SoftDeleteQuerySet [<Player: LBJ>, <Player: KD>]>
>>> p2.delete()
>>> Player.objects.all()
<SoftDeleteQuerySet []>
>>> Player.objects.datas()
<SoftDeleteQuerySet [<Player: LBJ>, <Player: KD>]>
>>> p1.force_delete()
(1, {'crossover.Player': 1})
>>> Player.objects.datas()
<SoftDeleteQuerySet [<Player: KD>]>
>>> Player.objects.datas().force_delete()
(1, {'crossover.Player': 1})
>>> Player.objects.datas()
<SoftDeleteQuerySet []>
About ForeignKey fields: when mark it as deleted, mark the children of it as deleted too.
from django.db.models.deletion import Collector
class MyCollector(Collector):
def delete(self):
...
with transaction.atomic(using=self.using, savepoint=False):
# update deleted=True
class AbsModel(models.Model):
def delete(self, using=None, keep_parents=False):
using = using or router.db_for_write(self.__class__, instance=self)
assert self.pk is not None
collector = MyCollector(using=using)
collector.collect([self], keep_parents=keep_parents)
return collector.delete()
...