summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2015-11-17 00:33:18 -0500
committerSimon Charette <charette.s@gmail.com>2016-01-06 20:00:07 -0500
commit7bb373e3097fe8e000e0bba005ff2dcfc18ab9a5 (patch)
tree86ebceca4da5db2c13a16d8db82ac856540e5690 /django
parentb2cddeaaf448234713f393749ceb1dbe22101f39 (diff)
Refs #25746 -- Added a test utility to isolate inlined model registration.
Thanks to Tim for the review.
Diffstat (limited to 'django')
-rw-r--r--django/db/models/options.py4
-rw-r--r--django/test/utils.py68
2 files changed, 71 insertions, 1 deletions
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 5dd3a815de..2f1f930ec5 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -69,6 +69,8 @@ class Options(object):
'local_concrete_fields', '_forward_fields_map'}
REVERSE_PROPERTIES = {'related_objects', 'fields_map', '_relation_tree'}
+ default_apps = apps
+
def __init__(self, meta, app_label=None):
self._get_fields_cache = {}
self.local_fields = []
@@ -124,7 +126,7 @@ class Options(object):
self.related_fkey_lookups = []
# A custom app registry to use, if you're making a separate model set.
- self.apps = apps
+ self.apps = self.default_apps
self.default_related_name = None
diff --git a/django/test/utils.py b/django/test/utils.py
index 4e27af9004..73f30ef6ca 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -9,10 +9,12 @@ from unittest import skipIf, skipUnless
from xml.dom.minidom import Node, parseString
from django.apps import apps
+from django.apps.registry import Apps
from django.conf import UserSettingsHolder, settings
from django.core import mail
from django.core.signals import request_started
from django.db import reset_queries
+from django.db.models.options import Options
from django.http import request
from django.template import Template
from django.test.signals import setting_changed, template_rendered
@@ -640,3 +642,69 @@ class LoggingCaptureMixin(object):
def tearDown(self):
self.logger.handlers[0].stream = self.old_stream
+
+
+class isolate_apps(object):
+ """
+ Act as either a decorator or a context manager to register models defined
+ in its wrapped context to an isolated registry.
+
+ The list of installed apps the isolated registry should contain must be
+ passed as arguments.
+
+ Two optional keyword arguments can be specified:
+
+ `attr_name`: attribute assigned the isolated registry if used as a class
+ decorator.
+
+ `kwarg_name`: keyword argument passing the isolated registry to the
+ decorated method.
+ """
+
+ def __init__(self, *installed_apps, **kwargs):
+ self.installed_apps = installed_apps
+ self.attr_name = kwargs.pop('attr_name', None)
+ self.kwarg_name = kwargs.pop('kwarg_name', None)
+
+ def enable(self):
+ self.old_apps = Options.default_apps
+ apps = Apps(self.installed_apps)
+ setattr(Options, 'default_apps', apps)
+ return apps
+
+ def disable(self):
+ setattr(Options, 'default_apps', self.old_apps)
+
+ def __enter__(self):
+ return self.enable()
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.disable()
+
+ def __call__(self, decorated):
+ if isinstance(decorated, type):
+ # A class is decorated
+ decorated_setUp = decorated.setUp
+ decorated_tearDown = decorated.tearDown
+
+ def setUp(inner_self):
+ apps = self.enable()
+ if self.attr_name:
+ setattr(inner_self, self.attr_name, apps)
+ decorated_setUp(inner_self)
+
+ def tearDown(inner_self):
+ decorated_tearDown(inner_self)
+ self.disable()
+
+ decorated.setUp = setUp
+ decorated.tearDown = tearDown
+ return decorated
+ else:
+ @wraps(decorated)
+ def inner(*args, **kwargs):
+ with self as apps:
+ if self.kwarg_name:
+ kwargs[self.kwarg_name] = apps
+ return decorated(*args, **kwargs)
+ return inner