Last active
April 9, 2019 03:20
-
-
Save arischow/2e21556ad32d2c7126e24096407e4ede to your computer and use it in GitHub Desktop.
[Django] abstract base model
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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