Code

Ticket #3871: 3871-patch-with-docs.diff

File 3871-patch-with-docs.diff, 7.0 KB (added by v1v3kn, 3 years ago)

patch with docs

Line 
1diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
2index eee2ecf..5bf75d7 100644
3--- a/django/db/models/fields/related.py
4+++ b/django/db/models/fields/related.py
5@@ -7,6 +7,7 @@ from django.db.models.related import RelatedObject
6 from django.db.models.query import QuerySet
7 from django.db.models.query_utils import QueryWrapper
8 from django.db.models.deletion import CASCADE
9+from django.db.models.manager import ManagerDescriptor
10 from django.utils.encoding import smart_unicode
11 from django.utils.translation import ugettext_lazy as _, string_concat
12 from django.utils.functional import curry
13@@ -386,8 +387,20 @@ class ForeignRelatedObjectsDescriptor(object):
14         if instance is None:
15             return self
16 
17-        return self.create_manager(instance,
18+        def _other_fk_managers(manager):
19+            """
20+            Selects a manager from the list of the model's managers, useful
21+            in the case of choosing a manager which is not the default, in a reverse relation.
22+            """
23+            if manager in self.related.model.__dict__ and isinstance(self.related.model.__dict__.get(manager, None), ManagerDescriptor):
24+                return self.create_manager(instance, self.related.model.__dict__.get(manager).manager.__class__)
25+            else:
26+                raise AttributeError("Manager %s does not exist" % manager)
27+
28+        manager = self.create_manager(instance,
29                 self.related.model._default_manager.__class__)
30+        manager.managers = _other_fk_managers
31+        return manager
32 
33     def __set__(self, instance, value):
34         if instance is None:
35@@ -671,6 +684,7 @@ class ManyRelatedObjectsDescriptor(object):
36         # model's default manager.
37         rel_model = self.related.model
38         superclass = rel_model._default_manager.__class__
39+
40         RelatedManager = create_many_related_manager(superclass, self.related.field.rel)
41 
42         manager = RelatedManager(
43@@ -684,6 +698,17 @@ class ManyRelatedObjectsDescriptor(object):
44             through=self.related.field.rel.through,
45         )
46 
47+        def _other_m2m_managers(manager):
48+            """
49+            Selects a manager from the list of the model's managers, useful
50+            in the case of choosing a manager which is not the default, in a reverse relation.
51+            """
52+            if manager in self.related.model.__dict__ and isinstance(self.related.model.__dict__.get(manager, None), ManagerDescriptor):
53+                return self.create_many_related_manager(self.related.model.__dict__.get(manager).manager.__class__, self.related.field.rel)
54+            else:
55+                raise AttributeError("Manager %s does not exist" % manager)
56+
57+        manager.managers = _other_m2m_managers
58         return manager
59 
60     def __set__(self, instance, value):
61diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
62index 23ed124..26e502f 100644
63--- a/docs/topics/db/queries.txt
64+++ b/docs/topics/db/queries.txt
65@@ -1036,6 +1036,14 @@ Each "reverse" operation described in this section has an immediate effect on
66 the database. Every addition, creation and deletion is immediately and
67 automatically saved to the database.
68 
69+The ``entry_set`` notation, returns the default ``Manager`` defined for the
70+``Entry`` model, if you want to choose a custom manager, use the ``managers()``
71+method of ``entry_set``. For example::
72+
73+    b = Blog.objects.get(id=1)
74+    # Selects the custom_manager attribute, defined on Entry
75+    b.entry_set.managers('custom_manager')
76+
77 .. _m2m-reverse-relationships:
78 
79 Many-to-many relationships
80@@ -1062,7 +1070,13 @@ An example makes this easier to understand::
81 Like ``ForeignKey``, ``ManyToManyField`` can specify ``related_name``. In the
82 above example, if the ``ManyToManyField`` in ``Entry`` had specified
83 ``related_name='entries'``, then each ``Author`` instance would have an
84-``entries`` attribute instead of ``entry_set``.
85+``entries`` attribute instead of ``entry_set``. The ``entries`` attribute,
86+also has a ``managers()`` method to select custom managers. For example::
87+
88+    b = Author.objects.get(id=5)
89+    # Selects the custom_manager attribute, defined on Entry
90+    b.entries.managers('custom_manager')
91+
92 
93 One-to-one relationships
94 ------------------------
95diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py
96index 1052552..89a665c 100644
97--- a/tests/modeltests/custom_managers/models.py
98+++ b/tests/modeltests/custom_managers/models.py
99@@ -50,6 +50,7 @@ class FastCarManager(models.Manager):
100 
101 class Car(models.Model):
102     name = models.CharField(max_length=10)
103+    manufacturer = models.ForeignKey('Manufacturer')
104     mileage = models.IntegerField()
105     top_speed = models.IntegerField(help_text="In miles per hour.")
106     cars = models.Manager()
107@@ -57,3 +58,7 @@ class Car(models.Model):
108 
109     def __unicode__(self):
110         return self.name
111+
112+class Manufacturer(models.Model):
113+    name = models.CharField(max_length=10)
114+    country = models.CharField(max_length=20)
115diff --git a/tests/modeltests/custom_managers/tests.py b/tests/modeltests/custom_managers/tests.py
116index 8721e9a..1ad41e8 100644
117--- a/tests/modeltests/custom_managers/tests.py
118+++ b/tests/modeltests/custom_managers/tests.py
119@@ -1,6 +1,6 @@
120 from django.test import TestCase
121 
122-from models import Person, Book, Car, PersonManager, PublishedBookManager
123+from models import Person, Book, Car, Manufacturer, PersonManager, PublishedBookManager
124 
125 
126 class CustomManagerTests(TestCase):
127@@ -40,13 +40,18 @@ class CustomManagerTests(TestCase):
128             lambda b: b.title
129         )
130 
131-        c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180)
132-        c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100)
133+        chevy = Manufacturer.objects.create(name="Chevrolet", country="USA")
134+        dodge = Manufacturer.objects.create(name="Dodge", country="USA")
135+
136+        c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180, manufacturer=chevy)
137+        c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100, manufacturer=dodge)
138+        c3 = Car.cars.create(name="Viper", mileage=14, top_speed=200, manufacturer=dodge)
139 
140         self.assertQuerysetEqual(
141             Car.cars.order_by("name"), [
142                 "Corvette",
143                 "Neon",
144+                "Viper"
145             ],
146             lambda c: c.name
147         )
148@@ -54,6 +59,7 @@ class CustomManagerTests(TestCase):
149         self.assertQuerysetEqual(
150             Car.fast_cars.all(), [
151                 "Corvette",
152+                "Viper",
153             ],
154             lambda c: c.name
155         )
156@@ -66,6 +72,15 @@ class CustomManagerTests(TestCase):
157             Car._default_manager.order_by("name"), [
158                 "Corvette",
159                 "Neon",
160+                "Viper",
161+            ],
162+            lambda c: c.name
163+        )
164+
165+        #Choosing a custom manager in a reverse relation
166+        self.assertQuerysetEqual(
167+            dodge.car_set.managers('fast_cars').all(), [
168+                "Viper",
169             ],
170             lambda c: c.name
171         )