[1.10.x] Fixed #26749 -- Preserved behavior of use_for_related_field during deprecation.

Backport of f4afb85d7e from master
This commit is contained in:
Julien Hartmann 2016-06-23 17:10:52 +07:00 committed by Tim Graham
parent 4b6560f5a9
commit 39c25b772b
3 changed files with 102 additions and 7 deletions

View File

@ -106,8 +106,7 @@ class ForwardManyToOneDescriptor(object):
def get_queryset(self, **hints):
related_model = self.field.remote_field.model
if (not related_model._meta.base_manager_name and
getattr(related_model._default_manager, 'use_for_related_fields', False)):
if getattr(related_model._default_manager, 'use_for_related_fields', False):
if not getattr(related_model._default_manager, 'silence_use_for_related_fields_deprecation', False):
warnings.warn(
"use_for_related_fields is deprecated, instead "
@ -292,8 +291,7 @@ class ReverseOneToOneDescriptor(object):
def get_queryset(self, **hints):
related_model = self.related.related_model
if (not related_model._meta.base_manager_name and
getattr(related_model._default_manager, 'use_for_related_fields', False)):
if getattr(related_model._default_manager, 'use_for_related_fields', False):
if not getattr(related_model._default_manager, 'silence_use_for_related_fields_deprecation', False):
warnings.warn(
"use_for_related_fields is deprecated, instead "

View File

@ -1091,6 +1091,12 @@ class to override the manager from the concrete model, or you'll set the
model's ``Meta.manager_inheritance_from_future=True`` option to opt-in to the
new inheritance behavior.
During the deprecation period, ``use_for_related_fields`` will be honored and
raise a warning, even if a ``base_manager_name`` is set. This allows
third-party code to preserve legacy behavior while transitioning to the new
API. The warning can be silenced by setting
``silence_use_for_related_fields_deprecation=True`` on the manager.
Miscellaneous
-------------

View File

@ -334,11 +334,26 @@ class TestManagerDeprecations(TestCase):
self.assertEqual(len(warns), 0)
def test_use_for_related_fields_for_many_to_one(self):
# Common objects
class MyManagerQuerySet(models.QuerySet):
pass
class MyLegacyManagerQuerySet(models.QuerySet):
pass
class MyManager(models.Manager):
def get_queryset(self):
return MyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
class MyLegacyManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return MyLegacyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
# With legacy config there should be a deprecation warning
class MyRelModel(models.Model):
objects = MyManager()
objects = MyLegacyManager()
class MyModel(models.Model):
fk = models.ForeignKey(MyRelModel, on_delete=models.DO_NOTHING)
@ -375,16 +390,61 @@ class TestManagerDeprecations(TestCase):
pass
self.assertEqual(len(warns), 0)
# When mixing base_manager_name and use_for_related_fields, there
# should be warnings.
class MyRelModel3(models.Model):
my_base_manager = MyManager()
my_default_manager = MyLegacyManager()
class Meta:
base_manager_name = 'my_base_manager'
default_manager_name = 'my_default_manager'
class MyModel3(models.Model):
fk = models.ForeignKey(MyRelModel3, on_delete=models.DO_NOTHING)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always', RemovedInDjango20Warning)
try:
MyModel3(fk_id=42).fk
except DatabaseError:
pass
self.assertEqual(len(warns), 1)
self.assertEqual(
str(warns[0].message),
"use_for_related_fields is deprecated, "
"instead set Meta.base_manager_name on "
"'managers_regress.MyRelModel3'.",
)
with warnings.catch_warnings(record=True):
warnings.simplefilter('always', RemovedInDjango20Warning)
self.assertIsInstance(MyModel3.fk.get_queryset(), MyLegacyManagerQuerySet)
def test_use_for_related_fields_for_one_to_one(self):
# Common objects
class MyManagerQuerySet(models.QuerySet):
pass
class MyLegacyManagerQuerySet(models.QuerySet):
pass
class MyManager(models.Manager):
def get_queryset(self):
return MyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
class MyLegacyManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return MyLegacyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
# With legacy config there should be a deprecation warning
class MyRelModel(models.Model):
objects = MyManager()
objects = MyLegacyManager()
class MyModel(models.Model):
o2o = models.OneToOneField(MyRelModel, on_delete=models.DO_NOTHING)
objects = MyManager()
objects = MyLegacyManager()
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always', RemovedInDjango20Warning)
@ -440,6 +500,37 @@ class TestManagerDeprecations(TestCase):
pass
self.assertEqual(len(warns), 0)
# When mixing base_manager_name and use_for_related_fields, there
# should be warnings.
class MyRelModel3(models.Model):
my_base_manager = MyManager()
my_default_manager = MyLegacyManager()
class Meta:
base_manager_name = 'my_base_manager'
default_manager_name = 'my_default_manager'
class MyModel3(models.Model):
o2o = models.OneToOneField(MyRelModel3, on_delete=models.DO_NOTHING)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always', RemovedInDjango20Warning)
try:
MyModel3(o2o_id=42).o2o
except DatabaseError:
pass
self.assertEqual(len(warns), 1)
self.assertEqual(
str(warns[0].message),
"use_for_related_fields is deprecated, "
"instead set Meta.base_manager_name on "
"'managers_regress.MyRelModel3'.",
)
with warnings.catch_warnings(record=True):
warnings.simplefilter('always', RemovedInDjango20Warning)
self.assertIsInstance(MyModel3.o2o.get_queryset(), MyLegacyManagerQuerySet)
def test_legacy_objects_is_created(self):
class ConcreteParentWithoutManager(models.Model):
pass