From 153c387b870a4988f13fb3e3abf8009b0f330ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dra=C5=BEen=20Odoba=C5=A1i=C4=87?= Date: Mon, 16 Jul 2018 13:49:58 +0200 Subject: [PATCH 1/3] Add support for Django2.0 --- .travis.yml | 10 +++++----- requirements/base.txt | 2 +- smartmin/middleware.py | 4 ++-- smartmin/templatetags/smartmin.py | 4 ++-- smartmin/tests.py | 2 +- smartmin/users/middleware.py | 8 +++++--- smartmin/users/views.py | 6 +++--- smartmin/views.py | 2 +- test_runner/blog/migrations/0001_initial.py | 8 ++++---- test_runner/blog/tests.py | 14 +++++++------- test_runner/settings.py | 2 +- test_runner/urls.py | 2 +- 12 files changed, 33 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05317da..f9b2c43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,12 +6,12 @@ python: - "3.5" - "3.6" env: - - DJANGO_VERSION=1.9.11 - - DJANGO_VERSION=1.10.5 - - DJANGO_VERSION=1.11.2 + - DJANGO_VERSION=1.10.8 + - DJANGO_VERSION=1.11.14 + - DJANGO_VERSION=2.0.7 install: - - pip install -q -rrequirements/base.txt - - pip install -q -rrequirements/tests.txt + - pip install -q -r requirements/base.txt + - pip install -q -r requirements/tests.txt - pip install -q django==$DJANGO_VERSION - pip install coveralls before_script: diff --git a/requirements/base.txt b/requirements/base.txt index 53ca267..399430e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ celery -django>=1.9,<2.0 +django>=1.10,<2.1 django_compressor pytz redis diff --git a/smartmin/middleware.py b/smartmin/middleware.py index 2454673..f87ca51 100644 --- a/smartmin/middleware.py +++ b/smartmin/middleware.py @@ -28,7 +28,7 @@ def process_response(self, request, response): class ProfileMiddleware(MiddlewareMixin): - def __init__(self): + def __init__(self, get_response=None): pass def process_view(self, request, view, *args, **kwargs): @@ -69,7 +69,7 @@ def process_view(self, request, view, *args, **kwargs): return None -class TimezoneMiddleware(object): +class TimezoneMiddleware(MiddlewareMixin): def process_request(self, request): user_tz = getattr(settings, 'USER_TIME_ZONE', None) diff --git a/smartmin/templatetags/smartmin.py b/smartmin/templatetags/smartmin.py index 7e59f26..5fd5860 100644 --- a/smartmin/templatetags/smartmin.py +++ b/smartmin/templatetags/smartmin.py @@ -7,14 +7,14 @@ from datetime import datetime, timedelta from django import template from django.conf import settings -from django.core.urlresolvers import reverse +from django.urls import reverse from django.template import TemplateSyntaxError from django.utils import timezone register = template.Library() -@register.assignment_tag +@register.simple_tag def get_hostname(): if settings.HOSTNAME: return settings.HOSTNAME diff --git a/smartmin/tests.py b/smartmin/tests.py index 9e9ee34..cf9e834 100644 --- a/smartmin/tests.py +++ b/smartmin/tests.py @@ -5,7 +5,7 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import Group -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test.testcases import TestCase from django.utils.encoding import force_str from six.moves.urllib.parse import urlparse diff --git a/smartmin/users/middleware.py b/smartmin/users/middleware.py index e0f1a37..cd79853 100644 --- a/smartmin/users/middleware.py +++ b/smartmin/users/middleware.py @@ -3,7 +3,7 @@ import django.views.static from django.conf import settings -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponseRedirect from .models import PasswordHistory @@ -19,14 +19,16 @@ class ChangePasswordMiddleware(MiddlewareMixin): Redirects all users to the password change form if we find that a user's password is expired. """ - def __init__(self): + def __init__(self, get_response=None): + super(ChangePasswordMiddleware, self).__init__(get_response) + self.password_expire = getattr(settings, 'USER_PASSWORD_EXPIRATION', -1) def process_view(self, request, view, *args, **kwargs): newpassword_path = reverse('users.user_newpassword', args=[0]) logout_path = reverse('users.user_logout') - if (self.password_expire < 0 or not request.user.is_authenticated() or view == django.views.static.serve or request.path == newpassword_path or request.path == logout_path): # noqa + if (self.password_expire < 0 or not request.user.is_authenticated or view == django.views.static.serve or request.path == newpassword_path or request.path == logout_path): # noqa return if PasswordHistory.is_password_expired(request.user): diff --git a/smartmin/users/views.py b/smartmin/users/views.py index afe5523..3161ca1 100644 --- a/smartmin/users/views.py +++ b/smartmin/users/views.py @@ -12,7 +12,7 @@ from django.contrib.auth.models import Group from django.contrib.auth.views import login as django_login from django.core.mail import send_mail -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponseRedirect from django.template import loader from django.utils import timezone @@ -338,7 +338,7 @@ def get_context_data(self, *args, **kwargs): return context_data def has_permission(self, request, *args, **kwargs): - return request.user.is_authenticated() + return request.user.is_authenticated def get_object(self, queryset=None): return self.request.user @@ -469,4 +469,4 @@ def login(request, template_name='smartmin/users/login.html', return django_login(request, template_name='smartmin/users/login.html', redirect_field_name=REDIRECT_FIELD_NAME, authentication_form=AuthenticationForm, - current_app=None, extra_context=dict(allow_email_recovery=allow_email_recovery)) + extra_context=dict(allow_email_recovery=allow_email_recovery)) diff --git a/smartmin/views.py b/smartmin/views.py index 9b9f405..c25f563 100644 --- a/smartmin/views.py +++ b/smartmin/views.py @@ -11,7 +11,7 @@ from django.contrib import messages from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import IntegrityError from django.db.models import Q from django.http import HttpResponseRedirect, HttpResponse, JsonResponse diff --git a/test_runner/blog/migrations/0001_initial.py b/test_runner/blog/migrations/0001_initial.py index c1f71c4..9467b98 100644 --- a/test_runner/blog/migrations/0001_initial.py +++ b/test_runner/blog/migrations/0001_initial.py @@ -20,8 +20,8 @@ class Migration(migrations.Migration): ('created_on', models.DateTimeField(help_text='When this item was originally created', auto_now_add=True)), ('modified_on', models.DateTimeField(help_text='When this item was last modified', auto_now=True)), ('name', models.SlugField(help_text='The name of this category', unique=True, max_length=64)), - ('created_by', models.ForeignKey(related_name='blog_category_creations', to=settings.AUTH_USER_MODEL, help_text='The user which originally created this item')), - ('modified_by', models.ForeignKey(related_name='blog_category_modifications', to=settings.AUTH_USER_MODEL, help_text='The user which last modified this item')), + ('created_by', models.ForeignKey(related_name='blog_category_creations', to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT, help_text='The user which originally created this item')), + ('modified_by', models.ForeignKey(related_name='blog_category_modifications', to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT, help_text='The user which last modified this item')), ], options={ 'abstract': False, @@ -38,8 +38,8 @@ class Migration(migrations.Migration): ('body', models.TextField(help_text='The body of the post, go crazy')), ('order', models.IntegerField(help_text='The order for this post, posts with smaller orders come first')), ('tags', models.CharField(help_text='Any tags for this post', max_length=128)), - ('created_by', models.ForeignKey(related_name='blog_post_creations', to=settings.AUTH_USER_MODEL, help_text='The user which originally created this item')), - ('modified_by', models.ForeignKey(related_name='blog_post_modifications', to=settings.AUTH_USER_MODEL, help_text='The user which last modified this item')), + ('created_by', models.ForeignKey(related_name='blog_post_creations', to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT, help_text='The user which originally created this item')), + ('modified_by', models.ForeignKey(related_name='blog_post_modifications', to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT, help_text='The user which last modified this item')), ], options={ 'abstract': False, diff --git a/test_runner/blog/tests.py b/test_runner/blog/tests.py index 16bcbf9..76049ec 100644 --- a/test_runner/blog/tests.py +++ b/test_runner/blog/tests.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import User, Group from django.core import mail from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import TestCase from django.test.client import Client from django.test.utils import override_settings @@ -904,7 +904,7 @@ def test_lockout(self): # try to log in four times for i in range(4): response = self.client.post(login_url, post_data) - self.assertFalse(response.context['user'].is_authenticated()) + self.assertFalse(response.context['user'].is_authenticated) # on the fifth failed login we get redirected response = self.client.post(login_url, post_data) @@ -1050,14 +1050,14 @@ def doLockout(self): # on the fifth time it should fail response = self.client.post(reverse('users.user_login'), post_data, follow=True) - self.assertFalse(response.context['user'].is_authenticated()) + self.assertFalse(response.context['user'].is_authenticated) content = response.content.decode("utf-8") self.assertEqual(content.find(reverse('users.user_forget')), -1) # even with right password, no dice post_data = dict(username='plain', password='plain') response = self.client.post(reverse('users.user_login'), post_data, follow=True) - self.assertFalse(response.context['user'].is_authenticated()) + self.assertFalse(response.context['user'].is_authenticated) content = response.content.decode("utf-8") self.assertEqual(content.find(reverse('users.user_forget')), -1) @@ -1079,7 +1079,7 @@ def testNoRecovery(self): # should now be able to log in response = self.client.post(reverse('users.user_login'), post_data, follow=True) - self.assertTrue(response.context['user'].is_authenticated()) + self.assertTrue(response.context['user'].is_authenticated) def testNoRecoveryNoTimeout(self): with self.settings(USER_ALLOW_EMAIL_RECOVERY=False, USER_LOCKOUT_TIMEOUT=-1): @@ -1124,7 +1124,7 @@ def testNoRecoveryNoTimeout(self): post_data = dict(username='plain', password='Password1') response = self.client.post(reverse('users.user_login'), post_data, follow=True) - self.assertTrue(response.context['user'].is_authenticated()) + self.assertTrue(response.context['user'].is_authenticated) class PasswordExpirationTestCase(TestCase): @@ -1146,7 +1146,7 @@ def testNoExpiration(self): self.client.logout() post_data = dict(username='plain', password='Password1 ') response = self.client.post(reverse('users.user_login'), post_data, follow=True) - self.assertTrue(response.context['user'].is_authenticated()) + self.assertTrue(response.context['user'].is_authenticated) # we shouldn't be on a page asking us for a new password self.assertFalse('form' in response.context) diff --git a/test_runner/settings.py b/test_runner/settings.py index 3b92cb5..0a96a81 100644 --- a/test_runner/settings.py +++ b/test_runner/settings.py @@ -87,7 +87,7 @@ # Make this unique, and don't share it with anybody. SECRET_KEY = 'w4*mtn&nquc57h@$-05gva+2ucq0$tnczy#!d=t4%1&pl!p=jo' -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', diff --git a/test_runner/urls.py b/test_runner/urls.py index adb91e5..ee4a35d 100644 --- a/test_runner/urls.py +++ b/test_runner/urls.py @@ -10,5 +10,5 @@ url(r'^csv_imports/', include('smartmin.csv_imports.urls')), # Uncomment the next line to enable the admin: - url(r'^admin/', include(admin.site.urls)), + url(r'^admin/', admin.site.urls), ] From 94682d8243e4920c71057520a2116c21d66fa2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dra=C5=BEen=20Odoba=C5=A1i=C4=87?= Date: Mon, 16 Jul 2018 14:02:19 +0200 Subject: [PATCH 2/3] Update .travis.yml to exclude incompatible versions --- .travis.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f9b2c43..5a403e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,34 @@ language: python sudo: false + python: - - "2.7" - - "3.4" - - "3.5" - - "3.6" + - 2.7 + - 3.4 + - 3.5 + - 3.6 + env: - DJANGO_VERSION=1.10.8 - DJANGO_VERSION=1.11.14 - DJANGO_VERSION=2.0.7 + +matrix: + exclude: + - python: 2.7 + env: DJANGO_VERSION=2.0.7 + install: - pip install -q -r requirements/base.txt - pip install -q -r requirements/tests.txt - pip install -q django==$DJANGO_VERSION - pip install coveralls + before_script: - createdb -E UTF-8 dash -U postgres -O $USER + script: - coverage run manage.py test --settings=test_runner.settings_travis --verbosity=2 --noinput - flake8 + after_success: - coveralls From 913186dbd51ebc68d4b6204f549ec4e6bb125b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dra=C5=BEen=20Odoba=C5=A1i=C4=87?= Date: Mon, 16 Jul 2018 14:29:59 +0200 Subject: [PATCH 3/3] Prep for 2.0.0 release --- .travis.yml | 1 - CHANGES.md | 5 +++++ README.md | 2 +- requirements/base.txt | 2 +- smartmin/__init__.py | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a403e2..624fcb6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ python: - 3.6 env: - - DJANGO_VERSION=1.10.8 - DJANGO_VERSION=1.11.14 - DJANGO_VERSION=2.0.7 diff --git a/CHANGES.md b/CHANGES.md index 40af828..be5d4d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +2.0.0 (2018-07-16) +================== + +* Add support for Django 2.0, drop support for Django 1.9 and Django 1.10 + 1.11.9 (2018-05-3) =================== * Create SmartminTestMixin https://github.com/nyaruka/smartmin/pull/116 diff --git a/README.md b/README.md index e4b545e..9e4947b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Smartmin tries to stay in lock step with the latest Django versions. With each n will be released and we will save the major changes (possibly breaking backwards compatibility) on these versions. This includes updating to the latest version of Twitter Bootstrap. -The latest version is the 1.11.* series which works against Django 1.9, 1.10 and 1.11. +The latest version is the 2.0.* series which works against Django 1.11 and 2.0. About ===== diff --git a/requirements/base.txt b/requirements/base.txt index 399430e..2c37fd6 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ celery -django>=1.10,<2.1 +django>=1.11,<2.1 django_compressor pytz redis diff --git a/smartmin/__init__.py b/smartmin/__init__.py index a007e16..32495c8 100644 --- a/smartmin/__init__.py +++ b/smartmin/__init__.py @@ -1,3 +1,3 @@ from __future__ import unicode_literals -__version__ = '1.11.9' +__version__ = '2.0.0'