diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 596e61c81..c9780a981 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,7 +37,7 @@ jobs:
strategy:
matrix:
python-version: ["3.12"]
- django-version: ["django42"]
+ django-version: ["django42", "django52"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
diff --git a/credentials/apps/api/v2/decorators.py b/credentials/apps/api/v2/decorators.py
index 97a10aa47..fb63a1439 100644
--- a/credentials/apps/api/v2/decorators.py
+++ b/credentials/apps/api/v2/decorators.py
@@ -26,7 +26,7 @@ def wrapper(*args, **kwargs):
data = request.body
logger.info(
f"{request.method} request received to endpoint [{request.get_full_path()}] from user "
- f"[{request.user.username}] originating from [{request.META.get('HTTP_HOST', 'Unknown')}] "
+ f"[{request.user.username}] originating from [{request.headers.get('host', 'Unknown')}] "
f"with data: [{data}]"
)
except Exception as exc:
diff --git a/credentials/apps/api/v2/tests/test_serializers.py b/credentials/apps/api/v2/tests/test_serializers.py
index b64e82cec..8e87e647f 100644
--- a/credentials/apps/api/v2/tests/test_serializers.py
+++ b/credentials/apps/api/v2/tests/test_serializers.py
@@ -1,12 +1,11 @@
from collections import namedtuple
-from datetime import datetime
from logging import WARNING
from uuid import uuid4
import ddt
-import pytz
from django.test import TestCase
from django.urls import reverse
+from django.utils import timezone
from rest_framework.exceptions import ValidationError
from rest_framework.settings import api_settings
from rest_framework.test import APIRequestFactory
@@ -203,8 +202,7 @@ def test_to_representation(self):
def test_to_internal_value(self):
Request = namedtuple("Request", ["site"])
serializer = UserGradeSerializer(context={"request": Request(site=self.site)})
- updated_at_dt = datetime.now()
- updated_at_utc = updated_at_dt.replace(tzinfo=pytz.UTC)
+ updated_at_utc = timezone.now()
data = {
"username": "alice",
diff --git a/credentials/apps/api/v2/tests/test_views.py b/credentials/apps/api/v2/tests/test_views.py
index d39edef38..e8df1226a 100644
--- a/credentials/apps/api/v2/tests/test_views.py
+++ b/credentials/apps/api/v2/tests/test_views.py
@@ -1,14 +1,13 @@
-import datetime
import json
from decimal import Decimal
from unittest import mock
import ddt
-import pytz
from django.contrib.auth.models import Permission
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase
from django.urls import reverse
+from django.utils import timezone
from rest_framework.renderers import JSONRenderer
from rest_framework.test import APIRequestFactory, APITestCase
from testfixtures import LogCapture
@@ -597,8 +596,7 @@ def test_upgrade_with_lms_last_updated_at_data(self):
self.add_user_permission(self.user, "add_usergrade")
# simulate updating the existing record with the new field in the data
- dt = datetime.datetime.now()
- last_updated_at = dt.replace(tzinfo=pytz.UTC)
+ last_updated_at = timezone.now()
data = self.serialize_user_grade(grade)
data["lms_last_updated_at"] = last_updated_at
response = self.client.post(self.list_path, data=JSONRenderer().render(data), content_type=JSON_CONTENT_TYPE)
diff --git a/credentials/apps/badges/admin.py b/credentials/apps/badges/admin.py
index 3ee41a2fc..705675881 100644
--- a/credentials/apps/badges/admin.py
+++ b/credentials/apps/badges/admin.py
@@ -318,6 +318,7 @@ def delete_queryset(self, request, queryset):
return
super().delete_queryset(request, queryset)
+ @admin.display(description=_("icon"))
def image(self, obj):
"""
Badge template preview image.
@@ -326,8 +327,6 @@ def image(self, obj):
return format_html('
', obj.icon)
return None
- image.short_description = _("icon")
-
def save_model(self, request, obj, form, change):
pass
@@ -391,6 +390,7 @@ class BadgeRequirementAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
+ @admin.display(description=_("badge template"))
def template_link(self, instance):
"""
Interactive link to parent (badge template).
@@ -401,8 +401,6 @@ def template_link(self, instance):
url = reverse(reverse_name, args=reverse_args)
return format_html('{}', url, instance.template)
- template_link.short_description = _("badge template")
-
def response_change(self, request, obj):
if "_save" in request.POST:
reverse_name = ADMIN_CHANGE_VIEW_REVERSE_NAMES.get(obj.template.origin, "admin:index")
@@ -453,6 +451,7 @@ class BadgePenaltyAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
+ @admin.display(description=_("badge template"))
def template_link(self, instance):
"""
Interactive link to parent (badge template).
@@ -462,8 +461,6 @@ def template_link(self, instance):
url = reverse(reverse_name, args=reverse_args)
return format_html('{}', url, instance.template)
- template_link.short_description = _("badge template")
-
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "requirements":
object_id = request.resolver_match.kwargs.get("object_id")
diff --git a/credentials/apps/catalog/tests/factories.py b/credentials/apps/catalog/tests/factories.py
index a486bc552..57f224dec 100644
--- a/credentials/apps/catalog/tests/factories.py
+++ b/credentials/apps/catalog/tests/factories.py
@@ -2,12 +2,11 @@
Factories for tests of Credentials.
"""
-import datetime
from uuid import uuid4
import factory
+from django.utils.timezone import datetime, timezone
from factory.fuzzy import FuzzyDateTime, FuzzyInteger, FuzzyText
-from pytz import UTC
from slugify import slugify
from credentials.apps.catalog.data import PathwayStatus
@@ -55,8 +54,8 @@ class Meta:
uuid = factory.LazyFunction(uuid4)
key = FuzzyText(prefix="course-run-id/", suffix="/fake")
title_override = None
- start_date = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC))
- end_date = FuzzyDateTime(datetime.datetime(2014, 1, 1, tzinfo=UTC)).end_dt
+ start_date = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=timezone.utc))
+ end_date = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=timezone.utc)).end_dt
class ProgramFactory(factory.django.DjangoModelFactory):
diff --git a/credentials/settings/base.py b/credentials/settings/base.py
index 620e9b7c1..d95f88cad 100644
--- a/credentials/settings/base.py
+++ b/credentials/settings/base.py
@@ -230,13 +230,9 @@
TIME_ZONE = "UTC"
TIME_ZONE_CLASS = timezone.utc
-# https://docs.djangoproject.com/en/4.2/releases/4.0/#zoneinfo-default-timezone-implementation
-USE_DEPRECATED_PYTZ = True
USE_I18N = True
-USE_L10N = True
-
USE_TZ = True
LOCALE_PATHS = (root("conf", "locale"),)
diff --git a/credentials/urls.py b/credentials/urls.py
index 615e563b5..2906694e6 100644
--- a/credentials/urls.py
+++ b/credentials/urls.py
@@ -78,7 +78,7 @@
if is_badges_enabled():
urlpatterns += [
- re_path(r"^badges/", include(("credentials.apps.badges.urls", "badges"), namespace="badges")),
+ path("badges/", include(("credentials.apps.badges.urls", "badges"), namespace="badges")),
]
# edx-drf-extensions csrf app
diff --git a/tox.ini b/tox.ini
index 2b1eb6713..4fbf9e18e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py{3.12}-django{42}
+envlist = py{3.12}-django{42,52}
skipsdist = true
[pytest]
@@ -9,6 +9,7 @@ testpaths = credentials/apps
[testenv]
deps =
django42: -r requirements/django.txt
+ django52: Django>=5.2,<5.3
-r {toxinidir}/requirements/test.txt
allowlist_externals:
make