From f4132140f52c88b67d11743d4062a9d455959ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Wed, 23 Jan 2013 08:22:07 +0200 Subject: [PATCH] [1.5.x] Fixed #19652 -- Fixed .none() regression in related fields The regression was caused by using .none() when querying for related models, and the origin field's value was None. This resulted in missing custom related manager subclass as .none() returns plain QuerySet. This isn't backport from master, in master .none() correctly preserves the queryset's class. Patch provided by Simon Charette, with some minor polish by committer. --- django/db/models/fields/related.py | 3 ++- tests/modeltests/custom_managers/models.py | 21 +++++++++++++++++++++ tests/modeltests/custom_managers/tests.py | 13 ++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 2e390c56eb606..ead1d49b57681 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -498,7 +498,8 @@ def get_query_set(self): db = self._db or router.db_for_read(self.model, instance=self.instance) qs = super(RelatedManager, self).get_query_set().using(db).filter(**self.core_filters) if getattr(self.instance, attname) is None: - return qs.none() + # We don't want to use qs.none() here, see #19652 + return qs.filter(pk__in=[]) qs._known_related_objects = {rel_field: {self.instance.pk: self.instance}} return qs diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index de7c1772ed620..f224ec0f6c56e 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -63,3 +63,24 @@ class Car(models.Model): def __str__(self): return self.name + + +# Bug #19652 +class ObjectQuerySet(models.query.QuerySet): + pass + +class ObjectManager(models.Manager): + use_for_related_fields = True + + def get_query_set(self): + return ObjectQuerySet(self.model, using=self._db) + + +class RelatedObject(models.Model): + pass + + +class Object(models.Model): + related = models.ForeignKey(RelatedObject, related_name='objs') + + objects = ObjectManager() diff --git a/tests/modeltests/custom_managers/tests.py b/tests/modeltests/custom_managers/tests.py index 294920de2b7a6..aa2e3e208d479 100644 --- a/tests/modeltests/custom_managers/tests.py +++ b/tests/modeltests/custom_managers/tests.py @@ -3,7 +3,8 @@ from django.test import TestCase from django.utils import six -from .models import Person, Book, Car, PersonManager, PublishedBookManager +from .models import (ObjectQuerySet, RelatedObject, Person, Book, Car, PersonManager, + PublishedBookManager) class CustomManagerTests(TestCase): @@ -72,3 +73,13 @@ def test_manager(self): ], lambda c: c.name ) + + def test_related_manager(self): + """ + Make sure un-saved object's related managers always return an instance + of the same class the manager's `get_query_set` returns. Refs #19652. + """ + rel_qs = RelatedObject().objs.all() + self.assertIsInstance(rel_qs, ObjectQuerySet) + with self.assertNumQueries(0): + self.assertFalse(rel_qs.exists())