diff --git a/common/djangoapps/course_modes/admin.py b/common/djangoapps/course_modes/admin.py index 0181c0c50b..eb5adcf864 100644 --- a/common/djangoapps/course_modes/admin.py +++ b/common/djangoapps/course_modes/admin.py @@ -56,7 +56,7 @@ class CourseModeForm(forms.ModelForm): def __init__(self, *args, **kwargs): # If args is a QueryDict, then the ModelForm addition request came in as a POST with a course ID string. # Change the course ID string to a CourseLocator object by copying the QueryDict to make it mutable. - if len(args) > 0 and 'course' in args[0] and isinstance(args[0], QueryDict): + if args and 'course' in args[0] and isinstance(args[0], QueryDict): args_copy = args[0].copy() args_copy['course'] = CourseKey.from_string(args_copy['course']) args = [args_copy] diff --git a/common/djangoapps/student/admin.py b/common/djangoapps/student/admin.py index 2ddc58497f..5acddf43b6 100644 --- a/common/djangoapps/student/admin.py +++ b/common/djangoapps/student/admin.py @@ -7,6 +7,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.forms import ReadOnlyPasswordHashField, UserChangeForm as BaseUserChangeForm from django.db import models +from django.http.request import QueryDict from django.utils.translation import ugettext_lazy as _ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey @@ -149,13 +150,26 @@ class LinkedInAddToProfileConfigurationAdmin(admin.ModelAdmin): class CourseEnrollmentForm(forms.ModelForm): def __init__(self, *args, **kwargs): + # If args is a QueryDict, then the ModelForm addition request came in as a POST with a course ID string. + # Change the course ID string to a CourseLocator object by copying the QueryDict to make it mutable. + if args and 'course' in args[0] and isinstance(args[0], QueryDict): + args_copy = args[0].copy() + try: + args_copy['course'] = CourseKey.from_string(args_copy['course']) + except InvalidKeyError: + raise forms.ValidationError("Cannot make a valid CourseKey from id {}!".format(args_copy['course'])) + args = [args_copy] + super(CourseEnrollmentForm, self).__init__(*args, **kwargs) if self.data.get('course'): try: self.data['course'] = CourseKey.from_string(self.data['course']) - except InvalidKeyError: - raise forms.ValidationError("Cannot make a valid CourseKey from id {}!".format(self.data['course'])) + except AttributeError: + # Change the course ID string to a CourseLocator. + # On a POST request, self.data is a QueryDict and is immutable - so this code will fail. + # However, the args copy above before the super() call handles this case. + pass def clean_course_id(self): course_id = self.cleaned_data['course'] diff --git a/common/djangoapps/student/tests/test_admin_views.py b/common/djangoapps/student/tests/test_admin_views.py index ef5d2cb882..e60170f8e2 100644 --- a/common/djangoapps/student/tests/test_admin_views.py +++ b/common/djangoapps/student/tests/test_admin_views.py @@ -4,6 +4,7 @@ Tests student admin.py import ddt from django.contrib.admin.sites import AdminSite from django.contrib.auth.models import User +from django.forms import ValidationError from django.urls import reverse from django.test import TestCase from mock import Mock @@ -209,7 +210,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): super(CourseEnrollmentAdminTest, self).setUp() self.user = UserFactory.create(is_staff=True, is_superuser=True) self.course = CourseFactory() - CourseEnrollmentFactory( + self.course_enrollment = CourseEnrollmentFactory( user=self.user, course_id=self.course.id, # pylint: disable=no-member ) @@ -254,3 +255,46 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): # Locate the