diff --git a/.zuul.yaml b/.zuul.yaml index d4488ac2f9..a02f1c8d77 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -54,11 +54,11 @@ - project: check: jobs: - - horizon-openstack-tox-py35dj20: - voting: false + - horizon-openstack-tox-py35dj20 - horizon-selenium-headless - horizon-dsvm-tempest-plugin gate: jobs: + - horizon-openstack-tox-py35dj20 - horizon-selenium-headless - horizon-dsvm-tempest-plugin diff --git a/horizon/base.py b/horizon/base.py index 9e60f10ad4..4ed7d19ca8 100644 --- a/horizon/base.py +++ b/horizon/base.py @@ -27,7 +27,6 @@ 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 @@ -58,12 +57,7 @@ def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs): for pattern in urlpatterns: if getattr(pattern, 'callback', None): 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 + pattern.callback = decorated if getattr(pattern, 'url_patterns', []): _decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs) diff --git a/horizon/templatetags/shellfilter.py b/horizon/templatetags/shellfilter.py index 108ca6204b..d3f2ac599b 100644 --- a/horizon/templatetags/shellfilter.py +++ b/horizon/templatetags/shellfilter.py @@ -10,15 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -import django from django import template from django.template import defaultfilters from django.utils import safestring -if django.VERSION >= (1, 9): - register = template.Library() -else: - register = template.base.Library() +register = template.Library() @register.filter(is_safe=True) diff --git a/horizon/test/unit/test_base.py b/horizon/test/unit/test_base.py index 344c80444f..a0fc9ed755 100644 --- a/horizon/test/unit/test_base.py +++ b/horizon/test/unit/test_base.py @@ -22,7 +22,6 @@ from importlib import import_module import six from six import moves -import django from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured @@ -310,10 +309,7 @@ class HorizonTests(BaseHorizonTests): self.client.logout() resp = self.client.get(url) - if django.VERSION >= (1, 9): - self.assertRedirects(resp, settings.TESTSERVER + redirect_url) - else: - self.assertRedirects(resp, redirect_url) + self.assertRedirects(resp, settings.TESTSERVER + redirect_url) # Set SSL settings for test server settings.SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', diff --git a/openstack_auth/tests/unit/test_auth.py b/openstack_auth/tests/unit/test_auth.py index c48702c472..2dd193ce11 100644 --- a/openstack_auth/tests/unit/test_auth.py +++ b/openstack_auth/tests/unit/test_auth.py @@ -13,7 +13,6 @@ import uuid -import django from django.conf import settings from django.contrib import auth from django import test @@ -395,10 +394,7 @@ class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase): response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -444,10 +440,7 @@ class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase): response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -768,10 +761,7 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) @@ -816,10 +806,7 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, response = self.client.get(url, form_data) if next: - if django.VERSION >= (1, 9): - expected_url = next - else: - expected_url = 'http://testserver%s' % next + expected_url = next self.assertEqual(response['location'], expected_url) else: self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) diff --git a/openstack_dashboard/dashboards/identity/projects/tests.py b/openstack_dashboard/dashboards/identity/projects/tests.py index d5b6d91bd0..26d1bac440 100644 --- a/openstack_dashboard/dashboards/identity/projects/tests.py +++ b/openstack_dashboard/dashboards/identity/projects/tests.py @@ -17,7 +17,6 @@ import logging import os import unittest -import django from django import http from django.test.utils import override_settings from django.urls import reverse @@ -277,14 +276,9 @@ class CreateProjectWorkflowTests(test.BaseAdminViewTests): res = self.client.get(reverse('horizon:identity:projects:create')) self.assertTemplateUsed(res, views.WorkflowView.template_name) - if django.VERSION >= (1, 10): - pattern = ('') - else: - pattern = ('') + pattern = ('') self.assertContains(res, pattern, html=True) workflow = res.context['workflow'] diff --git a/openstack_dashboard/dashboards/identity/users/tests.py b/openstack_dashboard/dashboards/identity/users/tests.py index c062cddf7f..b0d6f59e40 100644 --- a/openstack_dashboard/dashboards/identity/users/tests.py +++ b/openstack_dashboard/dashboards/identity/users/tests.py @@ -18,7 +18,6 @@ from socket import timeout as socket_timeout -import django from django import http from django.test.utils import override_settings from django.urls import reverse @@ -238,19 +237,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -291,19 +289,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() @@ -347,19 +344,18 @@ class UsersViewTests(test.BaseAdminViewTests): api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) api.keystone.get_default_role(IgnoreArg()) \ .AndReturn(self.roles.first()) - if django.VERSION >= (1, 9): - if api.keystone.VERSIONS.active >= 3: - api.keystone.tenant_list( - IgnoreArg(), domain=domain_id).AndReturn( - [self.tenants.list(), False]) - else: - api.keystone.tenant_list( - IgnoreArg(), user=None).AndReturn( - [self.tenants.list(), False]) + if api.keystone.VERSIONS.active >= 3: + api.keystone.tenant_list( + IgnoreArg(), domain=domain_id).AndReturn( + [self.tenants.list(), False]) + else: + api.keystone.tenant_list( + IgnoreArg(), user=None).AndReturn( + [self.tenants.list(), False]) - api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) - api.keystone.get_default_role(IgnoreArg()) \ - .AndReturn(self.roles.first()) + api.keystone.role_list(IgnoreArg()).AndReturn(self.roles.list()) + api.keystone.get_default_role(IgnoreArg()) \ + .AndReturn(self.roles.first()) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 9a67c827d4..8e2b5cd989 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -1783,18 +1783,11 @@ class InstanceTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase): # NOTE(adriant): Django 1.11 changes the checked syntax to use html5 # "checked" rather than XHTML's "checked='checked'". - if django.VERSION >= (1, 11): - checked_box = ( - '' - ) - else: - checked_box = ( - '' - ) + checked_box = ( + '' + ) if only_one_network: self.assertContains(res, checked_box, html=True) else: diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py index c9423864df..71d6a4f7bc 100644 --- a/openstack_dashboard/dashboards/project/routers/tests.py +++ b/openstack_dashboard/dashboards/project/routers/tests.py @@ -13,7 +13,6 @@ # under the License. import copy -import django from django import http from django.urls import reverse @@ -590,14 +589,9 @@ class RouterActionTests(RouterMixin, test.TestCase): self.assertTemplateUsed(res, 'project/routers/update.html') self.assertContains(res, 'Router Type') - if django.VERSION >= (1, 10): - pattern = ('') - else: - pattern = ('') + pattern = ('') self.assertContains(res, pattern, html=True) self.assertNotContains(res, 'centralized') diff --git a/openstack_dashboard/dashboards/project/security_groups/tests.py b/openstack_dashboard/dashboards/project/security_groups/tests.py index 95cce0b116..a97332ab60 100644 --- a/openstack_dashboard/dashboards/project/security_groups/tests.py +++ b/openstack_dashboard/dashboards/project/security_groups/tests.py @@ -21,7 +21,6 @@ import cgi from mox3.mox import IsA import six -import django from django.conf import settings from django import http from django.urls import reverse @@ -478,9 +477,7 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): + for i in range(2): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) @@ -503,13 +500,9 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - for i in range(3): + for i in range(6): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): - for i in range(3): - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) self.mox.ReplayAll() @@ -559,10 +552,7 @@ class SecurityGroupsViewTests(test.TestCase): icmp_rule = self.security_group_rules.list()[1] # Call POST 5 times (*2 if Django >= 1.9) - call_post = 5 - if django.VERSION >= (1, 9): - call_post *= 2 - + call_post = 5 * 2 for i in range(call_post): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) @@ -921,9 +911,7 @@ class SecurityGroupsViewTests(test.TestCase): sec_group_list = self.security_groups.list() rule = self.security_group_rules.first() - api.neutron.security_group_list( - IsA(http.HttpRequest)).AndReturn(sec_group_list) - if django.VERSION >= (1, 9): + for i in range(2): api.neutron.security_group_list( IsA(http.HttpRequest)).AndReturn(sec_group_list) diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 7571d08d00..ccec5e1620 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -17,7 +17,6 @@ import copy import mock import six -import django from django.conf import settings from django.forms import widgets from django.template.defaultfilters import slugify @@ -616,12 +615,8 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, "The volume size cannot be less than the " "snapshot size (40GiB)") - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) self.mock_volume_snapshot_get.assert_called_with(test.IsHttpRequest(), str(snapshot.id)) @@ -788,12 +783,8 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, msg) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) self.assertEqual(2, self.mock_tenant_limit_usages.call_count) self.mock_image_get.assert_called_with(test.IsHttpRequest(), @@ -835,14 +826,9 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): self.assertFormError(res, 'form', None, "The volume size cannot be less than the " "image minimum disk size (30GiB)") - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.mock_image_get.assert_called_with(test.IsHttpRequest(), str(image.id)) @@ -904,16 +890,10 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): ' have 20GiB of your quota available.'] self.assertEqual(res.context['form'].errors['__all__'], expected_error) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_volume_list.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_volume_list.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_volume_list.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.assertEqual(2, self.mock_tenant_limit_usages.call_count) self.mock_volume_snapshot_list.assert_called_with( @@ -963,14 +943,9 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): ' volumes.'] self.assertEqual(res.context['form'].errors['__all__'], expected_error) - if django.VERSION >= (1, 9): - self.assertEqual(3, self.mock_volume_type_list.call_count) - self.assertEqual(2, self.mock_volume_type_default.call_count) - self.assertEqual(2, self.mock_availability_zone_list.call_count) - else: - self.assertEqual(2, self.mock_volume_type_list.call_count) - self.assertEqual(1, self.mock_volume_type_default.call_count) - self.assertEqual(1, self.mock_availability_zone_list.call_count) + self.assertEqual(3, self.mock_volume_type_list.call_count) + self.assertEqual(2, self.mock_volume_type_default.call_count) + self.assertEqual(2, self.mock_availability_zone_list.call_count) self.mock_volume_snapshot_list.assert_called_with( test.IsHttpRequest(), search_opts=SEARCH_OPTS) diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index 01cf8610e1..5fdf266e41 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -25,7 +25,6 @@ import os import traceback import unittest -import django from django.conf import settings from django.contrib.messages.storage import default_storage from django.core.handlers import wsgi @@ -330,14 +329,10 @@ class TestCase(horizon_helpers.TestCase): Asserts that the given response issued a 302 redirect without processing the view which is redirected to. """ - if django.VERSION >= (1, 9): - loc = six.text_type(response._headers.get('location', None)[1]) - loc = http.urlunquote(loc) - expected_url = http.urlunquote(expected_url) - self.assertEqual(loc, expected_url) - else: - self.assertEqual(response._headers.get('location', None), - ('Location', settings.TESTSERVER + expected_url)) + loc = six.text_type(response._headers.get('location', None)[1]) + loc = http.urlunquote(loc) + expected_url = http.urlunquote(expected_url) + self.assertEqual(loc, expected_url) self.assertEqual(response.status_code, 302) def assertNoFormErrors(self, response, context_name="form"): diff --git a/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml b/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml new file mode 100644 index 0000000000..b829768d46 --- /dev/null +++ b/releasenotes/notes/django-2.0-b37c6e91d20519fa.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Django 2.0 support is added as experimental. + Support for Django 1.10 or older releases is dropped. + Django 1.11 (LTS) is still the primary supported Django version.