summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2015-10-19 14:17:55 -0400
committerSimon Charette <charette.s@gmail.com>2015-10-19 20:13:16 -0400
commit522b0bc91f6eddfcc8b8cc0f3d3566983f5bdfc2 (patch)
treecb6f02d1228f90c00ce36369d793bc5e16407b8f
parent094a60847a3e480fe694ce6c9f33832168987e90 (diff)
[1.9.x] Fixed #25563 -- Cached deferred models in their proxied model's _meta.apps.
Thanks to Andriy Sokolovskiy for the report and Tim Graham for the review. Backport of 3db3ab71e97d34260057a6f51d4b2f72da30dc8d from master
-rw-r--r--django/db/models/query_utils.py9
-rw-r--r--docs/releases/1.8.6.txt9
-rw-r--r--tests/defer_regress/tests.py20
3 files changed, 34 insertions, 4 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index efc995b35f..b25b408230 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -10,7 +10,6 @@ from __future__ import unicode_literals
import inspect
from collections import namedtuple
-from django.apps import apps
from django.core.exceptions import FieldDoesNotExist
from django.db.backends import utils
from django.db.models.constants import LOOKUP_SEP
@@ -272,12 +271,13 @@ def deferred_class_factory(model, attrs):
"""
if not attrs:
return model
+ opts = model._meta
# Never create deferred models based on deferred model
if model._deferred:
# Deferred models are proxies for the non-deferred model. We never
# create chains of defers => proxy_for_model is the non-deferred
# model.
- model = model._meta.proxy_for_model
+ model = opts.proxy_for_model
# The app registry wants a unique name for each model, otherwise the new
# class won't be created (we get an exception). Therefore, we generate
# the name using the passed in attrs. It's OK to reuse an existing class
@@ -286,13 +286,14 @@ def deferred_class_factory(model, attrs):
name = utils.truncate_name(name, 80, 32)
try:
- return apps.get_model(model._meta.app_label, name)
+ return opts.apps.get_model(model._meta.app_label, name)
except LookupError:
class Meta:
proxy = True
- app_label = model._meta.app_label
+ apps = opts.apps
+ app_label = opts.app_label
overrides = {attr: DeferredAttribute(attr, model) for attr in attrs}
overrides["Meta"] = Meta
diff --git a/docs/releases/1.8.6.txt b/docs/releases/1.8.6.txt
index 178d714f01..1c2af01c7d 100644
--- a/docs/releases/1.8.6.txt
+++ b/docs/releases/1.8.6.txt
@@ -25,3 +25,12 @@ Bugfixes
* Allowed filtering over a ``RawSQL`` annotation (:ticket:`25506`).
* Made the ``Concat`` database function idempotent on SQLite (:ticket:`25517`).
+
+* Avoided a confusing stack trace when starting :djadmin:`runserver` with an
+ invalid :setting:`INSTALLED_APPS` setting (:ticket:`25510`). This regression
+ appeared in 1.8.5 as a side effect of fixing :ticket:`24704`.
+
+* Made deferred models use their proxied model's ``_meta.apps`` for caching
+ and retrieval (:ticket:`25563`). This prevents any models generated in data
+ migrations using ``QuerySet.defer()`` from leaking to test and application
+ code.
diff --git a/tests/defer_regress/tests.py b/tests/defer_regress/tests.py
index d8c6e01884..cc1658ce58 100644
--- a/tests/defer_regress/tests.py
+++ b/tests/defer_regress/tests.py
@@ -3,8 +3,10 @@ from __future__ import unicode_literals
from operator import attrgetter
from django.apps import apps
+from django.apps.registry import Apps
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.backends.db import SessionStore
+from django.db import models
from django.db.models import Count
from django.db.models.query_utils import (
DeferredAttribute, deferred_class_factory,
@@ -263,6 +265,24 @@ class DeferRegressionTest(TestCase):
deferred_cls = deferred_class_factory(Item, ())
self.assertFalse(deferred_cls._deferred)
+ def test_deferred_class_factory_apps_reuse(self):
+ """
+ #25563 - model._meta.apps should be used for caching and
+ retrieval of the created proxy class.
+ """
+ isolated_apps = Apps(['defer_regress'])
+
+ class BaseModel(models.Model):
+ field = models.BooleanField()
+
+ class Meta:
+ apps = isolated_apps
+ app_label = 'defer_regress'
+
+ deferred_model = deferred_class_factory(BaseModel, ['field'])
+ self.assertIs(deferred_model._meta.apps, isolated_apps)
+ self.assertIs(deferred_class_factory(BaseModel, ['field']), deferred_model)
+
class DeferAnnotateSelectRelatedTest(TestCase):
def test_defer_annotate_select_related(self):