Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #3871 -- Custom managers when traversing reverse relations.

  • Loading branch information...
commit 04a2a6b0f9cb6bb98edfe84bf4361216d60a4e38 1 parent 83554b0
Loic Bistuer authored September 19, 2013 akaariai committed September 25, 2013
17  django/contrib/contenttypes/generic.py
@@ -319,6 +319,23 @@ def __init__(self, model=None, instance=None, symmetrical=None,
319 319
                 '%s__exact' % object_id_field_name: instance._get_pk_val(),
320 320
             }
321 321
 
  322
+        def __call__(self, **kwargs):
  323
+            # We use **kwargs rather than a kwarg argument to enforce the
  324
+            # `manager='manager_name'` syntax.
  325
+            manager = getattr(self.model, kwargs.pop('manager'))
  326
+            manager_class = create_generic_related_manager(manager.__class__)
  327
+            return manager_class(
  328
+                model = self.model,
  329
+                instance = self.instance,
  330
+                symmetrical = self.symmetrical,
  331
+                source_col_name = self.source_col_name,
  332
+                target_col_name = self.target_col_name,
  333
+                content_type = self.content_type,
  334
+                content_type_field_name = self.content_type_field_name,
  335
+                object_id_field_name = self.object_id_field_name,
  336
+                prefetch_cache_name = self.prefetch_cache_name,
  337
+            )
  338
+
322 339
         def get_queryset(self):
323 340
             try:
324 341
                 return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
188  django/db/models/fields/related.py
@@ -365,6 +365,92 @@ def __set__(self, instance, value):
@@ -392,86 +478,11 @@ def __set__(self, instance, value):
@@ -513,6 +524,23 @@ def __init__(self, model=None, query_field_name=None, instance=None, symmetrical
6  docs/releases/1.7.txt
@@ -92,6 +92,12 @@ The :meth:`QuerySet.as_manager() <django.db.models.query.QuerySet.as_manager>`
92 92
 class method has been added to :ref:`create Manager with QuerySet methods
93 93
 <create-manager-with-queryset-methods>`.
94 94
 
  95
+Using a custom manager when traversing reverse relations
  96
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  97
+
  98
+It is now possible to :ref:`specify a custom manager
  99
+<using-custom-reverse-manager>` when traversing a reverse relationship.
  100
+
95 101
 Admin shortcuts support time zones
96 102
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97 103
 
25  docs/topics/db/queries.txt
@@ -1136,6 +1136,31 @@ above example code would look like this::
1136 1136
     >>> b.entries.filter(headline__contains='Lennon')
1137 1137
     >>> b.entries.count()
1138 1138
 
  1139
+.. _using-custom-reverse-manager:
  1140
+
  1141
+Using a custom reverse manager
  1142
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1143
+
  1144
+.. versionadded:: 1.7
  1145
+
  1146
+By default the :class:`~django.db.models.fields.related.RelatedManager` used
  1147
+for reverse relations is a subclass of the :ref:`default manager <manager-names>`
  1148
+for that model. If you would like to specify a different manager for a given
  1149
+query you can use the following syntax::
  1150
+
  1151
+    from django.db import models
  1152
+
  1153
+    class Entry(models.Model):
  1154
+        #...
  1155
+        objects = models.Manager() # Default Manager
  1156
+        entries = EntryManager() # Custom Manager
  1157
+
  1158
+    >>> b = Blog.objects.get(id=1)
  1159
+    >>> b.entry_set(manager='entries').all()
  1160
+
  1161
+Additional methods to handle related objects
  1162
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1163
+
1139 1164
 In addition to the :class:`~django.db.models.query.QuerySet` methods defined in
1140 1165
 "Retrieving objects" above, the :class:`~django.db.models.ForeignKey`
1141 1166
 :class:`~django.db.models.Manager` has additional methods used to handle the
20  tests/custom_managers/models.py
@@ -11,6 +11,7 @@
11 11
 
12 12
 from __future__ import unicode_literals
13 13
 
  14
+from django.contrib.contenttypes import generic
14 15
 from django.db import models
15 16
 from django.utils.encoding import python_2_unicode_compatible
16 17
 
@@ -63,12 +64,28 @@ def manager_only(self):
63 64
 
64 65
 CustomManager = BaseCustomManager.from_queryset(CustomQuerySet)
65 66
 
  67
+class FunPeopleManager(models.Manager):
  68
+    def get_queryset(self):
  69
+        return super(FunPeopleManager, self).get_queryset().filter(fun=True)
  70
+
  71
+class BoringPeopleManager(models.Manager):
  72
+    def get_queryset(self):
  73
+        return super(BoringPeopleManager, self).get_queryset().filter(fun=False)
  74
+
66 75
 @python_2_unicode_compatible
67 76
 class Person(models.Model):
68 77
     first_name = models.CharField(max_length=30)
69 78
     last_name = models.CharField(max_length=30)
70 79
     fun = models.BooleanField(default=False)
  80
+
  81
+    favorite_book = models.ForeignKey('Book', null=True, related_name='favorite_books')
  82
+    favorite_thing_type = models.ForeignKey('contenttypes.ContentType', null=True)
  83
+    favorite_thing_id = models.IntegerField(null=True)
  84
+    favorite_thing = generic.GenericForeignKey('favorite_thing_type', 'favorite_thing_id')
  85
+
71 86
     objects = PersonManager()
  87
+    fun_people = FunPeopleManager()
  88
+    boring_people = BoringPeopleManager()
72 89
 
73 90
     custom_queryset_default_manager = CustomQuerySet.as_manager()
74 91
     custom_queryset_custom_manager = CustomManager('hello')
@@ -84,6 +101,9 @@ class Book(models.Model):
84 101
     published_objects = PublishedBookManager()
85 102
     authors = models.ManyToManyField(Person, related_name='books')
86 103
 
  104
+    favorite_things = generic.GenericRelation(Person,
  105
+        content_type_field='favorite_thing_type', object_id_field='favorite_thing_id')
  106
+
87 107
     def __str__(self):
88 108
         return self.title
89 109
 
97  tests/custom_managers/tests.py
@@ -7,10 +7,15 @@
7 7
 
8 8
 
9 9
 class CustomManagerTests(TestCase):
10  
-    def test_manager(self):
11  
-        Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True)
12  
-        p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False)
  10
+    def setUp(self):
  11
+        self.b1 = Book.published_objects.create(
  12
+            title="How to program", author="Rodney Dangerfield", is_published=True)
  13
+        self.b2 = Book.published_objects.create(
  14
+            title="How to be smart", author="Albert Einstein", is_published=False)
  15
+        self.p1 = Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True)
  16
+        self.p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False)
13 17
 
  18
+    def test_manager(self):
14 19
         # Test a custom `Manager` method.
15 20
         self.assertQuerysetEqual(
16 21
             Person.objects.get_fun_people(), [
@@ -61,14 +66,8 @@ def test_manager(self):
61 66
 
62 67
         # The RelatedManager used on the 'books' descriptor extends the default
63 68
         # manager
64  
-        self.assertIsInstance(p2.books, PublishedBookManager)
  69
+        self.assertIsInstance(self.p2.books, PublishedBookManager)
65 70
 
66  
-        Book.published_objects.create(
67  
-            title="How to program", author="Rodney Dangerfield", is_published=True
68  
-        )
69  
-        b2 = Book.published_objects.create(
70  
-            title="How to be smart", author="Albert Einstein", is_published=False
71  
-        )
72 71
 
73 72
         # The default manager, "objects", doesn't exist, because a custom one
74 73
         # was provided.
@@ -76,7 +75,7 @@ def test_manager(self):
76 75
 
77 76
         # The RelatedManager used on the 'authors' descriptor extends the
78 77
         # default manager
79  
-        self.assertIsInstance(b2.authors, PersonManager)
  78
+        self.assertIsInstance(self.b2.authors, PersonManager)
80 79
 
81 80
         self.assertQuerysetEqual(
82 81
             Book.published_objects.all(), [
@@ -114,3 +113,79 @@ def test_manager(self):
114 113
             ],
115 114
             lambda c: c.name
116 115
         )
  116
+
  117
+    def test_related_manager_fk(self):
  118
+        self.p1.favorite_book = self.b1
  119
+        self.p1.save()
  120
+        self.p2.favorite_book = self.b1
  121
+        self.p2.save()
  122
+
  123
+        self.assertQuerysetEqual(
  124
+            self.b1.favorite_books.order_by('first_name').all(), [
  125
+                "Bugs",
  126
+                "Droopy",
  127
+            ],
  128
+            lambda c: c.first_name
  129
+        )
  130
+        self.assertQuerysetEqual(
  131
+            self.b1.favorite_books(manager='boring_people').all(), [
  132
+                "Droopy",
  133
+            ],
  134
+            lambda c: c.first_name
  135
+        )
  136
+        self.assertQuerysetEqual(
  137
+            self.b1.favorite_books(manager='fun_people').all(), [
  138
+                "Bugs",
  139
+            ],
  140
+            lambda c: c.first_name
  141
+        )
  142
+
  143
+    def test_related_manager_gfk(self):
  144
+        self.p1.favorite_thing = self.b1
  145
+        self.p1.save()
  146
+        self.p2.favorite_thing = self.b1
  147
+        self.p2.save()
  148
+
  149
+        self.assertQuerysetEqual(
  150
+            self.b1.favorite_things.order_by('first_name').all(), [
  151
+                "Bugs",
  152
+                "Droopy",
  153
+            ],
  154
+            lambda c: c.first_name
  155
+        )
  156
+        self.assertQuerysetEqual(
  157
+            self.b1.favorite_things(manager='boring_people').all(), [
  158
+                "Droopy",
  159
+            ],
  160
+            lambda c: c.first_name
  161
+        )
  162
+        self.assertQuerysetEqual(
  163
+            self.b1.favorite_things(manager='fun_people').all(), [
  164
+                "Bugs",
  165
+            ],
  166
+            lambda c: c.first_name
  167
+        )
  168
+
  169
+    def test_related_manager_m2m(self):
  170
+        self.b1.authors.add(self.p1)
  171
+        self.b1.authors.add(self.p2)
  172
+
  173
+        self.assertQuerysetEqual(
  174
+            self.b1.authors.order_by('first_name').all(), [
  175
+                "Bugs",
  176
+                "Droopy",
  177
+            ],
  178
+            lambda c: c.first_name
  179
+        )
  180
+        self.assertQuerysetEqual(
  181
+            self.b1.authors(manager='boring_people').all(), [
  182
+                "Droopy",
  183
+            ],
  184
+            lambda c: c.first_name
  185
+        )
  186
+        self.assertQuerysetEqual(
  187
+            self.b1.authors(manager='fun_people').all(), [
  188
+                "Bugs",
  189
+            ],
  190
+            lambda c: c.first_name
  191
+        )

0 notes on commit 04a2a6b

Please sign in to comment.
Something went wrong with that request. Please try again.