]> git.ipfire.org Git - thirdparty/patchwork.git/commitdiff
requirements: Bump Django to 3.2.x, djangorestframework to 4.16.0
authorStephen Finucane <stephen@that.guru>
Fri, 30 Sep 2022 12:17:46 +0000 (13:17 +0100)
committerStephen Finucane <stephen@that.guru>
Fri, 30 Sep 2022 16:09:38 +0000 (17:09 +0100)
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@that.guru>
patchwork/forms.py
patchwork/settings/base.py
patchwork/tests/views/test_mail.py
patchwork/tests/views/test_patch.py
patchwork/tests/views/test_user.py
releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml [new file with mode: 0644]
tox.ini

index 844485862fe01209251b088706bd8be42b2a8971..1c5a29bee40c23ee7930a8dca763f17e429c6f4f 100644 (file)
@@ -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)
index 045f262f84ee3e727f3c5a5000aa2965965612b2..965c949f5a7ce851e78358ec89c2a6b40b124be9 100644 (file)
@@ -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'
index de9df3d2859fa2efc660914041dec3c4fbbc04d2..ae0b2c38853b2a1c9cc822b3e542e25002684053 100644 (file)
@@ -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@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)
index 9cea8a0b231c3cdd523f1b4502e2b4d790346cc8..d1de8ec973b81cebc42dfaaa924c52ddd3af692e 100644 (file)
@@ -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()
index 1f74ad50e5c9bf93525aaf3130e4285178996b62..8ab916706554196aae15450c0a0cf9f1200db294 100644 (file)
@@ -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 a/releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml b/releasenotes/notes/django-4-1-support-bcbe65a71d235b43.yaml
new file mode 100644 (file)
index 0000000..3dcab1b
--- /dev/null
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    `Django 4.1 <https://docs.djangoproject.com/en/dev/releases/4.1/>`_ is
+    now supported.
diff --git a/tox.ini b/tox.ini
index aa434a7262e561fdb3a491274f65795915f3dcae..f243faca05678ab620ac329e58c47a4f353b3e2a 100644 (file)
--- a/tox.ini
+++ b/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