Fix compatibility issues with Django 1.10
Required modifying our URL pattern decorator to be compatible with the slightly different callback attribute mechanism in 1.10 Modified breadcrumb structures to always be 2-tuples as tuple unpacking is now strictly enforced in 1.10 Also made the following things changed in Django 1.10. - fixed the now-deprecated TEMPLATE_DEBUG. - Caught up with Request context change - SimpleTestCase.urls is removed in 1.10. ROOT_URLCONF needs to be used instead. Co-Authored-By: Richard Jones <r1chardj0n3s@gmail.com> Co-Authored-By: Akihiro Motoki <motoki@da.jp.nec.com> Change-Id: I59cbd8bff117813258539ed0724fe89a9f5b77ee Implements: blueprint dj110
This commit is contained in:
parent
3149a19af3
commit
3488ab3d40
@ -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)
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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, '''
|
||||
<input class="form-control"
|
||||
id="id_subnet" min="-1"
|
||||
name="subnet" type="number" value="10" />
|
||||
''', html=True)
|
||||
if django.VERSION >= (1, 10):
|
||||
pattern = ('<input class="form-control" '
|
||||
'id="id_subnet" min="-1" '
|
||||
'name="subnet" type="number" value="10" required/>')
|
||||
else:
|
||||
pattern = ('<input class="form-control" '
|
||||
'id="id_subnet" min="-1" '
|
||||
'name="subnet" type="number" value="10"/>')
|
||||
self.assertContains(res, pattern, html=True)
|
||||
|
||||
workflow = res.context['workflow']
|
||||
self.assertEqual(res.context['workflow'].name,
|
||||
|
@ -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 = ('<input id="id_new_object_name" value="%s" '
|
||||
'name="new_object_name" type="text" '
|
||||
'class="form-control" maxlength="255" />' % copy_name)
|
||||
if django.VERSION >= (1, 10):
|
||||
pattern = ('<input id="id_new_object_name" value="%s" '
|
||||
'name="new_object_name" type="text" '
|
||||
'class="form-control" '
|
||||
'maxlength="255" required/>' % copy_name)
|
||||
else:
|
||||
pattern = ('<input id="id_new_object_name" value="%s" '
|
||||
'name="new_object_name" type="text" '
|
||||
'class="form-control" '
|
||||
'maxlength="255"/>' % copy_name)
|
||||
self.assertContains(res, pattern, html=True)
|
||||
|
||||
def test_get_copy_name(self):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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"] = \
|
||||
|
@ -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,
|
||||
'<input class="form-control" id="id_mode" name="mode" '
|
||||
'readonly="readonly" type="text" value="distributed" />',
|
||||
html=True)
|
||||
if django.VERSION >= (1, 10):
|
||||
pattern = ('<input class="form-control" id="id_mode" name="mode" '
|
||||
'readonly="readonly" type="text" value="distributed" '
|
||||
'required/>')
|
||||
else:
|
||||
pattern = ('<input class="form-control" id="id_mode" name="mode" '
|
||||
'readonly="readonly" type="text" '
|
||||
'value="distributed" />')
|
||||
self.assertContains(res, pattern, html=True)
|
||||
self.assertNotContains(res, 'centralized')
|
||||
|
||||
@test.create_stubs({api.neutron: ('router_get',
|
||||
|
@ -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,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_public_string" '
|
||||
'name="__param_public_string" '
|
||||
'type="text" />', html=True)
|
||||
self.assertContains(res,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_secret_string" '
|
||||
'name="__param_secret_string" '
|
||||
'type="password" />', html=True)
|
||||
if django.VERSION >= (1, 10):
|
||||
pattern = ('<input class="form-control" '
|
||||
'id="id___param_public_string" '
|
||||
'name="__param_public_string" type="text" required/>')
|
||||
secret = ('<input class="form-control" '
|
||||
'id="id___param_secret_string" '
|
||||
'name="__param_secret_string" '
|
||||
'type="password" required>')
|
||||
else:
|
||||
pattern = ('<input class="form-control" '
|
||||
'id="id___param_public_string" '
|
||||
'name="__param_public_string" type="text" />')
|
||||
secret = ('<input class="form-control" '
|
||||
'id="id___param_secret_string" '
|
||||
'name="__param_secret_string" '
|
||||
'type="password" />')
|
||||
|
||||
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,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_param1" '
|
||||
'name="__param_param1" '
|
||||
'type="text" />', html=True)
|
||||
self.assertContains(res,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_param2" '
|
||||
'name="__param_param2" '
|
||||
'type="number" />', html=True)
|
||||
self.assertContains(res,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_param3" '
|
||||
'name="__param_param3" '
|
||||
'type="text" />', html=True)
|
||||
self.assertContains(res,
|
||||
'<input class="form-control" '
|
||||
'id="id___param_param4" '
|
||||
'name="__param_param4" '
|
||||
'type="text" />', html=True)
|
||||
self.assertContains(res,
|
||||
'<input id="id___param_param5" '
|
||||
'name="__param_param5" '
|
||||
'type="checkbox" />', html=True)
|
||||
if django.VERSION >= (1, 10):
|
||||
input_str = ('<input class="form-control" '
|
||||
'id="id___param_param{0}" '
|
||||
'name="__param_param{0}" type="{1}" required/>')
|
||||
else:
|
||||
input_str = ('<input class="form-control" '
|
||||
'id="id___param_param{0}" '
|
||||
'name="__param_param{0}" type="{1}"/>')
|
||||
|
||||
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,
|
||||
'<input id="id___param_param5" name="__param_param5" '
|
||||
'type="checkbox">',
|
||||
html=True)
|
||||
|
||||
# post some sample data and make sure it validates
|
||||
url = reverse('horizon:project:stacks:launch')
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 = (
|
||||
|
@ -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)
|
||||
|
10
releasenotes/notes/bp-dj110-438f26c21f283c46.yaml
Normal file
10
releasenotes/notes/bp-dj110-438f26c21f283c46.yaml
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user