[PATCH 04/10] requirements: Bump Django to 3.2.x, djangorestframework to 4.16.0
Stephen Finucane
stephen at that.guru
Sat Oct 1 02:19:15 AEST 2022
There are two issues to be addressed:
RemovedInDjango50Warning: Passing response to assertFormError() is
deprecated. Use the form object directly:
RemovedInDjango50Warning: The "default.html" templates for forms and
formsets will be removed. These were proxies to the equivalent
"table.html" templates, but the new "div.html" templates will be the
default from Django 5.0. Transitional renderers are provided to allow
you to opt-in to the new output style now. See
https://docs.djangoproject.com/en/4.1/releases/4.1/ for more details
Nothing complicated in fixing either of these. For the former, we must
do as we're told and use the form object directly. For the latter, we
need to configure our own form renderer so we can continue using the
table form renderer for now.
Signed-off-by: Stephen Finucane <stephen at that.guru>
---
patchwork/forms.py | 8 ++
patchwork/settings/base.py | 2 +
patchwork/tests/views/test_mail.py | 91 +++++++++++++---
patchwork/tests/views/test_patch.py | 23 ++--
patchwork/tests/views/test_user.py | 102 ++++++++++++++----
.../django-4-1-support-bcbe65a71d235b43.yaml | 5 +
tox.ini | 9 +-
7 files changed, 197 insertions(+), 43 deletions(-)
create mode 100644 releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml
diff --git patchwork/forms.py patchwork/forms.py
index 84448586..1c5a29be 100644
--- patchwork/forms.py
+++ patchwork/forms.py
@@ -5,8 +5,10 @@
from django.contrib.auth.models import User
from django import forms
+from django.forms import renderers
from django.db.models import Q
from django.db.utils import ProgrammingError
+from django.template.backends import django as django_template_backend
from patchwork.models import Bundle
from patchwork.models import Patch
@@ -14,6 +16,12 @@ from patchwork.models import State
from patchwork.models import UserProfile
+class PatchworkTableRenderer(renderers.EngineMixin, renderers.BaseRenderer):
+ backend = django_template_backend.DjangoTemplates
+ form_template_name = 'django/forms/table.html'
+ formset_template_name = 'django/forms/formsets/table.html'
+
+
class RegistrationForm(forms.Form):
first_name = forms.CharField(max_length=30, required=False)
last_name = forms.CharField(max_length=30, required=False)
diff --git patchwork/settings/base.py patchwork/settings/base.py
index 045f262f..965c949f 100644
--- patchwork/settings/base.py
+++ patchwork/settings/base.py
@@ -71,6 +71,8 @@ TEMPLATES = [
},
]
+FORM_RENDERER = 'patchwork.forms.PatchworkTableRenderer'
+
# TODO(stephenfin): Consider changing to BigAutoField when we drop support for
# Django < 3.2
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
diff --git patchwork/tests/views/test_mail.py patchwork/tests/views/test_mail.py
index de9df3d2..ae0b2c38 100644
--- patchwork/tests/views/test_mail.py
+++ patchwork/tests/views/test_mail.py
@@ -5,6 +5,7 @@
import re
+import django
from django.core import mail
from django.test import TestCase
from django.urls import reverse
@@ -33,15 +34,37 @@ class MailSettingsTest(TestCase):
response = self.client.post(reverse('mail-settings'), {'email': ''})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/mail.html')
- self.assertFormError(
- response, 'form', 'email', 'This field is required.'
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ 'This field is required.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ 'This field is required.',
+ )
def test_post_invalid(self):
response = self.client.post(reverse('mail-settings'), {'email': 'foo'})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'patchwork/mail.html')
- self.assertFormError(response, 'form', 'email', error_strings['email'])
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ error_strings['email'],
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ error_strings['email'],
+ )
def test_post_optin(self):
email = 'foo at example.com'
@@ -91,9 +114,19 @@ class OptoutRequestTest(TestCase):
def test_post_empty(self):
response = self.client.post(reverse('mail-optout'), {'email': ''})
self.assertEqual(response.status_code, 200)
- self.assertFormError(
- response, 'form', 'email', 'This field is required.'
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ 'This field is required.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ 'This field is required.',
+ )
self.assertTrue(response.context['error'])
self.assertNotIn('confirmation', response.context)
self.assertEqual(len(mail.outbox), 0)
@@ -101,7 +134,19 @@ class OptoutRequestTest(TestCase):
def test_post_non_email(self):
response = self.client.post(reverse('mail-optout'), {'email': 'foo'})
self.assertEqual(response.status_code, 200)
- self.assertFormError(response, 'form', 'email', error_strings['email'])
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ error_strings['email'],
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ error_strings['email'],
+ )
self.assertTrue(response.context['error'])
self.assertNotIn('confirmation', response.context)
self.assertEqual(len(mail.outbox), 0)
@@ -172,9 +217,19 @@ class OptinRequestTest(TestCase):
def test_post_empty(self):
response = self.client.post(reverse('mail-optin'), {'email': ''})
self.assertEqual(response.status_code, 200)
- self.assertFormError(
- response, 'form', 'email', 'This field is required.'
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ 'This field is required.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ 'This field is required.',
+ )
self.assertTrue(response.context['error'])
self.assertNotIn('confirmation', response.context)
self.assertEqual(len(mail.outbox), 0)
@@ -182,7 +237,19 @@ class OptinRequestTest(TestCase):
def test_post_non_email(self):
response = self.client.post(reverse('mail-optin'), {'email': 'foo'})
self.assertEqual(response.status_code, 200)
- self.assertFormError(response, 'form', 'email', error_strings['email'])
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ error_strings['email'],
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ error_strings['email'],
+ )
self.assertTrue(response.context['error'])
self.assertNotIn('confirmation', response.context)
self.assertEqual(len(mail.outbox), 0)
diff --git patchwork/tests/views/test_patch.py patchwork/tests/views/test_patch.py
index 9cea8a0b..d1de8ec9 100644
--- patchwork/tests/views/test_patch.py
+++ patchwork/tests/views/test_patch.py
@@ -8,6 +8,7 @@ from datetime import timedelta
import re
import unittest
+import django
from django.conf import settings
from django.test import TestCase
from django.urls import reverse
@@ -460,13 +461,21 @@ class PatchUpdateTest(TestCase):
new_states = [Patch.objects.get(pk=p.pk).state for p in self.patches]
self.assertEqual(new_states, orig_states)
- self.assertFormError(
- response,
- 'patchform',
- 'state',
- 'Select a valid choice. That choice is not one '
- 'of the available choices.',
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['patchform'],
+ 'state',
+ 'Select a valid choice. That choice is not one '
+ 'of the available choices.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'patchform',
+ 'state',
+ 'Select a valid choice. That choice is not one '
+ 'of the available choices.',
+ )
def _test_delegate_change(self, delegate_str):
data = self.base_data.copy()
diff --git patchwork/tests/views/test_user.py patchwork/tests/views/test_user.py
index 1f74ad50..8ab91670 100644
--- patchwork/tests/views/test_user.py
+++ patchwork/tests/views/test_user.py
@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import django
from django.contrib.auth.models import User
from django.core import mail
from django.test.client import Client
@@ -70,14 +71,38 @@ class RegistrationTest(TestCase):
del data[field]
response = self.client.post('/register/', data)
self.assertEqual(response.status_code, 200)
- self.assertFormError(response, 'form', field, self.required_error)
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ field,
+ self.required_error,
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ field,
+ self.required_error,
+ )
def test_invalid_username(self):
data = self.default_data.copy()
data['username'] = 'invalid user'
response = self.client.post('/register/', data)
self.assertEqual(response.status_code, 200)
- self.assertFormError(response, 'form', 'username', self.invalid_error)
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'username',
+ self.invalid_error,
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'username',
+ self.invalid_error,
+ )
def test_existing_username(self):
user = create_user()
@@ -85,12 +110,19 @@ class RegistrationTest(TestCase):
data['username'] = user.username
response = self.client.post('/register/', data)
self.assertEqual(response.status_code, 200)
- self.assertFormError(
- response,
- 'form',
- 'username',
- 'This username is already taken. Please choose another.',
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'username',
+ 'This username is already taken. Please choose another.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'username',
+ 'This username is already taken. Please choose another.',
+ )
def test_existing_email(self):
user = create_user()
@@ -98,13 +130,21 @@ class RegistrationTest(TestCase):
data['email'] = user.email
response = self.client.post('/register/', data)
self.assertEqual(response.status_code, 200)
- self.assertFormError(
- response,
- 'form',
- 'email',
- 'This email address is already in use for the account '
- '"%s".\n' % user.username,
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['form'],
+ 'email',
+ 'This email address is already in use for the account '
+ '"%s".\n' % user.username,
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'form',
+ 'email',
+ 'This email address is already in use for the account '
+ '"%s".\n' % user.username,
+ )
def test_valid_registration(self):
response = self.client.post('/register/', self.default_data)
@@ -255,17 +295,37 @@ class UserLinkTest(_UserTestCase):
response = self.client.post(reverse('user-link'), {'email': ''})
self.assertEqual(response.status_code, 200)
self.assertTrue(response.context['linkform'])
- self.assertFormError(
- response, 'linkform', 'email', 'This field is required.'
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['linkform'],
+ 'email',
+ 'This field is required.',
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'linkform',
+ 'email',
+ 'This field is required.',
+ )
def test_user_person_request_invalid(self):
response = self.client.post(reverse('user-link'), {'email': 'foo'})
self.assertEqual(response.status_code, 200)
self.assertTrue(response.context['linkform'])
- self.assertFormError(
- response, 'linkform', 'email', error_strings['email']
- )
+ if django.VERSION >= (4, 1):
+ self.assertFormError(
+ response.context['linkform'],
+ 'email',
+ error_strings['email'],
+ )
+ else:
+ self.assertFormError(
+ response,
+ 'linkform',
+ 'email',
+ error_strings['email'],
+ )
def test_user_person_request_valid(self):
response = self.client.post(
diff --git releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml
new file mode 100644
index 00000000..3dcab1bd
--- /dev/null
+++ releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ `Django 4.1 <https://docs.djangoproject.com/en/dev/releases/4.1/>`_ is
+ now supported.
diff --git tox.ini tox.ini
index aa434a72..f243faca 100644
--- tox.ini
+++ tox.ini
@@ -1,6 +1,6 @@
[tox]
minversion = 3.2
-envlist = pep8,docs,py{37,38,39}-django32,py{38,39,310}-django{40}
+envlist = pep8,docs,py{37,38,39}-django32,py{38,39,310}-django{40,41}
skipsdist = true
ignore_basepython_conflict = true
@@ -9,11 +9,14 @@ basepython = python3
deps =
-r{toxinidir}/requirements-test.txt
django32: django~=3.2.0
- django32: djangorestframework~=3.13.0
+ django32: djangorestframework~=3.14.0
django32: django-filter~=22.1.0
django40: django~=4.0.0
- django40: djangorestframework~=3.13.0
+ django40: djangorestframework~=3.14.0
django40: django-filter~=22.1.0
+ django41: django~=4.1.0
+ django41: djangorestframework~=3.14.0
+ django41: django-filter~=22.1.0
setenv =
DJANGO_SETTINGS_MODULE = patchwork.settings.dev
PYTHONDONTWRITEBYTECODE = 1
--
2.37.3
More information about the Patchwork
mailing list