From 4d7c8a3a3a60373b415771e87f0633170ee6b69a Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Mon, 11 Aug 2025 10:18:59 -0400 Subject: [PATCH 1/6] build: Unpin DRF. There have been no breaking changes in the minor versions and there is no ticket for unpinning this constraint. It doesn't make sense to keep. --- requirements/constraints.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index a37d7e134d..1714bdb3ae 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -34,10 +34,6 @@ django-oauth-toolkit==1.7.1 # Issue for unpinning: https://github.com/openedx/edx-platform/issues/35276 django-webpack-loader==0.7.0 -# Date: 2023-06-20 -# Adding pin to avoid any major upgrade -djangorestframework<3.15.0 - # Date: 2024-07-19 # Generally speaking, the major version of django-stubs must either match the major version # of django, or exceed it by 1. So, we will need to perpetually constrain django-stubs and From bcd900ef232bd3a3ddfb48cd6ed6de7f72e62714 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Mon, 11 Aug 2025 10:28:51 -0400 Subject: [PATCH 2/6] build: urllib3 is only a testing direct requirement. It's only used by botocore and requests in the actual retirement code and those have been able to handle a newer version of the library for quite some time. urllib3 is directly used in the testing code but not in a way where we need to constrain it from being upgraded. --- scripts/user_retirement/requirements/base.in | 4 ---- scripts/user_retirement/requirements/testing.in | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/user_retirement/requirements/base.in b/scripts/user_retirement/requirements/base.in index eabc9393ea..d0bdab9082 100644 --- a/scripts/user_retirement/requirements/base.in +++ b/scripts/user_retirement/requirements/base.in @@ -11,7 +11,3 @@ unicodecsv simplejson simple-salesforce google-api-python-client - -# urllib3 is needed for the user_retirement scripts -# constraint will be investigated and removed in a separate issue -urllib3<2.0.0 diff --git a/scripts/user_retirement/requirements/testing.in b/scripts/user_retirement/requirements/testing.in index 49a4297b22..9f79075392 100644 --- a/scripts/user_retirement/requirements/testing.in +++ b/scripts/user_retirement/requirements/testing.in @@ -6,3 +6,4 @@ requests_mock responses mock ddt +urllib3 From 2edd6564925d4dbad7b92d78cf194a5c26972690 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Mon, 11 Aug 2025 10:33:53 -0400 Subject: [PATCH 3/6] chore: Run `make upgrade` --- requirements/edx/base.txt | 4 +--- requirements/edx/development.txt | 4 +--- requirements/edx/doc.txt | 4 +--- requirements/edx/testing.txt | 4 +--- scripts/user_retirement/requirements/base.txt | 3 +-- scripts/user_retirement/requirements/testing.txt | 3 ++- 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 1ba5008042..71503660c3 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -377,9 +377,8 @@ django-webpack-loader==0.7.0 # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in # edx-proctoring -djangorestframework==3.14.0 +djangorestframework==3.16.1 # via - # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in # django-config-models # django-user-tasks @@ -1010,7 +1009,6 @@ python3-saml==1.16.0 pytz==2025.2 # via # -r requirements/edx/kernel.in - # djangorestframework # drf-yasg # edx-completion # edx-enterprise diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 8b110430c6..9fdd80412c 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -610,9 +610,8 @@ django-webpack-loader==0.7.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-proctoring -djangorestframework==3.14.0 +djangorestframework==3.16.1 # via - # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # django-config-models @@ -1772,7 +1771,6 @@ pytz==2025.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt - # djangorestframework # drf-yasg # edx-completion # edx-enterprise diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index d7b35de19c..e73b873735 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -449,9 +449,8 @@ django-webpack-loader==0.7.0 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-proctoring -djangorestframework==3.14.0 +djangorestframework==3.16.1 # via - # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # django-config-models # django-user-tasks @@ -1238,7 +1237,6 @@ python3-saml==1.16.0 pytz==2025.2 # via # -r requirements/edx/base.txt - # djangorestframework # drf-yasg # edx-completion # edx-enterprise diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 7b5e5794d2..0fbe4c2cff 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -475,9 +475,8 @@ django-webpack-loader==0.7.0 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-proctoring -djangorestframework==3.14.0 +djangorestframework==3.16.1 # via - # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # django-config-models # django-user-tasks @@ -1350,7 +1349,6 @@ python3-saml==1.16.0 pytz==2025.2 # via # -r requirements/edx/base.txt - # djangorestframework # drf-yasg # edx-completion # edx-enterprise diff --git a/scripts/user_retirement/requirements/base.txt b/scripts/user_retirement/requirements/base.txt index 443f6e17f4..1640066d62 100644 --- a/scripts/user_retirement/requirements/base.txt +++ b/scripts/user_retirement/requirements/base.txt @@ -152,9 +152,8 @@ unicodecsv==0.14.1 # via -r scripts/user_retirement/requirements/base.in uritemplate==4.2.0 # via google-api-python-client -urllib3==1.26.20 +urllib3==2.5.0 # via - # -r scripts/user_retirement/requirements/base.in # botocore # requests zeep==4.3.1 diff --git a/scripts/user_retirement/requirements/testing.txt b/scripts/user_retirement/requirements/testing.txt index c19a6f73b0..e098efdedb 100644 --- a/scripts/user_retirement/requirements/testing.txt +++ b/scripts/user_retirement/requirements/testing.txt @@ -264,9 +264,10 @@ uritemplate==4.2.0 # via # -r scripts/user_retirement/requirements/base.txt # google-api-python-client -urllib3==1.26.20 +urllib3==2.5.0 # via # -r scripts/user_retirement/requirements/base.txt + # -r scripts/user_retirement/requirements/testing.in # botocore # requests # responses From b37006497814b604893f15161435a27a8c2adc4d Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 12 Aug 2025 12:22:05 -0400 Subject: [PATCH 4/6] test: Reduce query count. Previously we were doing the following query: SELECT 1 AS "a" FROM "user_api_userpreference" WHERE ("user_api_userpreference"."key" = 'pref-lang' AND "user_api_userpreference"."user_id" = 1 AND NOT ("user_api_userpreference"."id" = 1)) LIMIT 1 Looking at the middleware, I don't see why this was previously happening. --- openedx/core/djangoapps/lang_pref/tests/test_middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/core/djangoapps/lang_pref/tests/test_middleware.py b/openedx/core/djangoapps/lang_pref/tests/test_middleware.py index a9d69eddd3..c7e2897b0c 100644 --- a/openedx/core/djangoapps/lang_pref/tests/test_middleware.py +++ b/openedx/core/djangoapps/lang_pref/tests/test_middleware.py @@ -233,7 +233,7 @@ class TestUserPreferenceMiddleware(CacheIsolationTestCase): # Preference is the same as the cookie, shouldn't write to the database - with self.assertNumQueries(3): + with self.assertNumQueries(2): self.middleware.process_request(self.request) assert get_user_preference(self.user, LANGUAGE_KEY) == 'es' From 7121d4e4667c77aea6785b64001be949bee58a91 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 12 Aug 2025 13:56:43 -0400 Subject: [PATCH 5/6] fix: Correct serializer model reference. When linking a DRF serializer with a model, you need to link it to the model class not an instance of the model. The newer version of DRF tries to access the model_manager from the model here and runs into issues if it's not defined correctly. --- openedx/core/djangoapps/agreements/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/core/djangoapps/agreements/serializers.py b/openedx/core/djangoapps/agreements/serializers.py index 11e9d57f40..397a9ba61d 100644 --- a/openedx/core/djangoapps/agreements/serializers.py +++ b/openedx/core/djangoapps/agreements/serializers.py @@ -16,7 +16,7 @@ class IntegritySignatureSerializer(serializers.ModelSerializer): created_at = serializers.DateTimeField(source='created') class Meta: - model = IntegritySignature() + model = IntegritySignature fields = ('username', 'course_id', 'created_at') From bf62862b9fe0d2bfc7f90e200ebf82393ffc1787 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 12 Aug 2025 16:03:48 -0400 Subject: [PATCH 6/6] test: Fix notifacitons test for new DRF version. DRF was swallowing an underlying error string and providing its own which was being checked for here. See https://github.com/encode/django-rest-framework/pull/8051 for details. The new version fixes that underlying issue and so the test failed. Rather than checking for the exact string of the 404 which is not relevant, we now just verify that the error code associated with the request matches the HTTP response. This should make the test more resilient to future text changes in the underlying Django function `get_object_or_404` --- openedx/core/djangoapps/notifications/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/core/djangoapps/notifications/tests/test_views.py b/openedx/core/djangoapps/notifications/tests/test_views.py index 745b690b5c..de02141745 100644 --- a/openedx/core/djangoapps/notifications/tests/test_views.py +++ b/openedx/core/djangoapps/notifications/tests/test_views.py @@ -443,7 +443,7 @@ class NotificationReadAPIViewTestCase(APITestCase): response = self.client.patch(self.url, data) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response.data["detail"], 'Not found.') + self.assertEqual(response.data["detail"].code, 'not_found') def test_mark_notification_read_with_app_name_and_notification_id(self): # Create a PATCH request to mark notification as read for existing app e.g 'discussion' and notification_id: 2