Add available_objects soft deletable model manager. Emit deprecation warning when using objects soft deletable manager.

This commit is contained in:
Craig Anderson 2020-08-12 11:25:34 +01:00 committed by Asif Saif Uddin
parent eb00f65c81
commit b7a160936f
6 changed files with 44 additions and 16 deletions

View file

@ -15,6 +15,7 @@
| Bo Marchman <bo.marchman@gmail.com>
| Bojan Mihelac <bmihelac@mihelac.org>
| Bruno Alla <bruno.alla@founders4schools.org.uk>
| Craig Anderson <craiga@craiga.id.au>
| Daniel Andrlik <daniel@andrlik.org>
| Daniel Stanton <stringsonfire@me.com>
| Den Lesnov <den-lesnov@yandex-team.ru>

View file

@ -10,6 +10,8 @@ CHANGES
- `FieldTracker` now respects `update_fields` changed in overridden `save()`
method
- Replace ugettext_lazy with gettext_lazy to satisfy Django deprecation warning
- Add available_objects manager to SoftDeletableModel and add deprecation
warning to objects manager.
4.0.0 (2019-12-11)
------------------

View file

@ -83,9 +83,13 @@ returns objects with that status only:
SoftDeletableModel
------------------
This abstract base class just provides field ``is_removed`` which is
This abstract base class just provides a field ``is_removed`` which is
set to True instead of removing the instance. Entities returned in
default manager are limited to not-deleted instances.
manager ``available_objects`` are limited to not-deleted instances.
Note that relying on the default ``objects`` manager to filter out not-deleted
instances is deprecated. ``objects`` will include deleted objects in a future
release.
UUIDModel

View file

@ -1,3 +1,5 @@
import warnings
import django
from django.core.exceptions import ObjectDoesNotExist
from django.db import connection
@ -282,10 +284,25 @@ class SoftDeletableManagerMixin:
"""
_queryset_class = SoftDeletableQuerySet
def __init__(self, *args, _emit_deprecation_warnings=False, **kwargs):
self.emit_deprecation_warnings = _emit_deprecation_warnings
super().__init__(*args, **kwargs)
def get_queryset(self):
"""
Return queryset limited to not removed entries.
"""
if self.emit_deprecation_warnings:
warning_message = (
"{0}.objects model manager will include soft-deleted objects in an "
"upcoming release; please use {0}.available_objects to continue "
"excluding soft-deleted objects. See "
"https://django-model-utils.readthedocs.io/en/stable/models.html"
"#softdeletablemodel for more information."
).format(self.model.__class__.__name__)
warnings.warn(warning_message, DeprecationWarning)
kwargs = {'model': self.model, 'using': self._db}
if hasattr(self, '_hints'):
kwargs['hints'] = self._hints

View file

@ -37,7 +37,7 @@ class TimeStampedModel(models.Model):
if 'update_fields' in kwargs and 'modified' not in kwargs['update_fields']:
kwargs['update_fields'] += ['modified']
super().save(*args, **kwargs)
class Meta:
abstract = True
@ -132,7 +132,8 @@ class SoftDeletableModel(models.Model):
class Meta:
abstract = True
objects = SoftDeletableManager()
objects = SoftDeletableManager(_emit_deprecation_warnings=True)
available_objects = SoftDeletableManager()
all_objects = models.Manager()
def delete(self, using=None, soft=True, *args, **kwargs):

View file

@ -6,45 +6,48 @@ from tests.models import SoftDeletable
class SoftDeletableModelTests(TestCase):
def test_can_only_see_not_removed_entries(self):
SoftDeletable.objects.create(name='a', is_removed=True)
SoftDeletable.objects.create(name='b', is_removed=False)
SoftDeletable.available_objects.create(name='a', is_removed=True)
SoftDeletable.available_objects.create(name='b', is_removed=False)
queryset = SoftDeletable.objects.all()
queryset = SoftDeletable.available_objects.all()
self.assertEqual(queryset.count(), 1)
self.assertEqual(queryset[0].name, 'b')
def test_instance_cannot_be_fully_deleted(self):
instance = SoftDeletable.objects.create(name='a')
instance = SoftDeletable.available_objects.create(name='a')
instance.delete()
self.assertEqual(SoftDeletable.objects.count(), 0)
self.assertEqual(SoftDeletable.available_objects.count(), 0)
self.assertEqual(SoftDeletable.all_objects.count(), 1)
def test_instance_cannot_be_fully_deleted_via_queryset(self):
SoftDeletable.objects.create(name='a')
SoftDeletable.available_objects.create(name='a')
SoftDeletable.objects.all().delete()
SoftDeletable.available_objects.all().delete()
self.assertEqual(SoftDeletable.objects.count(), 0)
self.assertEqual(SoftDeletable.available_objects.count(), 0)
self.assertEqual(SoftDeletable.all_objects.count(), 1)
def test_delete_instance_no_connection(self):
obj = SoftDeletable.objects.create(name='a')
obj = SoftDeletable.available_objects.create(name='a')
self.assertRaises(ConnectionDoesNotExist, obj.delete, using='other')
def test_instance_purge(self):
instance = SoftDeletable.objects.create(name='a')
instance = SoftDeletable.available_objects.create(name='a')
instance.delete(soft=False)
self.assertEqual(SoftDeletable.objects.count(), 0)
self.assertEqual(SoftDeletable.available_objects.count(), 0)
self.assertEqual(SoftDeletable.all_objects.count(), 0)
def test_instance_purge_no_connection(self):
instance = SoftDeletable.objects.create(name='a')
instance = SoftDeletable.available_objects.create(name='a')
self.assertRaises(ConnectionDoesNotExist, instance.delete,
using='other', soft=False)
def test_deprecation_warning(self):
self.assertWarns(DeprecationWarning, SoftDeletable.objects.all)