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

Django 4.0.0 compatibility #2009

Merged
merged 6 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ jobs:
- tox-env: "py310-dj32"
python-version: "3.10"
# Django 4.0
# - tox-env: "py38-dj40"
# python-version: "3.8"
# - tox-env: "py39-dj40"
# python-version: "3.9"
# - tox-env: "py310-dj40"
# python-version: "3.10"
- tox-env: "py38-dj40"
python-version: "3.8"
- tox-env: "py39-dj40"
python-version: "3.9"
- tox-env: "py310-dj40"
python-version: "3.10"

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ standard Django environment), with the following dependencies, which
unless noted as optional, should be installed automatically following
the above instructions:

* `Python`_ 3.6 to 3.9
* `Django`_ 2.2 to 3.2
* `Python`_ 3.6 to 3.10
* `Django`_ 2.2 to 4.0
* `django-contrib-comments`_ - for built-in threaded comments
* `Pillow`_ - for image resizing (`Python Imaging Library`_ fork)
* `grappelli-safe`_ - admin skin (`Grappelli`_ fork)
Expand Down
26 changes: 15 additions & 11 deletions mezzanine/accounts/urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.conf.urls import url
from django.urls import re_path

from mezzanine.accounts import views
from mezzanine.conf import settings
Expand Down Expand Up @@ -28,32 +28,36 @@
_slash = "/" if settings.APPEND_SLASH else ""

urlpatterns = [
url(r"^{}{}$".format(LOGIN_URL.strip("/"), _slash), views.login, name="login"),
url(r"^{}{}$".format(LOGOUT_URL.strip("/"), _slash), views.logout, name="logout"),
url(r"^{}{}$".format(SIGNUP_URL.strip("/"), _slash), views.signup, name="signup"),
url(
re_path(r"^{}{}$".format(LOGIN_URL.strip("/"), _slash), views.login, name="login"),
re_path(
r"^{}{}$".format(LOGOUT_URL.strip("/"), _slash), views.logout, name="logout"
),
re_path(
r"^{}{}$".format(SIGNUP_URL.strip("/"), _slash), views.signup, name="signup"
),
re_path(
r"^{}{}{}$".format(SIGNUP_VERIFY_URL.strip("/"), _verify_pattern, _slash),
views.signup_verify,
name="signup_verify",
),
url(
re_path(
r"^{}{}$".format(PROFILE_UPDATE_URL.strip("/"), _slash),
views.profile_update,
name="profile_update",
),
url(
re_path(
r"^{}{}$".format(PASSWORD_RESET_URL.strip("/"), _slash),
views.password_reset,
name="mezzanine_password_reset",
),
url(
re_path(
r"^{}{}{}$".format(
PASSWORD_RESET_VERIFY_URL.strip("/"), _verify_pattern, _slash
),
views.password_reset_verify,
name="password_reset_verify",
),
url(
re_path(
r"^{}{}$".format(ACCOUNT_URL.strip("/"), _slash),
views.account_redirect,
name="account_redirect",
Expand All @@ -62,12 +66,12 @@

if settings.ACCOUNTS_PROFILE_VIEWS_ENABLED:
urlpatterns += [
url(
re_path(
r"^{}{}$".format(PROFILE_URL.strip("/"), _slash),
views.profile_redirect,
name="profile_redirect",
),
url(
re_path(
r"^{}/(?P<username>.*){}$".format(PROFILE_URL.strip("/"), _slash),
views.profile,
name="profile",
Expand Down
19 changes: 10 additions & 9 deletions mezzanine/boot/lazy_admin.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls import include
from django.contrib.admin.sites import AdminSite, AlreadyRegistered, NotRegistered
from django.contrib.admin.sites import site as default_site
from django.contrib.auth import get_user_model
from django.shortcuts import redirect
from django.urls import re_path

from mezzanine.utils.importing import import_dotted_path

Expand Down Expand Up @@ -71,12 +72,12 @@ def urls(self):
# doesn't provide), so that we can target it in the
# ADMIN_MENU_ORDER setting, allowing each view to correctly
# highlight its left-hand admin nav item.
url(
re_path(
r"^media-library/$",
lambda r: redirect("fb_browse"),
name="media-library",
),
url(r"^media-library/", include(fb_urls)),
re_path(r"^media-library/", include(fb_urls)),
]

# Give the urlpattern for the user password change view an
Expand All @@ -88,7 +89,7 @@ def urls(self):
if user_change_password:
bits = (User._meta.app_label, User._meta.object_name.lower())
urls += [
url(
re_path(
r"^%s/%s/(\d+)/password/$" % bits,
self.admin_view(user_change_password),
name="user_change_password",
Expand All @@ -102,13 +103,13 @@ def urls(self):
from mezzanine.generic.views import admin_keywords_submit

urls += [
url(
re_path(
r"^admin_keywords_submit/$",
admin_keywords_submit,
name="admin_keywords_submit",
),
url(r"^asset_proxy/$", static_proxy, name="static_proxy"),
url(
re_path(r"^asset_proxy/$", static_proxy, name="static_proxy"),
re_path(
r"^displayable_links.js$",
displayable_links_js,
name="displayable_links_js",
Expand All @@ -118,11 +119,11 @@ def urls(self):
from mezzanine.pages.views import admin_page_ordering

urls += [
url(
re_path(
r"^admin_page_ordering/$",
admin_page_ordering,
name="admin_page_ordering",
)
]

return urls + [url(r"", super().urls)]
return urls + [re_path(r"", super().urls)]
4 changes: 2 additions & 2 deletions mezzanine/conf/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.contrib import admin
from django.contrib.messages import info
from django.http import HttpResponseRedirect
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _

from mezzanine.conf import settings
Expand Down Expand Up @@ -53,7 +53,7 @@ def changelist_view(self, request, extra_context=None):
extra_context["settings_form"] = settings_form
extra_context["title"] = "{} {}".format(
_("Change"),
force_text(Setting._meta.verbose_name_plural),
force_str(Setting._meta.verbose_name_plural),
)
return super().changelist_view(request, extra_context)

Expand Down
6 changes: 3 additions & 3 deletions mezzanine/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def __init__(self, *args, **kwargs):

@property
def base_concrete_modeladmin(self):
""" The class inheriting directly from ContentModelAdmin. """
"""The class inheriting directly from ContentModelAdmin."""
candidates = [self.__class__]
while candidates:
candidate = candidates.pop()
Expand Down Expand Up @@ -368,7 +368,7 @@ def change_view(self, request, object_id, **kwargs):
return super().change_view(request, object_id, **kwargs)

def changelist_view(self, request, extra_context=None):
""" Redirect to the changelist view for subclasses. """
"""Redirect to the changelist view for subclasses."""
if self.model is not self.concrete_model:
return HttpResponseRedirect(admin_url(self.concrete_model, "changelist"))

Expand All @@ -378,7 +378,7 @@ def changelist_view(self, request, extra_context=None):
return super().changelist_view(request, extra_context)

def get_content_models(self):
""" Return all subclasses that are admin registered. """
"""Return all subclasses that are admin registered."""
models = []

for model in self.concrete_model.get_content_models():
Expand Down
8 changes: 6 additions & 2 deletions mezzanine/core/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@
nevercache_token,
)
from mezzanine.utils.conf import middlewares_or_subclasses_installed
from mezzanine.utils.deprecation import MiddlewareMixin, is_authenticated
from mezzanine.utils.deprecation import (
MiddlewareMixin,
get_middleware_request,
is_authenticated,
)
from mezzanine.utils.sites import current_site_id
from mezzanine.utils.urls import next_url

Expand Down Expand Up @@ -207,7 +211,7 @@ def process_response(self, request, response):
# the cookie will be correctly set for the the response
if csrf_middleware_installed():
response.csrf_processing_done = False
csrf_mw = CsrfViewMiddleware()
csrf_mw = CsrfViewMiddleware(get_middleware_request)
csrf_mw.process_response(request, response)
return response

Expand Down
2 changes: 1 addition & 1 deletion mezzanine/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ def get_content_model_name(cls):

@classmethod
def get_content_models(cls):
""" Return all subclasses of the concrete model. """
"""Return all subclasses of the concrete model."""
concrete_model = base_concrete_model(ContentTyped, cls)
return [
m
Expand Down
10 changes: 5 additions & 5 deletions mezzanine/forms/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from mimetypes import guess_type
from os.path import join

from django.conf.urls import url
from django.contrib import admin
from django.contrib.messages import info
from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import re_path
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ungettext
from django.utils.translation import ngettext

from mezzanine.conf import settings
from mezzanine.core.admin import TabularDynamicInlineAdmin
Expand Down Expand Up @@ -112,12 +112,12 @@ def get_urls(self):
"""
urls = super().get_urls()
extra_urls = [
url(
re_path(
r"^(?P<form_id>\d+)/entries/$",
self.admin_site.admin_view(self.entries_view),
name="form_entries",
),
url(
re_path(
r"^file/(?P<field_entry_id>\d+)/$",
self.admin_site.admin_view(self.file_view),
name="form_file",
Expand Down Expand Up @@ -170,7 +170,7 @@ def entries_view(self, request, form_id):
count = entries.count()
if count > 0:
entries.delete()
message = ungettext(
message = ngettext(
"1 entry deleted", "%(count)s entries deleted", count
)
info(request, message % {"count": count})
Expand Down
4 changes: 2 additions & 2 deletions mezzanine/forms/signals.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.dispatch import Signal

form_invalid = Signal(providing_args=["form"])
form_valid = Signal(providing_args=["form", "entry"])
form_invalid = Signal()
form_valid = Signal()
4 changes: 2 additions & 2 deletions mezzanine/galleries/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.db import models
from django.utils.encoding import force_text
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _

from mezzanine.conf import settings
Expand Down Expand Up @@ -151,7 +151,7 @@ def save(self, *args, **kwargs):
file name.
"""
if not self.id and not self.description:
name = force_text(self.file)
name = force_str(self.file)
name = name.rsplit("/", 1)[-1].rsplit(".", 1)[0]
name = name.replace("'", "")
name = "".join(c if c not in punctuation else " " for c in name)
Expand Down
8 changes: 4 additions & 4 deletions mezzanine/generic/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from mezzanine.generic.forms import RatingForm, ThreadedCommentForm
from mezzanine.generic.models import Keyword
from mezzanine.utils.cache import add_cache_bypass
from mezzanine.utils.deprecation import is_authenticated
from mezzanine.utils.deprecation import is_authenticated, request_is_ajax
from mezzanine.utils.importing import import_dotted_path
from mezzanine.utils.views import is_spam, set_cookie

Expand Down Expand Up @@ -87,7 +87,7 @@ def initial_validation(request, prefix):
except (TypeError, ObjectDoesNotExist, LookupError):
redirect_url = "/"
if redirect_url:
if request.is_ajax():
if request_is_ajax(request):
return HttpResponse(dumps({"location": redirect_url}))
else:
return redirect(redirect_url)
Expand Down Expand Up @@ -117,7 +117,7 @@ def comment(request, template="generic/comments.html", extra_context=None):
cookie_value = post_data.get(field, "")
set_cookie(response, cookie_name, cookie_value)
return response
elif request.is_ajax() and form.errors:
elif request_is_ajax(request) and form.errors:
return HttpResponse(dumps({"errors": form.errors}))
# Show errors with stand-alone comment form.
context = {"obj": obj, "posted_comment_form": form}
Expand All @@ -139,7 +139,7 @@ def rating(request):
rating_form = RatingForm(request, obj, post_data)
if rating_form.is_valid():
rating_form.save()
if request.is_ajax():
if request_is_ajax(request):
# Reload the object and return the rating fields as json.
obj = obj.__class__.objects.get(id=obj.id)
rating_name = obj.get_ratingfield_name()
Expand Down
18 changes: 18 additions & 0 deletions mezzanine/utils/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ class MiddlewareMixin:
pass


def request_is_ajax(request):
"""
request.is_ajax() is deprecated. Check the content_type

Returns true if request CONTENT_TYPE is "application/json"
"""
return request.META.get("CONTENT_TYPE") == "application/json"


def get_middleware_request(request):
"""
Middlewares require get_request in after django4.0

Returns the passed request object
"""
return request


def get_middleware_setting_name():
"""
Returns the name of the middleware setting.
Expand Down
Loading