Merge "Fix compatibility issues with Django 1.10" into stable/newton

This commit is contained in:
Jenkins 2016-09-27 11:20:35 +00:00 committed by Gerrit Code Review
commit c2eaffeb2a
18 changed files with 170 additions and 106 deletions

View File

@ -26,6 +26,7 @@ import inspect
import logging import logging
import os import os
import django
from django.conf import settings from django.conf import settings
from django.conf.urls import include from django.conf.urls import include
from django.conf.urls import url from django.conf.urls import url
@ -54,7 +55,13 @@ LOG = logging.getLogger(__name__)
def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs): def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs):
for pattern in urlpatterns: for pattern in urlpatterns:
if getattr(pattern, 'callback', None): 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', []): if getattr(pattern, 'url_patterns', []):
_decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs) _decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs)

View File

@ -48,13 +48,13 @@ def breadcrumb_nav(context):
custom_breadcrumb = context.get('custom_breadcrumb') custom_breadcrumb = context.get('custom_breadcrumb')
# Build list of tuples (name, optional url) # Build list of tuples (name, optional url)
breadcrumb.append((dashboard.name,)) breadcrumb.append((dashboard.name, None))
if panel_group: if panel_group:
breadcrumb.append((panel_group.name,)) breadcrumb.append((panel_group.name, None))
if panel: if panel:
breadcrumb.append((panel.name, panel.get_absolute_url())) breadcrumb.append((panel.name, panel.get_absolute_url()))
if custom_breadcrumb: if custom_breadcrumb:
breadcrumb.extend(custom_breadcrumb) breadcrumb.extend(custom_breadcrumb)
breadcrumb.append((context.get('page_title'),)) breadcrumb.append((context.get('page_title'), None))
return {'breadcrumb': breadcrumb} return {'breadcrumb': breadcrumb}

View File

@ -31,7 +31,6 @@ ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static')) STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
DEBUG = False DEBUG = False
TEMPLATE_DEBUG = DEBUG
TESTSERVER = 'http://testserver' TESTSERVER = 'http://testserver'
SECRET_KEY = 'elj1IWiLoWHgcyYxFVLj7cM5rGOOxWl0' SECRET_KEY = 'elj1IWiLoWHgcyYxFVLj7cM5rGOOxWl0'
@ -73,20 +72,28 @@ MIDDLEWARE_CLASSES = (
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
) )
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATES = [
'django.core.context_processors.debug', {
'django.core.context_processors.i18n', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'django.core.context_processors.request', 'DIRS': [os.path.join(ROOT_PATH, 'tests', 'templates')],
'django.core.context_processors.media', 'OPTIONS': {
'django.core.context_processors.static', 'context_processors': [
'django.contrib.messages.context_processors.messages', 'django.template.context_processors.debug',
'horizon.context_processors.horizon') 'django.template.context_processors.i18n',
'django.template.context_processors.request',
TEMPLATE_LOADERS = ( 'django.template.context_processors.media',
'django.template.loaders.filesystem.Loader', 'django.template.context_processors.static',
'django.template.loaders.app_directories.Loader', 'django.contrib.messages.context_processors.messages',
'horizon.loaders.TemplateLoader' 'horizon.context_processors.horizon',
) ],
'loaders': [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'horizon.loaders.TemplateLoader'
],
},
},
]
STATIC_URL = '/static/' STATIC_URL = '/static/'
WEBROOT = '/' WEBROOT = '/'
@ -94,7 +101,6 @@ WEBROOT = '/'
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
ROOT_URLCONF = 'horizon.test.urls' ROOT_URLCONF = 'horizon.test.urls'
TEMPLATE_DIRS = (os.path.join(ROOT_PATH, 'tests', 'templates'),)
SITE_ID = 1 SITE_ID = 1
SITE_BRANDING = 'Horizon' SITE_BRANDING = 'Horizon'

View File

@ -67,7 +67,6 @@ class AdminDetailView(tables.DataTableView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(AdminDetailView, self).get_context_data(**kwargs) context = super(AdminDetailView, self).get_context_data(**kwargs)
hypervisor_name = self.kwargs['hypervisor'].split('_', 1)[1] hypervisor_name = self.kwargs['hypervisor'].split('_', 1)[1]
breadcrumb = [ breadcrumb = [(hypervisor_name, None)]
(hypervisor_name,), ]
context['custom_breadcrumb'] = breadcrumb context['custom_breadcrumb'] = breadcrumb
return context return context

View File

@ -87,7 +87,8 @@ class DetailView(project_views.DetailView):
breadcrumb = [ breadcrumb = [
(_("Networks"), self.get_redirect_url()), (_("Networks"), self.get_redirect_url()),
((port.network_name or port.network_id), port.network_url), ((port.network_name or port.network_id), port.network_url),
(_("Ports"),), ] (_("Ports"), None)
]
context["custom_breadcrumb"] = breadcrumb context["custom_breadcrumb"] = breadcrumb
context["url"] = \ context["url"] = \
reverse('horizon:admin:networks:ports_tab', args=[port.network_id]) reverse('horizon:admin:networks:ports_tab', args=[port.network_id])

View File

@ -17,6 +17,7 @@ import logging
import os import os
import unittest import unittest
import django
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
from django.test.utils import override_settings from django.test.utils import override_settings
@ -288,11 +289,15 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests):
res = self.client.get(reverse('horizon:identity:projects:create')) res = self.client.get(reverse('horizon:identity:projects:create'))
self.assertTemplateUsed(res, views.WorkflowView.template_name) self.assertTemplateUsed(res, views.WorkflowView.template_name)
self.assertContains(res, ''' if django.VERSION >= (1, 10):
<input class="form-control" pattern = ('<input class="form-control" '
id="id_subnet" min="-1" 'id="id_subnet" min="-1" '
name="subnet" type="number" value="10" /> 'name="subnet" type="number" value="10" required/>')
''', html=True) 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'] workflow = res.context['workflow']
self.assertEqual(res.context['workflow'].name, self.assertEqual(res.context['workflow'].name,

View File

@ -20,6 +20,7 @@ import copy
import email.header import email.header
import tempfile import tempfile
import django
from django.core.files.uploadedfile import InMemoryUploadedFile # noqa from django.core.files.uploadedfile import InMemoryUploadedFile # noqa
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
@ -436,9 +437,16 @@ class SwiftTests(test.TestCase):
args=[container.name, obj.name]) args=[container.name, obj.name])
res = self.client.get(copy_url) res = self.client.get(copy_url)
# The copy's name must appear in initial data # The copy's name must appear in initial data
pattern = ('<input id="id_new_object_name" value="%s" ' if django.VERSION >= (1, 10):
'name="new_object_name" type="text" ' pattern = ('<input id="id_new_object_name" value="%s" '
'class="form-control" maxlength="255" />' % copy_name) '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) self.assertContains(res, pattern, html=True)
def test_get_copy_name(self): def test_get_copy_name(self):

View File

@ -139,7 +139,8 @@ class VipDetailsView(tabs.TabView):
(vip_nav, (vip_nav,
reverse('horizon:project:loadbalancers:vipdetails', reverse('horizon:project:loadbalancers:vipdetails',
args=(vip.id,))), args=(vip.id,))),
(_("VIP"),), ] (_("VIP"), None)
]
context["custom_breadcrumb"] = breadcrumb context["custom_breadcrumb"] = breadcrumb
return context return context

View File

@ -90,7 +90,8 @@ class DetailView(tabs.TabbedTableView):
# TODO(robcresswell) Add URL for "Ports" crumb after bug/1416838 # TODO(robcresswell) Add URL for "Ports" crumb after bug/1416838
breadcrumb = [ breadcrumb = [
((port.network_name or port.network_id), port.network_url), ((port.network_name or port.network_id), port.network_url),
(_("Ports"),), ] (_("Ports"), None)
]
context["custom_breadcrumb"] = breadcrumb context["custom_breadcrumb"] = breadcrumb
context["port"] = port context["port"] = port
context["url"] = self.get_redirect_url() context["url"] = self.get_redirect_url()

View File

@ -154,7 +154,8 @@ class DetailView(tabs.TabView):
# TODO(robcresswell) Add URL for "Subnets" crumb after bug/1416838 # TODO(robcresswell) Add URL for "Subnets" crumb after bug/1416838
breadcrumb = [ breadcrumb = [
(network_nav, subnet.network_url), (network_nav, subnet.network_url),
(_("Subnets"),), ] (_("Subnets"), None)
]
context["custom_breadcrumb"] = breadcrumb context["custom_breadcrumb"] = breadcrumb
context["subnet"] = subnet context["subnet"] = subnet
context["url"] = \ context["url"] = \

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
import copy import copy
import django
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import http from django import http
@ -441,11 +442,15 @@ class RouterActionTests(RouterMixin, test.TestCase):
self.assertTemplateUsed(res, 'project/routers/update.html') self.assertTemplateUsed(res, 'project/routers/update.html')
self.assertContains(res, 'Router Type') self.assertContains(res, 'Router Type')
self.assertContains( if django.VERSION >= (1, 10):
res, pattern = ('<input class="form-control" id="id_mode" name="mode" '
'<input class="form-control" id="id_mode" name="mode" ' 'readonly="readonly" type="text" value="distributed" '
'readonly="readonly" type="text" value="distributed" />', 'required/>')
html=True) 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') self.assertNotContains(res, 'centralized')
@test.create_stubs({api.neutron: ('router_get', @test.create_stubs({api.neutron: ('router_get',

View File

@ -13,6 +13,7 @@
import json import json
import re import re
import django
from django.conf import settings from django.conf import settings
from django.core import exceptions from django.core import exceptions
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -394,16 +395,25 @@ class StackTests(test.TestCase):
self.assertTemplateUsed(res, 'project/stacks/create.html') self.assertTemplateUsed(res, 'project/stacks/create.html')
# ensure the fields were rendered correctly # ensure the fields were rendered correctly
self.assertContains(res, if django.VERSION >= (1, 10):
'<input class="form-control" ' pattern = ('<input class="form-control" '
'id="id___param_public_string" ' 'id="id___param_public_string" '
'name="__param_public_string" ' 'name="__param_public_string" type="text" required/>')
'type="text" />', html=True) secret = ('<input class="form-control" '
self.assertContains(res, 'id="id___param_secret_string" '
'<input class="form-control" ' 'name="__param_secret_string" '
'id="id___param_secret_string" ' 'type="password" required>')
'name="__param_secret_string" ' else:
'type="password" />', html=True) 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',)}) @test.create_stubs({api.heat: ('template_validate',)})
def test_launch_stack_with_parameter_group(self): def test_launch_stack_with_parameter_group(self):
@ -560,30 +570,24 @@ class StackTests(test.TestCase):
self.assertTemplateUsed(res, 'project/stacks/create.html') self.assertTemplateUsed(res, 'project/stacks/create.html')
# ensure the fields were rendered correctly # ensure the fields were rendered correctly
self.assertContains(res, if django.VERSION >= (1, 10):
'<input class="form-control" ' input_str = ('<input class="form-control" '
'id="id___param_param1" ' 'id="id___param_param{0}" '
'name="__param_param1" ' 'name="__param_param{0}" type="{1}" required/>')
'type="text" />', html=True) else:
self.assertContains(res, input_str = ('<input class="form-control" '
'<input class="form-control" ' 'id="id___param_param{0}" '
'id="id___param_param2" ' 'name="__param_param{0}" type="{1}"/>')
'name="__param_param2" '
'type="number" />', html=True) self.assertContains(res, input_str.format(1, 'text'), html=True)
self.assertContains(res, self.assertContains(res, input_str.format(2, 'number'), html=True)
'<input class="form-control" ' self.assertContains(res, input_str.format(3, 'text'), html=True)
'id="id___param_param3" ' self.assertContains(res, input_str.format(4, 'text'), html=True)
'name="__param_param3" ' self.assertContains(
'type="text" />', html=True) res,
self.assertContains(res, '<input id="id___param_param5" name="__param_param5" '
'<input class="form-control" ' 'type="checkbox">',
'id="id___param_param4" ' html=True)
'name="__param_param4" '
'type="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 # post some sample data and make sure it validates
url = reverse('horizon:project:stacks:launch') url = reverse('horizon:project:stacks:launch')

View File

@ -1093,7 +1093,7 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
def _get_volume_row_action_from_ajax(self, res, action_name, row_id): def _get_volume_row_action_from_ajax(self, res, action_name, row_id):
def _matches_row_id(context_row): def _matches_row_id(context_row):
return (len(context_row.dicts) > 1 and 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) context_row.dicts[1].get('row_id', None) == row_id)
matching = list(moves.filter(lambda r: _matches_row_id(r), matching = list(moves.filter(lambda r: _matches_row_id(r),

View File

@ -10,7 +10,6 @@ from openstack_dashboard import exceptions
from openstack_dashboard.settings import HORIZON_CONFIG from openstack_dashboard.settings import HORIZON_CONFIG
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = DEBUG
# WEBROOT is the location relative to Webserver root # WEBROOT is the location relative to Webserver root

View File

@ -42,7 +42,6 @@ if ROOT_PATH not in sys.path:
sys.path.append(ROOT_PATH) sys.path.append(ROOT_PATH)
DEBUG = False DEBUG = False
TEMPLATE_DEBUG = DEBUG
SITE_BRANDING = 'OpenStack Dashboard' SITE_BRANDING = 'OpenStack Dashboard'
@ -115,29 +114,35 @@ MIDDLEWARE_CLASSES = (
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
) )
TEMPLATE_CONTEXT_PROCESSORS = ( CACHED_TEMPLATE_LOADERS = [
'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 = (
'django.template.loaders.filesystem.Loader', 'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader', 'django.template.loaders.app_directories.Loader',
'horizon.loaders.TemplateLoader',) 'horizon.loaders.TemplateLoader'
]
ADD_TEMPLATE_LOADERS = [] ADD_TEMPLATE_LOADERS = []
TEMPLATE_DIRS = ( TEMPLATES = [
os.path.join(ROOT_PATH, '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 = ( STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.FileSystemFinder',
@ -309,13 +314,19 @@ try:
except ImportError: except ImportError:
logging.warning("No local_settings file found.") logging.warning("No local_settings file found.")
# configure template debugging
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
# Template loaders # Template loaders
if DEBUG: if DEBUG:
TEMPLATE_LOADERS += CACHED_TEMPLATE_LOADERS + tuple(ADD_TEMPLATE_LOADERS) TEMPLATES[0]['OPTIONS']['loaders'].extend(
CACHED_TEMPLATE_LOADERS + ADD_TEMPLATE_LOADERS
)
else: else:
TEMPLATE_LOADERS += ( TEMPLATES[0]['OPTIONS']['loaders'].extend(
('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS), [('django.template.loaders.cached.Loader', CACHED_TEMPLATE_LOADERS)] +
) + tuple(ADD_TEMPLATE_LOADERS) ADD_TEMPLATE_LOADERS
)
NG_TEMPLATE_CACHE_AGE = NG_TEMPLATE_CACHE_AGE if not DEBUG else 0 NG_TEMPLATE_CACHE_AGE = NG_TEMPLATE_CACHE_AGE if not DEBUG else 0

View File

@ -36,8 +36,13 @@ WEBROOT = '/'
SECRET_KEY = secret_key.generate_or_read_from_file( SECRET_KEY = secret_key.generate_or_read_from_file(
os.path.join(TEST_DIR, '.secret_key_store')) os.path.join(TEST_DIR, '.secret_key_store'))
ROOT_URLCONF = 'openstack_dashboard.test.urls' 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' CUSTOM_THEME_PATH = 'themes/default'
@ -58,10 +63,6 @@ AVAILABLE_THEMES = [
# Theme Static Directory # Theme Static Directory
THEME_COLLECTION_DIR = 'themes' THEME_COLLECTION_DIR = 'themes'
TEMPLATE_CONTEXT_PROCESSORS += (
'openstack_dashboard.context_processors.openstack',
)
COMPRESS_OFFLINE = False COMPRESS_OFFLINE = False
INSTALLED_APPS = ( INSTALLED_APPS = (

View File

@ -25,7 +25,12 @@ class ErrorPageTests(test.TestCase):
urls = 'openstack_dashboard.test.error_pages_urls' urls = 'openstack_dashboard.test.error_pages_urls'
def test_500_error(self): def test_500_error(self):
TEMPLATE_DIRS = (path.join(settings.ROOT_PATH, 'templates'),) with self.settings(
with self.settings(TEMPLATE_DIRS=TEMPLATE_DIRS): TEMPLATES=[{
'DIRS': [path.join(settings.ROOT_PATH, 'templates')],
'BACKEND': ('django.template.backends.django.'
'DjangoTemplates')
}],
ROOT_URLCONF=self.urls):
response = self.client.get('/500/') response = self.client.get('/500/')
self.assertIn(b'Server error', response.content) self.assertIn(b'Server error', response.content)

View 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.