diff --git a/horizon/base.py b/horizon/base.py index 4b57e9bb1a..ab4f5e1ba4 100644 --- a/horizon/base.py +++ b/horizon/base.py @@ -26,6 +26,7 @@ import inspect import logging import os +import django from django.conf import settings from django.conf.urls import include from django.conf.urls import url @@ -54,7 +55,13 @@ LOG = logging.getLogger(__name__) def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs): for pattern in urlpatterns: if getattr(pattern, 'callback', None): - pattern._callback = decorator(pattern.callback, *args, **kwargs) + decorated = decorator(pattern.callback, *args, **kwargs) + if django.VERSION >= (1, 10): + pattern.callback = decorated + else: + # prior to 1.10 callback was a property and we had + # to modify the private attribute behind the property + pattern._callback = decorated if getattr(pattern, 'url_patterns', []): _decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs) diff --git a/horizon/templatetags/breadcrumb_nav.py b/horizon/templatetags/breadcrumb_nav.py index f2cacb389b..ef05723015 100644 --- a/horizon/templatetags/breadcrumb_nav.py +++ b/horizon/templatetags/breadcrumb_nav.py @@ -48,13 +48,13 @@ def breadcrumb_nav(context): custom_breadcrumb = context.get('custom_breadcrumb') # Build list of tuples (name, optional url) - breadcrumb.append((dashboard.name,)) + breadcrumb.append((dashboard.name, None)) if panel_group: - breadcrumb.append((panel_group.name,)) + breadcrumb.append((panel_group.name, None)) if panel: breadcrumb.append((panel.name, panel.get_absolute_url())) if custom_breadcrumb: breadcrumb.extend(custom_breadcrumb) - breadcrumb.append((context.get('page_title'),)) + breadcrumb.append((context.get('page_title'), None)) return {'breadcrumb': breadcrumb} diff --git a/horizon/test/settings.py b/horizon/test/settings.py index d275bc90c4..e261343ca8 100644 --- a/horizon/test/settings.py +++ b/horizon/test/settings.py @@ -31,7 +31,6 @@ ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static')) DEBUG = False -TEMPLATE_DEBUG = DEBUG TESTSERVER = 'http://testserver' SECRET_KEY = 'elj1IWiLoWHgcyYxFVLj7cM5rGOOxWl0' @@ -73,20 +72,28 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.request', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'django.contrib.messages.context_processors.messages', - 'horizon.context_processors.horizon') - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'horizon.loaders.TemplateLoader' -) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(ROOT_PATH, 'tests', 'templates')], + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.contrib.messages.context_processors.messages', + 'horizon.context_processors.horizon', + ], + 'loaders': [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + 'horizon.loaders.TemplateLoader' + ], + }, + }, +] STATIC_URL = '/static/' WEBROOT = '/' @@ -94,7 +101,6 @@ WEBROOT = '/' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' ROOT_URLCONF = 'horizon.test.urls' -TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'tests', 'templates'),) SITE_ID = 1 SITE_BRANDING = 'Horizon' diff --git a/openstack_dashboard/dashboards/admin/hypervisors/views.py b/openstack_dashboard/dashboards/admin/hypervisors/views.py index 55c210c885..5e770f6e27 100644 --- a/openstack_dashboard/dashboards/admin/hypervisors/views.py +++ b/openstack_dashboard/dashboards/admin/hypervisors/views.py @@ -67,7 +67,6 @@ class AdminDetailView(tables.DataTableView): def get_context_data(self, **kwargs): context = super(AdminDetailView, self).get_context_data(**kwargs) hypervisor_name = self.kwargs['hypervisor'].split('_', 1)[1] - breadcrumb = [ - (hypervisor_name,), ] + breadcrumb = [(hypervisor_name, None)] context['custom_breadcrumb'] = breadcrumb return context diff --git a/openstack_dashboard/dashboards/admin/networks/ports/views.py b/openstack_dashboard/dashboards/admin/networks/ports/views.py index 30b3aae80c..a19e048838 100644 --- a/openstack_dashboard/dashboards/admin/networks/ports/views.py +++ b/openstack_dashboard/dashboards/admin/networks/ports/views.py @@ -87,7 +87,8 @@ class DetailView(project_views.DetailView): breadcrumb = [ (_("Networks"), self.get_redirect_url()), ((port.network_name or port.network_id), port.network_url), - (_("Ports"),), ] + (_("Ports"), None) + ] context["custom_breadcrumb"] = breadcrumb context["url"] = \ reverse('horizon:admin:networks:ports_tab', args=[port.network_id]) diff --git a/openstack_dashboard/dashboards/identity/projects/tests.py b/openstack_dashboard/dashboards/identity/projects/tests.py index 9176d5920f..9e4e08fe8f 100644 --- a/openstack_dashboard/dashboards/identity/projects/tests.py +++ b/openstack_dashboard/dashboards/identity/projects/tests.py @@ -17,6 +17,7 @@ import logging import os import unittest +import django from django.core.urlresolvers import reverse from django import http from django.test.utils import override_settings @@ -288,11 +289,15 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests): res = self.client.get(reverse('horizon:identity:projects:create')) self.assertTemplateUsed(res, views.WorkflowView.template_name) - self.assertContains(res, ''' - - ''', html=True) + if django.VERSION >= (1, 10): + pattern = ('') + else: + pattern = ('') + self.assertContains(res, pattern, html=True) workflow = res.context['workflow'] self.assertEqual(res.context['workflow'].name, diff --git a/openstack_dashboard/dashboards/project/containers/tests.py b/openstack_dashboard/dashboards/project/containers/tests.py index 8114befb52..1fd672973c 100644 --- a/openstack_dashboard/dashboards/project/containers/tests.py +++ b/openstack_dashboard/dashboards/project/containers/tests.py @@ -20,6 +20,7 @@ import copy import email.header import tempfile +import django from django.core.files.uploadedfile import InMemoryUploadedFile # noqa from django.core.urlresolvers import reverse from django import http @@ -436,9 +437,16 @@ class SwiftTests(test.TestCase): args=[container.name, obj.name]) res = self.client.get(copy_url) # The copy's name must appear in initial data - pattern = ('' % copy_name) + if django.VERSION >= (1, 10): + pattern = ('' % copy_name) + else: + pattern = ('' % copy_name) self.assertContains(res, pattern, html=True) def test_get_copy_name(self): diff --git a/openstack_dashboard/dashboards/project/loadbalancers/views.py b/openstack_dashboard/dashboards/project/loadbalancers/views.py index 30e552a4d8..192b3098ca 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/views.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/views.py @@ -139,7 +139,8 @@ class VipDetailsView(tabs.TabView): (vip_nav, reverse('horizon:project:loadbalancers:vipdetails', args=(vip.id,))), - (_("VIP"),), ] + (_("VIP"), None) + ] context["custom_breadcrumb"] = breadcrumb return context diff --git a/openstack_dashboard/dashboards/project/networks/ports/views.py b/openstack_dashboard/dashboards/project/networks/ports/views.py index 088887765c..3d42de1dec 100644 --- a/openstack_dashboard/dashboards/project/networks/ports/views.py +++ b/openstack_dashboard/dashboards/project/networks/ports/views.py @@ -90,7 +90,8 @@ class DetailView(tabs.TabbedTableView): # TODO(robcresswell) Add URL for "Ports" crumb after bug/1416838 breadcrumb = [ ((port.network_name or port.network_id), port.network_url), - (_("Ports"),), ] + (_("Ports"), None) + ] context["custom_breadcrumb"] = breadcrumb context["port"] = port context["url"] = self.get_redirect_url() diff --git a/openstack_dashboard/dashboards/project/networks/subnets/views.py b/openstack_dashboard/dashboards/project/networks/subnets/views.py index eceda8aaae..d1dff4da67 100644 --- a/openstack_dashboard/dashboards/project/networks/subnets/views.py +++ b/openstack_dashboard/dashboards/project/networks/subnets/views.py @@ -154,7 +154,8 @@ class DetailView(tabs.TabView): # TODO(robcresswell) Add URL for "Subnets" crumb after bug/1416838 breadcrumb = [ (network_nav, subnet.network_url), - (_("Subnets"),), ] + (_("Subnets"), None) + ] context["custom_breadcrumb"] = breadcrumb context["subnet"] = subnet context["url"] = \ diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py index 36022038c0..5a25b528f6 100644 --- a/openstack_dashboard/dashboards/project/routers/tests.py +++ b/openstack_dashboard/dashboards/project/routers/tests.py @@ -13,6 +13,7 @@ # under the License. import copy +import django from django.core.urlresolvers import reverse from django import http @@ -441,11 +442,15 @@ class RouterActionTests(RouterMixin, test.TestCase): self.assertTemplateUsed(res, 'project/routers/update.html') self.assertContains(res, 'Router Type') - self.assertContains( - res, - '', - html=True) + if django.VERSION >= (1, 10): + pattern = ('') + else: + pattern = ('') + self.assertContains(res, pattern, html=True) self.assertNotContains(res, 'centralized') @test.create_stubs({api.neutron: ('router_get', diff --git a/openstack_dashboard/dashboards/project/stacks/tests.py b/openstack_dashboard/dashboards/project/stacks/tests.py index 4ecfc39ed0..f1bbb06f3d 100644 --- a/openstack_dashboard/dashboards/project/stacks/tests.py +++ b/openstack_dashboard/dashboards/project/stacks/tests.py @@ -13,6 +13,7 @@ import json import re +import django from django.conf import settings from django.core import exceptions from django.core.urlresolvers import reverse @@ -394,16 +395,25 @@ class StackTests(test.TestCase): self.assertTemplateUsed(res, 'project/stacks/create.html') # ensure the fields were rendered correctly - self.assertContains(res, - '', html=True) - self.assertContains(res, - '', html=True) + if django.VERSION >= (1, 10): + pattern = ('') + secret = ('') + else: + pattern = ('') + secret = ('') + + self.assertContains(res, pattern, html=True) + self.assertContains(res, secret, html=True) @test.create_stubs({api.heat: ('template_validate',)}) def test_launch_stack_with_parameter_group(self): @@ -560,30 +570,24 @@ class StackTests(test.TestCase): self.assertTemplateUsed(res, 'project/stacks/create.html') # ensure the fields were rendered correctly - self.assertContains(res, - '', html=True) - self.assertContains(res, - '', html=True) - self.assertContains(res, - '', html=True) - self.assertContains(res, - '', html=True) - self.assertContains(res, - '', html=True) + if django.VERSION >= (1, 10): + input_str = ('') + else: + input_str = ('') + + self.assertContains(res, input_str.format(1, 'text'), html=True) + self.assertContains(res, input_str.format(2, 'number'), html=True) + self.assertContains(res, input_str.format(3, 'text'), html=True) + self.assertContains(res, input_str.format(4, 'text'), html=True) + self.assertContains( + res, + '', + html=True) # post some sample data and make sure it validates url = reverse('horizon:project:stacks:launch') diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py index 41b2ac0605..3138c17373 100644 --- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py @@ -1093,7 +1093,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): def _get_volume_row_action_from_ajax(self, res, action_name, row_id): def _matches_row_id(context_row): return (len(context_row.dicts) > 1 and - isinstance(context_row.dicts[1], dict) and + hasattr(context_row.dicts[1], 'get') and context_row.dicts[1].get('row_id', None) == row_id) matching = list(moves.filter(lambda r: _matches_row_id(r), diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index 8044ddbfd7..07607ce82a 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -10,7 +10,6 @@ from openstack_dashboard import exceptions from openstack_dashboard.settings import HORIZON_CONFIG DEBUG = True -TEMPLATE_DEBUG = DEBUG # WEBROOT is the location relative to Webserver root diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index 419b280bfe..e772626337 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -42,7 +42,6 @@ if ROOT_PATH not in sys.path: sys.path.append(ROOT_PATH) DEBUG = False -TEMPLATE_DEBUG = DEBUG SITE_BRANDING = 'OpenStack Dashboard' @@ -115,29 +114,35 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.request', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'django.contrib.messages.context_processors.messages', - 'horizon.context_processors.horizon', - 'openstack_dashboard.context_processors.openstack', -) - -TEMPLATE_LOADERS = ('horizon.themes.ThemeTemplateLoader',) - -CACHED_TEMPLATE_LOADERS = ( +CACHED_TEMPLATE_LOADERS = [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', - 'horizon.loaders.TemplateLoader',) + 'horizon.loaders.TemplateLoader' +] ADD_TEMPLATE_LOADERS = [] -TEMPLATE_DIRS = ( - os.path.join(ROOT_PATH, 'templates'), -) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(ROOT_PATH, 'templates')], + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.contrib.messages.context_processors.messages', + 'horizon.context_processors.horizon', + 'openstack_dashboard.context_processors.openstack', + ], + 'loaders': [ + 'horizon.themes.ThemeTemplateLoader' + ], + }, + }, +] STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', @@ -309,13 +314,19 @@ try: except ImportError: logging.warning("No local_settings file found.") +# configure template debugging +TEMPLATES[0]['OPTIONS']['debug'] = DEBUG + # Template loaders if DEBUG: - TEMPLATE_LOADERS += CACHED_TEMPLATE_LOADERS + tuple(ADD_TEMPLATE_LOADERS) + TEMPLATES[0]['OPTIONS']['loaders'].extend( + CACHED_TEMPLATE_LOADERS + ADD_TEMPLATE_LOADERS + ) else: - TEMPLATE_LOADERS += ( - ('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS), - ) + tuple(ADD_TEMPLATE_LOADERS) + TEMPLATES[0]['OPTIONS']['loaders'].extend( + [('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS)] + + ADD_TEMPLATE_LOADERS + ) NG_TEMPLATE_CACHE_AGE = NG_TEMPLATE_CACHE_AGE if not DEBUG else 0 diff --git a/openstack_dashboard/test/settings.py b/openstack_dashboard/test/settings.py index 2622781b51..4a84322629 100644 --- a/openstack_dashboard/test/settings.py +++ b/openstack_dashboard/test/settings.py @@ -36,8 +36,13 @@ WEBROOT = '/' SECRET_KEY = secret_key.generate_or_read_from_file( os.path.join(TEST_DIR, '.secret_key_store')) ROOT_URLCONF = 'openstack_dashboard.test.urls' -TEMPLATE_DIRS = ( - os.path.join(TEST_DIR, 'templates'), + +TEMPLATES[0]['DIRS'] = [ + os.path.join(TEST_DIR, 'templates') +] + +TEMPLATES[0]['OPTIONS']['context_processors'].append( + 'openstack_dashboard.context_processors.openstack' ) CUSTOM_THEME_PATH = 'themes/default' @@ -58,10 +63,6 @@ AVAILABLE_THEMES = [ # Theme Static Directory THEME_COLLECTION_DIR = 'themes' -TEMPLATE_CONTEXT_PROCESSORS += ( - 'openstack_dashboard.context_processors.openstack', -) - COMPRESS_OFFLINE = False INSTALLED_APPS = ( diff --git a/openstack_dashboard/test/tests/error_pages.py b/openstack_dashboard/test/tests/error_pages.py index 8e2204ec5a..3b55c1c951 100644 --- a/openstack_dashboard/test/tests/error_pages.py +++ b/openstack_dashboard/test/tests/error_pages.py @@ -25,7 +25,12 @@ class ErrorPageTests(test.TestCase): urls = 'openstack_dashboard.test.error_pages_urls' def test_500_error(self): - TEMPLATE_DIRS = (path.join(settings.ROOT_PATH, 'templates'),) - with self.settings(TEMPLATE_DIRS=TEMPLATE_DIRS): + with self.settings( + TEMPLATES=[{ + 'DIRS': [path.join(settings.ROOT_PATH, 'templates')], + 'BACKEND': ('django.template.backends.django.' + 'DjangoTemplates') + }], + ROOT_URLCONF=self.urls): response = self.client.get('/500/') self.assertIn(b'Server error', response.content) diff --git a/releasenotes/notes/bp-dj110-438f26c21f283c46.yaml b/releasenotes/notes/bp-dj110-438f26c21f283c46.yaml new file mode 100644 index 0000000000..8ca244aa42 --- /dev/null +++ b/releasenotes/notes/bp-dj110-438f26c21f283c46.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - The ``TEMPLATE_*`` settings have been replaced with a ``TEMPLATE`` dict. + This will likely cause issues when porting settings to this version of + Horizon. The TEMPLATE_DEBUG setting has been removed and is tied to + the DEBUG setting now. A detailed explanation of this dict can be found at + https://docs.djangoproject.com/en/1.10/ref/settings/#templates + - The ``is_authenticated()`` and ``is_anonymous()`` functions in Django + OpenStack Auth's ``User`` class are properties when running under Django + 1.10, and no longer take a margin parameter.