diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index eee2ecf..5bf75d7 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -7,6 +7,7 @@ from django.db.models.related import RelatedObject from django.db.models.query import QuerySet from django.db.models.query_utils import QueryWrapper from django.db.models.deletion import CASCADE +from django.db.models.manager import ManagerDescriptor from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _, string_concat from django.utils.functional import curry @@ -386,8 +387,20 @@ class ForeignRelatedObjectsDescriptor(object): if instance is None: return self - return self.create_manager(instance, + def _other_fk_managers(manager): + """ + Selects a manager from the list of the model's managers, useful + in the case of choosing a manager which is not the default, in a reverse relation. + """ + if manager in self.related.model.__dict__ and isinstance(self.related.model.__dict__.get(manager, None), ManagerDescriptor): + return self.create_manager(instance, self.related.model.__dict__.get(manager).manager.__class__) + else: + raise AttributeError("Manager %s does not exist" % manager) + + manager = self.create_manager(instance, self.related.model._default_manager.__class__) + manager.managers = _other_fk_managers + return manager def __set__(self, instance, value): if instance is None: @@ -671,6 +684,7 @@ class ManyRelatedObjectsDescriptor(object): # model's default manager. rel_model = self.related.model superclass = rel_model._default_manager.__class__ + RelatedManager = create_many_related_manager(superclass, self.related.field.rel) manager = RelatedManager( @@ -684,6 +698,17 @@ class ManyRelatedObjectsDescriptor(object): through=self.related.field.rel.through, ) + def _other_m2m_managers(manager): + """ + Selects a manager from the list of the model's managers, useful + in the case of choosing a manager which is not the default, in a reverse relation. + """ + if manager in self.related.model.__dict__ and isinstance(self.related.model.__dict__.get(manager, None), ManagerDescriptor): + return self.create_many_related_manager(self.related.model.__dict__.get(manager).manager.__class__, self.related.field.rel) + else: + raise AttributeError("Manager %s does not exist" % manager) + + manager.managers = _other_m2m_managers return manager def __set__(self, instance, value): diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index 23ed124..26e502f 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1036,6 +1036,14 @@ Each "reverse" operation described in this section has an immediate effect on the database. Every addition, creation and deletion is immediately and automatically saved to the database. +The ``entry_set`` notation, returns the default ``Manager`` defined for the +``Entry`` model, if you want to choose a custom manager, use the ``managers()`` +method of ``entry_set``. For example:: + + b = Blog.objects.get(id=1) + # Selects the custom_manager attribute, defined on Entry + b.entry_set.managers('custom_manager') + .. _m2m-reverse-relationships: Many-to-many relationships @@ -1062,7 +1070,13 @@ An example makes this easier to understand:: Like ``ForeignKey``, ``ManyToManyField`` can specify ``related_name``. In the above example, if the ``ManyToManyField`` in ``Entry`` had specified ``related_name='entries'``, then each ``Author`` instance would have an -``entries`` attribute instead of ``entry_set``. +``entries`` attribute instead of ``entry_set``. The ``entries`` attribute, +also has a ``managers()`` method to select custom managers. For example:: + + b = Author.objects.get(id=5) + # Selects the custom_manager attribute, defined on Entry + b.entries.managers('custom_manager') + One-to-one relationships ------------------------ diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index 1052552..89a665c 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -50,6 +50,7 @@ class FastCarManager(models.Manager): class Car(models.Model): name = models.CharField(max_length=10) + manufacturer = models.ForeignKey('Manufacturer') mileage = models.IntegerField() top_speed = models.IntegerField(help_text="In miles per hour.") cars = models.Manager() @@ -57,3 +58,7 @@ class Car(models.Model): def __unicode__(self): return self.name + +class Manufacturer(models.Model): + name = models.CharField(max_length=10) + country = models.CharField(max_length=20) diff --git a/tests/modeltests/custom_managers/tests.py b/tests/modeltests/custom_managers/tests.py index 8721e9a..1ad41e8 100644 --- a/tests/modeltests/custom_managers/tests.py +++ b/tests/modeltests/custom_managers/tests.py @@ -1,6 +1,6 @@ from django.test import TestCase -from models import Person, Book, Car, PersonManager, PublishedBookManager +from models import Person, Book, Car, Manufacturer, PersonManager, PublishedBookManager class CustomManagerTests(TestCase): @@ -40,13 +40,18 @@ class CustomManagerTests(TestCase): lambda b: b.title ) - c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180) - c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100) + chevy = Manufacturer.objects.create(name="Chevrolet", country="USA") + dodge = Manufacturer.objects.create(name="Dodge", country="USA") + + c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180, manufacturer=chevy) + c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100, manufacturer=dodge) + c3 = Car.cars.create(name="Viper", mileage=14, top_speed=200, manufacturer=dodge) self.assertQuerysetEqual( Car.cars.order_by("name"), [ "Corvette", "Neon", + "Viper" ], lambda c: c.name ) @@ -54,6 +59,7 @@ class CustomManagerTests(TestCase): self.assertQuerysetEqual( Car.fast_cars.all(), [ "Corvette", + "Viper", ], lambda c: c.name ) @@ -66,6 +72,15 @@ class CustomManagerTests(TestCase): Car._default_manager.order_by("name"), [ "Corvette", "Neon", + "Viper", + ], + lambda c: c.name + ) + + #Choosing a custom manager in a reverse relation + self.assertQuerysetEqual( + dodge.car_set.managers('fast_cars').all(), [ + "Viper", ], lambda c: c.name )