Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Upgrade to Django 3.2 #5629

Merged
merged 25 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
00118f7
chore: Update requirements.txt for Django 3.2
jennifer-richards May 12, 2023
aa4c041
chore: Use re_path() instead of url(), its deprecated synonym,
jennifer-richards May 12, 2023
87fdfaa
chore: Update django-tastypie requirement
jennifer-richards May 12, 2023
828071a
chore: Unpin oidc-provider, update its patch
jennifer-richards May 12, 2023
ebd28cd
chore: Update fix-oidc-access-token-post.patch
jennifer-richards May 12, 2023
0927615
chore: Update tastypie-django22-fielderror-response.patch
jennifer-richards May 12, 2023
4cfe1c1
chore: Update django-cookie-delete-with-all-settings.patch
jennifer-richards May 12, 2023
9d21196
chore: Add DEFAULT_AUTO_FIELD to settings.py
jennifer-richards May 12, 2023
075aed7
refactor: Replace deprecated request.is_ajax()
jennifer-richards May 12, 2023
b5d9e9b
refactor: Replace deprecated django.utils.http.urlquote
jennifer-richards May 12, 2023
bee7e11
chore: Remove accidental import from settings.py
jennifer-richards May 12, 2023
32ed1b7
refactor: Replace deprecated {% ifequal %} with {% if %}
jennifer-richards May 12, 2023
2100486
refactor: Replace deprecated is_safe_url with new name
jennifer-richards May 12, 2023
79b749f
style: Restyle method using Black
jennifer-richards May 12, 2023
872bdef
refactor: Use gettext instead of deprecated ugettext
jennifer-richards May 12, 2023
00f3f01
fix: {% endif %}, not {% end %}
jennifer-richards May 12, 2023
587bc4d
test: Remove outdated mypy test exceptions
jennifer-richards May 12, 2023
7ad74c9
refactor: import from django.urls instead of django.conf.urls
jennifer-richards May 12, 2023
68eb685
test: Use django_stubs_ext.QuerySetAny for isinstance() checks
jennifer-richards May 13, 2023
c840d53
test: Suppress mypy error on import of _lazy_re_compile()
jennifer-richards May 15, 2023
cbb9464
test: Remove unused assignment that caused a mypy error
jennifer-richards May 15, 2023
22bf508
test: Suppress notices from mypy involving factory types
jennifer-richards May 15, 2023
0319f35
test: Use Django 3.2 HttpResponse.headers API
jennifer-richards May 15, 2023
329fa26
chore: Remove abandoned django-password-strength package
jennifer-richards May 15, 2023
b714bfb
chore: Put widgets from django-password-strength into ietfauth
jennifer-richards May 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions ietf/api/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
from django.http import HttpResponse
from django.utils.encoding import smart_str
from django.db.models import Field
from django.db.models.query import QuerySet
from django.db.models.signals import post_save, post_delete, m2m_changed

from django_stubs_ext import QuerySetAny

import debug # pyflakes:ignore


Expand Down Expand Up @@ -145,7 +146,7 @@ def end_object(self, obj):
field_value = None
else:
field_value = field
if isinstance(field_value, QuerySet) or isinstance(field_value, list):
if isinstance(field_value, QuerySetAny) or isinstance(field_value, list):
self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field_value ])
else:
if hasattr(field_value, "_meta"):
Expand Down
2 changes: 1 addition & 1 deletion ietf/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright The IETF Trust 2017, All Rights Reserved

from django.conf import settings
from django.conf.urls import include
from django.urls import include
from django.views.generic import TemplateView

from ietf import api
Expand Down
5 changes: 3 additions & 2 deletions ietf/community/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from ietf.community.utils import states_of_significant_change, reset_name_contains_index_for_rule
from ietf.doc.models import DocEvent, Document
from ietf.doc.utils_search import prepare_document_table
from ietf.utils.http import is_ajax
from ietf.utils.response import permission_denied

def view_list(request, username=None):
Expand Down Expand Up @@ -142,7 +143,7 @@ def track_document(request, name, username=None, acronym=None):
if not doc in clist.added_docs.all():
clist.added_docs.add(doc)

if request.is_ajax():
if is_ajax(request):
return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
else:
return HttpResponseRedirect(clist.get_absolute_url())
Expand All @@ -162,7 +163,7 @@ def untrack_document(request, name, username=None, acronym=None):
if clist.pk is not None:
clist.added_docs.remove(doc)

if request.is_ajax():
if is_ajax(request):
return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
else:
return HttpResponseRedirect(clist.get_absolute_url())
Expand Down
2 changes: 1 addition & 1 deletion ietf/doc/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1779,7 +1779,7 @@ def test_document_ballot_needed_positions(self):
self.client.login(username='ad', password='ad+password')
r = self.client.post(urlreverse('ietf.doc.views_status_change.change_state',kwargs=dict(name=doc.name)),dict(new_state=iesgeval_pk))
self.assertEqual(r.status_code, 302)
r = self.client.get(r._headers["location"][1])
r = self.client.get(r.headers["location"])
self.assertContains(r, ">IESG Evaluation<")
self.assertEqual(len(outbox), 2)
self.assertIn('iesg-secretary',outbox[0]['To'])
Expand Down
4 changes: 2 additions & 2 deletions ietf/doc/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


from django.conf.urls import include
from django.views.generic import RedirectView
from django.conf import settings
from django.urls import include
from django.views.generic import RedirectView

from ietf.doc import views_search, views_draft, views_ballot, views_status_change, views_doc, views_downref, views_stats, views_help, views_bofreq
from ietf.utils.urls import url
Expand Down
18 changes: 12 additions & 6 deletions ietf/doc/views_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import email.utils

from django.utils import timezone
from django.utils.http import is_safe_url
from django.utils.http import url_has_allowed_host_and_scheme

from simple_history.utils import update_change_reason

Expand Down Expand Up @@ -53,6 +53,7 @@
from ietf.utils.mail import send_mail_message
from ietf.mailtrigger.utils import gather_address_lists
from ietf.utils.fields import MultiEmailField
from ietf.utils.http import is_ajax
from ietf.utils.response import permission_denied
from ietf.utils.timezone import date_today, DEADLINE_TZINFO

Expand Down Expand Up @@ -1087,11 +1088,16 @@ def review_wishes_remove(request, name):


def _generate_ajax_or_redirect_response(request, doc):
redirect_url = request.GET.get('next')
url_is_safe = is_safe_url(url=redirect_url, allowed_hosts=request.get_host(),
require_https=request.is_secure())
if request.is_ajax():
return HttpResponse(json.dumps({'success': True}), content_type='application/json')
redirect_url = request.GET.get("next")
url_is_safe = url_has_allowed_host_and_scheme(
url=redirect_url,
allowed_hosts=request.get_host(),
require_https=request.is_secure(),
)
if is_ajax(request):
return HttpResponse(
json.dumps({"success": True}), content_type="application/json"
)
elif url_is_safe:
return HttpResponseRedirect(redirect_url)
else:
Expand Down
1 change: 0 additions & 1 deletion ietf/doc/views_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

epochday = datetime.datetime.utcfromtimestamp(0).date().toordinal()

column_chart_conf = settings.CHART_TYPE_COLUMN_OPTIONS

def dt(s):
"Convert the date string returned by sqlite's date() to a datetime.date"
Expand Down
2 changes: 1 addition & 1 deletion ietf/group/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from django.shortcuts import render
from django.utils.encoding import force_str
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _

from ietf.group.models import (Group, GroupFeatures, GroupHistory, GroupEvent, GroupURL, GroupMilestone,
GroupMilestoneHistory, GroupStateTransitions, Role, RoleHistory, ChangeStateGroupEvent,
Expand Down
2 changes: 1 addition & 1 deletion ietf/group/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright The IETF Trust 2013-2020, All Rights Reserved

from django.conf import settings
from django.conf.urls import include
from django.urls import include
from django.views.generic import RedirectView

from ietf.community import views as community_views
Expand Down
4 changes: 2 additions & 2 deletions ietf/ietfauth/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
from django.db import models
from django.contrib.auth.models import User

from django_password_strength.widgets import PasswordStrengthInput, PasswordConfirmationInput

import debug # pyflakes:ignore

from ietf.person.models import Person, Email
from ietf.mailinglists.models import Allowlisted
from ietf.utils.text import isascii

from .widgets import PasswordStrengthInput, PasswordConfirmationInput


class RegistrationForm(forms.Form):
email = forms.EmailField(label="Your email (lowercase)")
Expand Down
2 changes: 1 addition & 1 deletion ietf/ietfauth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@


from functools import wraps, WRAPPER_ASSIGNMENTS
from urllib.parse import quote as urlquote

from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.utils.http import urlquote

import debug # pyflakes:ignore

Expand Down
114 changes: 114 additions & 0 deletions ietf/ietfauth/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from django.forms import PasswordInput
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _

# The PasswordStrengthInput and PasswordConfirmationInput widgets come from the
# django-password-strength project, https://pypi.org/project/django-password-strength/
#
# Original license:
#
# Copyright &copy; 2015 A.J. May and individual contributors. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
# products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

class PasswordStrengthInput(PasswordInput):
"""
Form widget to show the user how strong his/her password is.
"""

def render(self, name, value, attrs=None, renderer=None):
strength_markup = """
<div style="margin-top: 10px;">
<div class="progress" style="margin-bottom: 10px;">
<div class="progress-bar progress-bar-warning password_strength_bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="5" style="width: 0%%"></div>
</div>
<p class="text-muted password_strength_info hidden">
<span class="label label-danger">
%s
</span>
<span style="margin-left:5px;">
%s
</span>
</p>
</div>
""" % (
_("Warning"),
_(
'This password would take <em class="password_strength_time"></em> to crack.'
),
)

try:
self.attrs["class"] = "%s password_strength".strip() % self.attrs["class"]
except KeyError:
self.attrs["class"] = "password_strength"

return mark_safe(
super(PasswordInput, self).render(name, value, attrs, renderer)
+ strength_markup
)

class Media:
js = (
"ietf/js/zxcvbn.js",
"ietf/js/password_strength.js",
)


class PasswordConfirmationInput(PasswordInput):
"""
Form widget to confirm the users password by letting him/her type it again.
"""

def __init__(self, confirm_with=None, attrs=None, render_value=False):
super(PasswordConfirmationInput, self).__init__(attrs, render_value)
self.confirm_with = confirm_with

def render(self, name, value, attrs=None, renderer=None):
if self.confirm_with:
self.attrs["data-confirm-with"] = "id_%s" % self.confirm_with

confirmation_markup = """
<div style="margin-top: 10px;" class="hidden password_strength_info">
<p class="text-muted">
<span class="label label-danger">
%s
</span>
<span style="margin-left:5px;">%s</span>
</p>
</div>
""" % (
_("Warning"),
_("Your passwords don't match."),
)

try:
self.attrs["class"] = (
"%s password_confirmation".strip() % self.attrs["class"]
)
except KeyError:
self.attrs["class"] = "password_confirmation"

return mark_safe(
super(PasswordInput, self).render(name, value, attrs, renderer)
+ confirmation_markup
)
4 changes: 2 additions & 2 deletions ietf/liaisons/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
from django import forms
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db.models.query import QuerySet
from django.forms.utils import ErrorList
from django.db.models import Q
#from django.forms.widgets import RadioFieldRenderer
from django.core.validators import validate_email
from django_stubs_ext import QuerySetAny

import debug # pyflakes:ignore

Expand Down Expand Up @@ -203,7 +203,7 @@ def get_results(self):
class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField):
'''If value is a QuerySet, return it as is (for use in widget.render)'''
def prepare_value(self, value):
if isinstance(value, QuerySet):
if isinstance(value, QuerySetAny):
return value
if (hasattr(value, '__iter__') and
not isinstance(value, str) and
Expand Down
7 changes: 4 additions & 3 deletions ietf/liaisons/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@


from django.urls import reverse as urlreverse
from django.db.models.query import QuerySet
from django.forms.widgets import Widget
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape

from django_stubs_ext import QuerySetAny


class ButtonWidget(Widget):
def __init__(self, *args, **kwargs):
Expand All @@ -34,7 +35,7 @@ def render(self, name, value, **kwargs):
html = '<div id="id_%s">' % name
html += '<span class="d-none showAttachmentsEmpty form-control widget">No files attached</span>'
html += '<div class="attachedFiles form-control widget">'
if value and isinstance(value, QuerySet):
if value and isinstance(value, QuerySetAny):
for attachment in value:
html += '<a class="initialAttach" href="%s">%s</a>&nbsp' % (conditional_escape(attachment.document.get_href()), conditional_escape(attachment.document.title))
html += '<a class="btn btn-primary btn-sm" href="{}">Edit</a>&nbsp'.format(urlreverse("ietf.liaisons.views.liaison_edit_attachment", kwargs={'object_id':attachment.statement.pk,'doc_id':attachment.document.pk}))
Expand All @@ -43,4 +44,4 @@ def render(self, name, value, **kwargs):
else:
html += 'No files attached'
html += '</div></div>'
return mark_safe(html)
return mark_safe(html)
10 changes: 5 additions & 5 deletions ietf/meeting/tests_js.py
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
"""Test the timeslot editor"""
def setUp(self):
super().setUp()
self.meeting: Meeting = MeetingFactory(
self.meeting: Meeting = MeetingFactory( # type: ignore[annotation-unchecked]
type_id='ietf',
number=120,
date=date_today() + datetime.timedelta(days=10),
Expand Down Expand Up @@ -1570,13 +1570,13 @@ def do_delete_time_interval_test(self, cancel=False):
delete_time = delete_time_local.astimezone(datetime.timezone.utc)
duration = datetime.timedelta(minutes=60)

delete: [TimeSlot] = TimeSlotFactory.create_batch(
delete: [TimeSlot] = TimeSlotFactory.create_batch( # type: ignore[annotation-unchecked]
2,
meeting=self.meeting,
time=delete_time_local,
duration=duration,
)
keep: [TimeSlot] = [
keep: [TimeSlot] = [ # type: ignore[annotation-unchecked]
TimeSlotFactory(
meeting=self.meeting,
time=keep_time,
Expand Down Expand Up @@ -1613,14 +1613,14 @@ def do_delete_day_test(self, cancel=False):
hours = [10, 12]
other_days = [self.meeting.get_meeting_date(d) for d in range(1, 3)]

delete: [TimeSlot] = [
delete: [TimeSlot] = [ # type: ignore[annotation-unchecked]
TimeSlotFactory(
meeting=self.meeting,
time=datetime_from_date(delete_day, self.meeting.tz()).replace(hour=hour),
) for hour in hours
]

keep: [TimeSlot] = [
keep: [TimeSlot] = [ # type: ignore[annotation-unchecked]
TimeSlotFactory(
meeting=self.meeting,
time=datetime_from_date(day, self.meeting.tz()).replace(hour=hour),
Expand Down
Loading