diff --git a/tests/custom_managers/models.py b/tests/custom_managers/models.py index bbf037b362..feda9ef630 100644 --- a/tests/custom_managers/models.py +++ b/tests/custom_managers/models.py @@ -11,28 +11,20 @@ returns. from __future__ import unicode_literals -from django.contrib.contenttypes.fields import ( - GenericForeignKey, GenericRelation -) +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.db import models from django.utils.encoding import python_2_unicode_compatible -# An example of a custom manager called "objects". - class PersonManager(models.Manager): def get_fun_people(self): return self.filter(fun=True) -# An example of a custom manager that sets get_queryset(). - class PublishedBookManager(models.Manager): def get_queryset(self): return super(PublishedBookManager, self).get_queryset().filter(is_published=True) -# An example of a custom queryset that copies its methods onto the manager. - class CustomQuerySet(models.QuerySet): def filter(self, *args, **kwargs): @@ -138,8 +130,6 @@ class Book(models.Model): def __str__(self): return self.title -# An example of providing multiple custom managers. - class FastCarManager(models.Manager): def get_queryset(self): @@ -156,3 +146,42 @@ class Car(models.Model): def __str__(self): return self.name + + +class RestrictedManager(models.Manager): + def get_queryset(self): + return super(RestrictedManager, self).get_queryset().filter(is_public=True) + + +@python_2_unicode_compatible +class RelatedModel(models.Model): + name = models.CharField(max_length=50) + + def __str__(self): + return self.name + + +@python_2_unicode_compatible +class RestrictedModel(models.Model): + name = models.CharField(max_length=50) + is_public = models.BooleanField(default=False) + related = models.ForeignKey(RelatedModel) + + objects = RestrictedManager() + plain_manager = models.Manager() + + def __str__(self): + return self.name + + +@python_2_unicode_compatible +class OneToOneRestrictedModel(models.Model): + name = models.CharField(max_length=50) + is_public = models.BooleanField(default=False) + related = models.OneToOneField(RelatedModel) + + objects = RestrictedManager() + plain_manager = models.Manager() + + def __str__(self): + return self.name diff --git a/tests/custom_managers/tests.py b/tests/custom_managers/tests.py index 437d75deea..383ccc0e44 100644 --- a/tests/custom_managers/tests.py +++ b/tests/custom_managers/tests.py @@ -3,7 +3,8 @@ from __future__ import unicode_literals from django.test import TestCase from django.utils import six -from .models import Person, FunPerson, Book, Car, PersonManager, PublishedBookManager +from .models import (Book, Car, FunPerson, OneToOneRestrictedModel, Person, + PersonManager, PublishedBookManager, RelatedModel, RestrictedModel) class CustomManagerTests(TestCase): @@ -463,3 +464,47 @@ class CustomManagerTests(TestCase): lambda c: c.first_name, ordered=False, ) + + +class CustomManagersRegressTestCase(TestCase): + def test_filtered_default_manager(self): + """Even though the default manager filters out some records, + we must still be able to save (particularly, save by updating + existing records) those filtered instances. This is a + regression test for #8990, #9527""" + related = RelatedModel.objects.create(name="xyzzy") + obj = RestrictedModel.objects.create(name="hidden", related=related) + obj.name = "still hidden" + obj.save() + + # If the hidden object wasn't seen during the save process, + # there would now be two objects in the database. + self.assertEqual(RestrictedModel.plain_manager.count(), 1) + + def test_delete_related_on_filtered_manager(self): + """Deleting related objects should also not be distracted by a + restricted manager on the related object. This is a regression + test for #2698.""" + related = RelatedModel.objects.create(name="xyzzy") + + for name, public in (('one', True), ('two', False), ('three', False)): + RestrictedModel.objects.create(name=name, is_public=public, related=related) + + obj = RelatedModel.objects.get(name="xyzzy") + obj.delete() + + # All of the RestrictedModel instances should have been + # deleted, since they *all* pointed to the RelatedModel. If + # the default manager is used, only the public one will be + # deleted. + self.assertEqual(len(RestrictedModel.plain_manager.all()), 0) + + def test_delete_one_to_one_manager(self): + # The same test case as the last one, but for one-to-one + # models, which are implemented slightly different internally, + # so it's a different code path. + obj = RelatedModel.objects.create(name="xyzzy") + OneToOneRestrictedModel.objects.create(name="foo", is_public=False, related=obj) + obj = RelatedModel.objects.get(name="xyzzy") + obj.delete() + self.assertEqual(len(OneToOneRestrictedModel.plain_manager.all()), 0) diff --git a/tests/custom_managers_regress/__init__.py b/tests/custom_managers_regress/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/custom_managers_regress/models.py b/tests/custom_managers_regress/models.py deleted file mode 100644 index eda711f8d7..0000000000 --- a/tests/custom_managers_regress/models.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Regression tests for custom manager classes. -""" - -from django.db import models -from django.utils.encoding import python_2_unicode_compatible - - -class RestrictedManager(models.Manager): - """ - A manager that filters out non-public instances. - """ - def get_queryset(self): - return super(RestrictedManager, self).get_queryset().filter(is_public=True) - - -@python_2_unicode_compatible -class RelatedModel(models.Model): - name = models.CharField(max_length=50) - - def __str__(self): - return self.name - - -@python_2_unicode_compatible -class RestrictedModel(models.Model): - name = models.CharField(max_length=50) - is_public = models.BooleanField(default=False) - related = models.ForeignKey(RelatedModel) - - objects = RestrictedManager() - plain_manager = models.Manager() - - def __str__(self): - return self.name - - -@python_2_unicode_compatible -class OneToOneRestrictedModel(models.Model): - name = models.CharField(max_length=50) - is_public = models.BooleanField(default=False) - related = models.OneToOneField(RelatedModel) - - objects = RestrictedManager() - plain_manager = models.Manager() - - def __str__(self): - return self.name diff --git a/tests/custom_managers_regress/tests.py b/tests/custom_managers_regress/tests.py deleted file mode 100644 index 1737c52926..0000000000 --- a/tests/custom_managers_regress/tests.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import unicode_literals - -from django.test import TestCase - -from .models import RelatedModel, RestrictedModel, OneToOneRestrictedModel - - -class CustomManagersRegressTestCase(TestCase): - def test_filtered_default_manager(self): - """Even though the default manager filters out some records, - we must still be able to save (particularly, save by updating - existing records) those filtered instances. This is a - regression test for #8990, #9527""" - related = RelatedModel.objects.create(name="xyzzy") - obj = RestrictedModel.objects.create(name="hidden", related=related) - obj.name = "still hidden" - obj.save() - - # If the hidden object wasn't seen during the save process, - # there would now be two objects in the database. - self.assertEqual(RestrictedModel.plain_manager.count(), 1) - - def test_delete_related_on_filtered_manager(self): - """Deleting related objects should also not be distracted by a - restricted manager on the related object. This is a regression - test for #2698.""" - related = RelatedModel.objects.create(name="xyzzy") - - for name, public in (('one', True), ('two', False), ('three', False)): - RestrictedModel.objects.create(name=name, is_public=public, related=related) - - obj = RelatedModel.objects.get(name="xyzzy") - obj.delete() - - # All of the RestrictedModel instances should have been - # deleted, since they *all* pointed to the RelatedModel. If - # the default manager is used, only the public one will be - # deleted. - self.assertEqual(len(RestrictedModel.plain_manager.all()), 0) - - def test_delete_one_to_one_manager(self): - # The same test case as the last one, but for one-to-one - # models, which are implemented slightly different internally, - # so it's a different code path. - obj = RelatedModel.objects.create(name="xyzzy") - OneToOneRestrictedModel.objects.create(name="foo", is_public=False, related=obj) - obj = RelatedModel.objects.get(name="xyzzy") - obj.delete() - self.assertEqual(len(OneToOneRestrictedModel.plain_manager.all()), 0)