Skip to content

Instantly share code, notes, and snippets.

@arischow
Last active April 9, 2019 03:20
Show Gist options
  • Save arischow/2e21556ad32d2c7126e24096407e4ede to your computer and use it in GitHub Desktop.
Save arischow/2e21556ad32d2c7126e24096407e4ede to your computer and use it in GitHub Desktop.
[Django] abstract base model
# Based on https://medium.com/@adriennedomingus/soft-deletion-in-django-e4882581c340
# There’s one other thing that might be interesting to point out about the implementation of the soft-deletion: the unique together constraint.
# If you have a model with a unique together rule for fields ‘a’ and ‘b’, when an entry with the values ‘a=1’ and ‘b=2’ is soft-deleted, another one could not be created with the same values for those fields. So the ‘deleted_at’ has to be included on the constraint.
from django.db import models
from django.utils import timezone
class HasDeletedError(Exception):
pass
class SoftDeletionManager(models.Manager):
def __init__(self, *args, **kwargs):
self.alive_only = kwargs.pop('alive_only', True)
super(SoftDeletionManager, self).__init__(*args, **kwargs)
def get_queryset(self):
if self.alive_only:
return SoftDeletionQuerySet(self.model).filter(deleted_at=None)
return SoftDeletionQuerySet(self.model)
def hard_delete(self):
return self.get_queryset().hard_delete()
class SoftDeletionQuerySet(models.QuerySet):
def delete(self):
return super(SoftDeletionQuerySet, self).update(deleted_at=timezone.now())
def hard_delete(self):
return super(SoftDeletionQuerySet, self).delete()
def alive(self):
return self.filter(deleted_at=None)
def dead(self):
return self.exclude(deleted_at=None)
class AbstractBaseModel(models.Model):
class Meta:
abstract = True
created_at = models.DateTimeField(auto_now_add=True)
class AbstractSoftDeletionBaseModel(AbstractBaseModel):
class Meta:
abstract = True
deleted_at = models.DateTimeField(null=True)
objects = SoftDeletionManager()
all_objects = SoftDeletionManager(alive_only=False)
def delete(self, *args, **kwargs):
if not self.deleted_at:
self.deleted_at = timezone.now()
super().save(*args, **kwargs)
else:
raise HasDeletedError
def hard_delete(self, *args, **kwargs):
return super().delete(*args, **kwargs)
from django.db import connection
class QueryCountDebugMiddleware(object):
"""
This middleware will log the number of queries run
and the total time taken for each request (with a
status code of 200). It does not currently support
multi-db setups.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if response.status_code == 200:
total_time = 0
for query in connection.queries:
query_time = query.get('time')
if query_time is None:
# django-debug-toolbar monkeypatches the connection
# cursor wrapper and adds extra information in each
# item in connection.queries. The query time is stored
# under the key "duration" rather than "time" and is
# in milliseconds, not seconds.
query_time = query.get('duration', 0) / 1000
total_time += float(query_time)
logger.debug('%s queries run, total %s seconds' % (len(connection.queries), total_time))
return response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment