From 39c25b772b566f77e94119f880fc6f99d563fcb6 Mon Sep 17 00:00:00 2001
From: Julien Hartmann <julien@etherdream.org>
Date: Thu, 23 Jun 2016 17:10:52 +0700
Subject: [PATCH] [1.10.x] Fixed #26749 -- Preserved behavior of
 use_for_related_field during deprecation.

Backport of f4afb85d7e900123fa8f88110adc011ab184e153 from master
---
 .../db/models/fields/related_descriptors.py   |  6 +-
 docs/releases/1.10.txt                        |  6 ++
 tests/managers_regress/tests.py               | 97 ++++++++++++++++++-
 3 files changed, 102 insertions(+), 7 deletions(-)

diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index 9f904a26b4..ea80a1cdd7 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -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 "
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index c56ac6efc6..b7b2813b31 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -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
 -------------
 
diff --git a/tests/managers_regress/tests.py b/tests/managers_regress/tests.py
index 7c1223a5f1..feaca1cba0 100644
--- a/tests/managers_regress/tests.py
+++ b/tests/managers_regress/tests.py
@@ -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