Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
A Django model base class that hides models instead of deleting them.
from django.utils import timezone
from django.db import models as m
from django.db.models.base import ModelBase
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields.related import SingleRelatedObjectDescriptor
from model_utils.managers import PassThroughManagerMixin
class DeletableQuerySetMixin(object):
def __init__(self, *args, **kwargs):
if "deleted_filter" in kwargs:
self.deleted_filter = kwargs.pop("deleted_filter")
super(DeletableQuerySetMixin, self).__init__(*args, **kwargs)
def _clone(self, *args, **kwargs):
qs = super(DeletableQuerySetMixin, self)._clone(*args, **kwargs)
qs.deleted_filter = self.deleted_filter
return qs
def delete(self):
def delete_soft(self):
return self.update(
def delete_hard(self):
super(DeletableQuerySetMixin, self).delete()
def active(self):
return self.filter(**self.deleted_filter)
def deleted(self):
return self.exclude(**self.deleted_filter)
class DeletableQuerySet(DeletableQuerySetMixin, m.query.QuerySet):
class DeletableManager(PassThroughManagerMixin, m.Manager):
filter_fields = ["deleted_on"]
queryset_cls = DeletableQuerySet
def __init__(self, queryset_cls=None, include_deleted=False,
*args, **kwargs):
self.include_deleted = include_deleted
self._deleted_filter = dict(
(name, None) for name in self.filter_fields
super(DeletableManager, self).__init__(
queryset_cls=(queryset_cls or self.queryset_cls), *args, **kwargs
def get_queryset(self):
qs = self._queryset_cls(
if not self.include_deleted:
qs =
return qs
get_query_set = get_queryset
class DeletableModelBase(ModelBase):
def __new__(cls, name, bases, attrs):
model = super(DeletableModelBase, cls).__new__(cls, name, bases, attrs)
unique_fields = [
f for f in model._meta.fields
if f.unique and f !=
if unique_fields:
raise AssertionError(
"%r is a DeletableModel but has unique fields: %s"
%(model, ", ".join( for f in unique_fields))
return model
class DeletableModel(m.Model):
__metaclass__ = DeletableModelBase
# Note: created_on is NULLABLE to make migrations easy; in practice it
# should probably never be NULL.
created_on = m.DateTimeField(auto_now_add=True, null=True, editable=False)
deleted_on = m.DateTimeField(blank=True, null=True, editable=False)
objects = DeletableManager()
objects_all = DeletableManager(include_deleted=True)
class Meta:
abstract = True
def mark_deleted(self):
self.deleted_on =
def delete(self):
def delete_soft(self):
def delete_hard(self):
super(DeletableModel, self).delete()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment