Removed the app_config.installed flag.
Since applications that aren't installed no longer have an application configuration, it is now always True in practice. Provided an abstraction to temporarily add or remove applications as several tests messed with app_config.installed to achieve this effect. For now this API is _-prefixed because it looks dangerous.
This commit is contained in:
parent
972babc3b4
commit
9b3389b726
|
@ -1,9 +1,10 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes.views import shortcut
|
from django.contrib.contenttypes.views import shortcut
|
||||||
from django.contrib.sites.models import Site, get_current_site
|
from django.contrib.sites.models import get_current_site
|
||||||
|
from django.core.apps import app_cache
|
||||||
|
from django.db import models
|
||||||
from django.http import HttpRequest, Http404
|
from django.http import HttpRequest, Http404
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
@ -54,11 +55,9 @@ class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
|
||||||
class ContentTypesTests(TestCase):
|
class ContentTypesTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._old_installed = Site._meta.app_config.installed
|
|
||||||
ContentType.objects.clear_cache()
|
ContentType.objects.clear_cache()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
Site._meta.app_config.installed = self._old_installed
|
|
||||||
ContentType.objects.clear_cache()
|
ContentType.objects.clear_cache()
|
||||||
|
|
||||||
def test_lookup_cache(self):
|
def test_lookup_cache(self):
|
||||||
|
@ -223,15 +222,15 @@ class ContentTypesTests(TestCase):
|
||||||
user_ct = ContentType.objects.get_for_model(FooWithUrl)
|
user_ct = ContentType.objects.get_for_model(FooWithUrl)
|
||||||
obj = FooWithUrl.objects.create(name="john")
|
obj = FooWithUrl.objects.create(name="john")
|
||||||
|
|
||||||
Site._meta.app_config.installed = True
|
with app_cache._with_app('django.contrib.sites'):
|
||||||
response = shortcut(request, user_ct.id, obj.id)
|
response = shortcut(request, user_ct.id, obj.id)
|
||||||
self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
|
self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
|
||||||
response._headers.get("location")[1])
|
response._headers.get("location")[1])
|
||||||
|
|
||||||
Site._meta.app_config.installed = False
|
with app_cache._without_app('django.contrib.sites'):
|
||||||
response = shortcut(request, user_ct.id, obj.id)
|
response = shortcut(request, user_ct.id, obj.id)
|
||||||
self.assertEqual("http://Example.com/users/john/",
|
self.assertEqual("http://Example.com/users/john/",
|
||||||
response._headers.get("location")[1])
|
response._headers.get("location")[1])
|
||||||
|
|
||||||
def test_shortcut_view_without_get_absolute_url(self):
|
def test_shortcut_view_without_get_absolute_url(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.conf import settings
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.contrib.gis.geos import HAS_GEOS
|
from django.contrib.gis.geos import HAS_GEOS
|
||||||
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
||||||
|
from django.core.apps import app_cache
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
if HAS_GEOS:
|
if HAS_GEOS:
|
||||||
|
@ -20,11 +21,10 @@ class GeoFeedTest(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
||||||
self._old_installed = Site._meta.app_config.installed
|
self._with_sites = app_cache._begin_with_app('django.contrib.sites')
|
||||||
Site._meta.app_config.installed = True
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
Site._meta.app_config.installed = self._old_installed
|
app_cache._end_with_app(self._with_sites)
|
||||||
|
|
||||||
def assertChildNodes(self, elem, expected):
|
def assertChildNodes(self, elem, expected):
|
||||||
"Taken from syndication/tests.py."
|
"Taken from syndication/tests.py."
|
||||||
|
|
|
@ -10,6 +10,7 @@ from django.conf import settings
|
||||||
from django.contrib.gis.geos import HAS_GEOS
|
from django.contrib.gis.geos import HAS_GEOS
|
||||||
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
from django.core.apps import app_cache
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import IgnoreDeprecationWarningsMixin
|
from django.test.utils import IgnoreDeprecationWarningsMixin
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
|
@ -26,11 +27,10 @@ class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(GeoSitemapTest, self).setUp()
|
super(GeoSitemapTest, self).setUp()
|
||||||
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
||||||
self._old_installed = Site._meta.app_config.installed
|
self._with_sites = app_cache._begin_with_app('django.contrib.sites')
|
||||||
Site._meta.app_config.installed = True
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
Site._meta.app_config.installed = self._old_installed
|
app_cache._end_with_app(self._with_sites)
|
||||||
super(GeoSitemapTest, self).tearDown()
|
super(GeoSitemapTest, self).tearDown()
|
||||||
|
|
||||||
def assertChildNodes(self, elem, expected):
|
def assertChildNodes(self, elem, expected):
|
||||||
|
|
|
@ -25,10 +25,6 @@ class SitemapTestsBase(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.base_url = '%s://%s' % (self.protocol, self.domain)
|
self.base_url = '%s://%s' % (self.protocol, self.domain)
|
||||||
self._old_installed = Site._meta.app_config.installed
|
|
||||||
cache.clear()
|
cache.clear()
|
||||||
# Create an object for sitemap content.
|
# Create an object for sitemap content.
|
||||||
TestModel.objects.create(name='Test Object')
|
TestModel.objects.create(name='Test Object')
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
Site._meta.app_config.installed = self._old_installed
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from unittest import skipUnless
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sitemaps import Sitemap, GenericSitemap
|
from django.contrib.sitemaps import Sitemap, GenericSitemap
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
from django.core.apps import app_cache
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.formats import localize
|
from django.utils.formats import localize
|
||||||
|
@ -108,15 +109,14 @@ class HTTPSitemapTests(SitemapTestsBase):
|
||||||
def test_requestsite_sitemap(self):
|
def test_requestsite_sitemap(self):
|
||||||
# Make sure hitting the flatpages sitemap without the sites framework
|
# Make sure hitting the flatpages sitemap without the sites framework
|
||||||
# installed doesn't raise an exception.
|
# installed doesn't raise an exception.
|
||||||
# Reset by SitemapTestsBase.tearDown().
|
with app_cache._without_app('django.contrib.sites'):
|
||||||
Site._meta.app_config.installed = False
|
response = self.client.get('/simple/sitemap.xml')
|
||||||
response = self.client.get('/simple/sitemap.xml')
|
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
<url><loc>http://testserver/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
|
<url><loc>http://testserver/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
|
||||||
</urlset>
|
</urlset>
|
||||||
""" % date.today()
|
""" % date.today()
|
||||||
self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
|
self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
|
||||||
|
|
||||||
@skipUnless("django.contrib.sites" in settings.INSTALLED_APPS,
|
@skipUnless("django.contrib.sites" in settings.INSTALLED_APPS,
|
||||||
"django.contrib.sites app not installed.")
|
"django.contrib.sites app not installed.")
|
||||||
|
@ -134,9 +134,8 @@ class HTTPSitemapTests(SitemapTestsBase):
|
||||||
Sitemap.get_urls if Site objects exists, but the sites framework is not
|
Sitemap.get_urls if Site objects exists, but the sites framework is not
|
||||||
actually installed.
|
actually installed.
|
||||||
"""
|
"""
|
||||||
# Reset by SitemapTestsBase.tearDown().
|
with app_cache._without_app('django.contrib.sites'):
|
||||||
Site._meta.app_config.installed = False
|
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
|
||||||
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
|
|
||||||
|
|
||||||
def test_sitemap_item(self):
|
def test_sitemap_item(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sites.models import Site, RequestSite, get_current_site
|
from django.contrib.sites.models import Site, RequestSite, get_current_site
|
||||||
|
from django.core.apps import app_cache
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
@ -12,11 +13,10 @@ class SitesFrameworkTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
|
||||||
self._old_installed = Site._meta.app_config.installed
|
self._with_sites = app_cache._begin_with_app('django.contrib.sites')
|
||||||
Site._meta.app_config.installed = True
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
Site._meta.app_config.installed = self._old_installed
|
app_cache._end_with_app(self._with_sites)
|
||||||
|
|
||||||
def test_save_another(self):
|
def test_save_another(self):
|
||||||
# Regression for #17415
|
# Regression for #17415
|
||||||
|
@ -67,10 +67,10 @@ class SitesFrameworkTests(TestCase):
|
||||||
self.assertRaises(ObjectDoesNotExist, get_current_site, request)
|
self.assertRaises(ObjectDoesNotExist, get_current_site, request)
|
||||||
|
|
||||||
# A RequestSite is returned if the sites framework is not installed
|
# A RequestSite is returned if the sites framework is not installed
|
||||||
Site._meta.app_config.installed = False
|
with app_cache._without_app('django.contrib.sites'):
|
||||||
site = get_current_site(request)
|
site = get_current_site(request)
|
||||||
self.assertTrue(isinstance(site, RequestSite))
|
self.assertTrue(isinstance(site, RequestSite))
|
||||||
self.assertEqual(site.name, "example.com")
|
self.assertEqual(site.name, "example.com")
|
||||||
|
|
||||||
def test_domain_name_with_whitespaces(self):
|
def test_domain_name_with_whitespaces(self):
|
||||||
# Regression for #17320
|
# Regression for #17320
|
||||||
|
|
|
@ -30,14 +30,10 @@ class AppConfig(object):
|
||||||
# Populated by calls to AppCache.register_model().
|
# Populated by calls to AppCache.register_model().
|
||||||
self.models = OrderedDict()
|
self.models = OrderedDict()
|
||||||
|
|
||||||
# Whether the app is in INSTALLED_APPS or was automatically created
|
|
||||||
# when one of its models was imported.
|
|
||||||
self.installed = app_module is not None
|
|
||||||
|
|
||||||
# Filesystem path to the application directory eg.
|
# Filesystem path to the application directory eg.
|
||||||
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'.
|
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'.
|
||||||
# This is a unicode object on Python 2 and a str on Python 3.
|
# This is a unicode object on Python 2 and a str on Python 3.
|
||||||
self.path = upath(app_module.__path__[0]) if app_module is not None else None
|
self.path = upath(app_module.__path__[0])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<AppConfig: %s>' % self.label
|
return '<AppConfig: %s>' % self.label
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"Utilities for loading models and the modules that contain them."
|
"Utilities for loading models and the modules that contain them."
|
||||||
|
|
||||||
from collections import defaultdict, OrderedDict
|
from collections import defaultdict, OrderedDict
|
||||||
|
from contextlib import contextmanager
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -120,8 +121,7 @@ class AppCache(object):
|
||||||
finally:
|
finally:
|
||||||
self.nesting_level -= 1
|
self.nesting_level -= 1
|
||||||
|
|
||||||
app_config = AppConfig(
|
app_config = AppConfig(app_name, app_module, models_module)
|
||||||
name=app_name, app_module=app_module, models_module=models_module)
|
|
||||||
app_config.models = self.all_models[app_config.label]
|
app_config.models = self.all_models[app_config.label]
|
||||||
self.app_configs[app_config.label] = app_config
|
self.app_configs[app_config.label] = app_config
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ class AppCache(object):
|
||||||
self.populate()
|
self.populate()
|
||||||
if only_installed:
|
if only_installed:
|
||||||
app_config = self.app_configs.get(app_label)
|
app_config = self.app_configs.get(app_label)
|
||||||
if app_config is None or not app_config.installed:
|
if app_config is None:
|
||||||
return None
|
return None
|
||||||
if (self.available_apps is not None
|
if (self.available_apps is not None
|
||||||
and app_config.name not in self.available_apps):
|
and app_config.name not in self.available_apps):
|
||||||
|
@ -304,6 +304,63 @@ class AppCache(object):
|
||||||
def unset_available_apps(self):
|
def unset_available_apps(self):
|
||||||
self.available_apps = None
|
self.available_apps = None
|
||||||
|
|
||||||
|
### DANGEROUS METHODS ### (only used to preserve existing tests)
|
||||||
|
|
||||||
|
def _begin_with_app(self, app_name):
|
||||||
|
# Returns an opaque value that can be passed to _end_with_app().
|
||||||
|
app_module = import_module(app_name)
|
||||||
|
models_module = import_module('%s.models' % app_name)
|
||||||
|
app_config = AppConfig(app_name, app_module, models_module)
|
||||||
|
if app_config.label in self.app_configs:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
app_config.models = self.all_models[app_config.label]
|
||||||
|
self.app_configs[app_config.label] = app_config
|
||||||
|
return app_config
|
||||||
|
|
||||||
|
def _end_with_app(self, app_config):
|
||||||
|
if app_config is not None:
|
||||||
|
del self.app_configs[app_config.label]
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _with_app(self, app_name):
|
||||||
|
app_config = self._begin_with_app(app_name)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._end_with_app(app_config)
|
||||||
|
|
||||||
|
def _begin_without_app(self, app_name):
|
||||||
|
# Returns an opaque value that can be passed to _end_without_app().
|
||||||
|
return self.app_configs.pop(app_name.rpartition(".")[2], None)
|
||||||
|
|
||||||
|
def _end_without_app(self, app_config):
|
||||||
|
if app_config is not None:
|
||||||
|
self.app_configs[app_config.label] = app_config
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _without_app(self, app_name):
|
||||||
|
app_config = self._begin_without_app(app_name)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._end_without_app(app_config)
|
||||||
|
|
||||||
|
def _begin_empty(self):
|
||||||
|
app_configs, self.app_configs = self.app_configs, OrderedDict()
|
||||||
|
return app_configs
|
||||||
|
|
||||||
|
def _end_empty(self, app_configs):
|
||||||
|
self.app_configs = app_configs
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _empty(self):
|
||||||
|
app_configs = self._begin_empty()
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self._end_empty(app_configs)
|
||||||
|
|
||||||
### DEPRECATED METHODS GO BELOW THIS LINE ###
|
### DEPRECATED METHODS GO BELOW THIS LINE ###
|
||||||
|
|
||||||
def get_app(self, app_label):
|
def get_app(self, app_label):
|
||||||
|
|
|
@ -94,11 +94,11 @@ class Options(object):
|
||||||
@property
|
@property
|
||||||
def app_config(self):
|
def app_config(self):
|
||||||
# Don't go through get_app_config to avoid triggering populate().
|
# Don't go through get_app_config to avoid triggering populate().
|
||||||
return self.app_cache.app_configs[self.app_label]
|
return self.app_cache.app_configs.get(self.app_label)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def installed(self):
|
def installed(self):
|
||||||
return self.app_config.installed
|
return self.app_config is not None
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.conf import settings
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.contrib.admindocs import utils
|
from django.contrib.admindocs import utils
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.apps import app_cache
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
@ -13,27 +14,18 @@ class MiscTests(TestCase):
|
||||||
urls = 'admin_docs.urls'
|
urls = 'admin_docs.urls'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._old_installed = Site._meta.app_config.installed
|
|
||||||
User.objects.create_superuser('super', None, 'secret')
|
User.objects.create_superuser('super', None, 'secret')
|
||||||
self.client.login(username='super', password='secret')
|
self.client.login(username='super', password='secret')
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
Site._meta.app_config.installed = self._old_installed
|
|
||||||
|
|
||||||
@override_settings(
|
|
||||||
SITE_ID=None,
|
|
||||||
INSTALLED_APPS=[app for app in settings.INSTALLED_APPS
|
|
||||||
if app != 'django.contrib.sites'],
|
|
||||||
)
|
|
||||||
def test_no_sites_framework(self):
|
def test_no_sites_framework(self):
|
||||||
"""
|
"""
|
||||||
Without the sites framework, should not access SITE_ID or Site
|
Without the sites framework, should not access SITE_ID or Site
|
||||||
objects. Deleting settings is fine here as UserSettingsHolder is used.
|
objects. Deleting settings is fine here as UserSettingsHolder is used.
|
||||||
"""
|
"""
|
||||||
Site._meta.app_config.installed = False
|
with self.settings(SITE_ID=None), app_cache._without_app('django.contrib.sites'):
|
||||||
Site.objects.all().delete()
|
Site.objects.all().delete()
|
||||||
del settings.SITE_ID
|
del settings.SITE_ID
|
||||||
self.client.get('/admindocs/views/') # should not raise
|
self.client.get('/admindocs/views/') # should not raise
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
|
Loading…
Reference in New Issue