Upgrade django-rest-framework version to edX fork, which is DRF v3.6.3
with a custom patch needed by edx-platform. Upgrade django-filter as well to v1.0.4 Import DjangoFilterBackend from the correct module - django_filter. Add django-filter to INSTALLED_APPS.
This commit is contained in:
@@ -187,7 +187,7 @@ def update_account_settings(requesting_user, update, username=None):
|
||||
# We have not found a way using signals to get the language proficiency changes (grouped by user).
|
||||
# As a workaround, store old and new values here and emit them after save is complete.
|
||||
if "language_proficiencies" in update:
|
||||
old_language_proficiencies = legacy_profile_serializer.data["language_proficiencies"]
|
||||
old_language_proficiencies = list(existing_user_profile.language_proficiencies.values('code'))
|
||||
|
||||
for serializer in user_serializer, legacy_profile_serializer:
|
||||
serializer.save()
|
||||
|
||||
@@ -454,7 +454,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
("country", "GB", "XY", u'"XY" is not a valid choice.'),
|
||||
("year_of_birth", 2009, "not_an_int", u"A valid integer is required."),
|
||||
("name", "bob", "z" * 256, u"Ensure this value has at most 255 characters (it has 256)."),
|
||||
("name", u"ȻħȺɍłɇs", "z ", "The name field must be at least 2 characters long."),
|
||||
("name", u"ȻħȺɍłɇs", "z ", u"The name field must be at least 2 characters long."),
|
||||
("goals", "Smell the roses"),
|
||||
("mailing_address", "Sesame Street"),
|
||||
# Note that we store the raw data, so it is up to client to escape the HTML.
|
||||
@@ -677,16 +677,25 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
|
||||
self.assertItemsEqual(response.data["language_proficiencies"], proficiencies)
|
||||
|
||||
@ddt.data(
|
||||
(u"not_a_list", {u'non_field_errors': [u'Expected a list of items but got type "unicode".']}),
|
||||
([u"not_a_JSON_object"], [{u'non_field_errors': [u'Invalid data. Expected a dictionary, but got unicode.']}]),
|
||||
([{}], [OrderedDict([('code', [u'This field is required.'])])]),
|
||||
(
|
||||
u"not_a_list",
|
||||
{u'non_field_errors': [u'Expected a list of items but got type "unicode".']}
|
||||
),
|
||||
(
|
||||
[u"not_a_JSON_object"],
|
||||
[{u'non_field_errors': [u'Invalid data. Expected a dictionary, but got unicode.']}]
|
||||
),
|
||||
(
|
||||
[{}],
|
||||
[{'code': [u'This field is required.']}]
|
||||
),
|
||||
(
|
||||
[{u"code": u"invalid_language_code"}],
|
||||
[OrderedDict([('code', [u'"invalid_language_code" is not a valid choice.'])])]
|
||||
[{'code': [u'"invalid_language_code" is not a valid choice.']}]
|
||||
),
|
||||
(
|
||||
[{u"code": u"kw"}, {u"code": u"el"}, {u"code": u"kw"}],
|
||||
['The language_proficiencies field must consist of unique languages']
|
||||
[u'The language_proficiencies field must consist of unique languages']
|
||||
),
|
||||
)
|
||||
@ddt.unpack
|
||||
|
||||
@@ -10,7 +10,7 @@ from functools import wraps
|
||||
|
||||
from django import forms
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.http import HttpResponseBadRequest, HttpRequest
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import Promise
|
||||
|
||||
@@ -407,26 +407,32 @@ def shim_student_view(view_func, check_logged_in=False):
|
||||
"""
|
||||
@wraps(view_func)
|
||||
def _inner(request): # pylint: disable=missing-docstring
|
||||
# Ensure that the POST querydict is mutable
|
||||
request.POST = request.POST.copy()
|
||||
# Make a copy of the current POST request to modify.
|
||||
modified_request = request.POST.copy()
|
||||
if isinstance(request, HttpRequest):
|
||||
# Works for an HttpRequest but not a rest_framework.request.Request.
|
||||
request.POST = modified_request
|
||||
else:
|
||||
# The request must be a rest_framework.request.Request.
|
||||
request._data = modified_request
|
||||
|
||||
# The login and registration handlers in student view try to change
|
||||
# the user's enrollment status if these parameters are present.
|
||||
# Since we want the JavaScript client to communicate directly with
|
||||
# the enrollment API, we want to prevent the student views from
|
||||
# updating enrollments.
|
||||
if "enrollment_action" in request.POST:
|
||||
del request.POST["enrollment_action"]
|
||||
if "course_id" in request.POST:
|
||||
del request.POST["course_id"]
|
||||
if "enrollment_action" in modified_request:
|
||||
del modified_request["enrollment_action"]
|
||||
if "course_id" in modified_request:
|
||||
del modified_request["course_id"]
|
||||
|
||||
# Include the course ID if it's specified in the analytics info
|
||||
# so it can be included in analytics events.
|
||||
if "analytics" in request.POST:
|
||||
if "analytics" in modified_request:
|
||||
try:
|
||||
analytics = json.loads(request.POST["analytics"])
|
||||
analytics = json.loads(modified_request["analytics"])
|
||||
if "enroll_course_id" in analytics:
|
||||
request.POST["course_id"] = analytics.get("enroll_course_id")
|
||||
modified_request["course_id"] = analytics.get("enroll_course_id")
|
||||
except (ValueError, TypeError):
|
||||
LOGGER.error(
|
||||
u"Could not parse analytics object sent to user API: {analytics}".format(
|
||||
|
||||
@@ -113,6 +113,7 @@ def update_user_preferences(requesting_user, update, user=None):
|
||||
for preference_key in update.keys():
|
||||
preference_value = update[preference_key]
|
||||
if preference_value is not None:
|
||||
preference_value = unicode(preference_value)
|
||||
try:
|
||||
serializer = create_user_preference_serializer(user, preference_key, preference_value)
|
||||
validate_user_preference_serializer(serializer, preference_key, preference_value)
|
||||
@@ -129,6 +130,7 @@ def update_user_preferences(requesting_user, update, user=None):
|
||||
for preference_key in update.keys():
|
||||
preference_value = update[preference_key]
|
||||
if preference_value is not None:
|
||||
preference_value = unicode(preference_value)
|
||||
try:
|
||||
serializer = serializers[preference_key]
|
||||
|
||||
@@ -152,7 +154,7 @@ def set_user_preference(requesting_user, preference_key, preference_value, usern
|
||||
requesting_user (User): The user requesting to modify account information. Only the user with username
|
||||
'username' has permissions to modify account information.
|
||||
preference_key (str): The key for the user preference.
|
||||
preference_value (str): The value to be stored. Non-string values will be converted to strings.
|
||||
preference_value (str): The value to be stored. Non-string values are converted to strings.
|
||||
username (str): Optional username specifying which account should be updated. If not specified,
|
||||
`requesting_user.username` is assumed.
|
||||
|
||||
@@ -166,6 +168,8 @@ def set_user_preference(requesting_user, preference_key, preference_value, usern
|
||||
UserAPIInternalError: the operation failed due to an unexpected error.
|
||||
"""
|
||||
existing_user = _get_authorized_user(requesting_user, username)
|
||||
if preference_value is not None:
|
||||
preference_value = unicode(preference_value)
|
||||
serializer = create_user_preference_serializer(existing_user, preference_key, preference_value)
|
||||
validate_user_preference_serializer(serializer, preference_key, preference_value)
|
||||
|
||||
|
||||
@@ -39,13 +39,14 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
||||
class UserPreferenceSerializer(serializers.HyperlinkedModelSerializer):
|
||||
"""
|
||||
Serializer that generates a represenation of a UserPreference entity
|
||||
Serializer that generates a representation of a UserPreference entity.
|
||||
"""
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta(object):
|
||||
model = UserPreference
|
||||
depth = 1
|
||||
fields = ('user', 'key', 'value', 'url')
|
||||
|
||||
|
||||
class RawUserPreferenceSerializer(serializers.ModelSerializer):
|
||||
@@ -57,6 +58,7 @@ class RawUserPreferenceSerializer(serializers.ModelSerializer):
|
||||
class Meta(object):
|
||||
model = UserPreference
|
||||
depth = 1
|
||||
fields = ('user', 'key', 'value', 'url')
|
||||
|
||||
|
||||
class ReadOnlyFieldsSerializerMixin(object):
|
||||
|
||||
@@ -359,7 +359,7 @@ class UserPreferenceViewSetTest(CacheIsolationTestCase, UserApiTestCase):
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("put", self.LIST_URI))
|
||||
|
||||
def test_patch_list_not_allowed(self):
|
||||
raise SkipTest("Django 1.4's test client does not support patch")
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("patch", self.LIST_URI))
|
||||
|
||||
def test_delete_list_not_allowed(self):
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("delete", self.LIST_URI))
|
||||
@@ -450,7 +450,7 @@ class UserPreferenceViewSetTest(CacheIsolationTestCase, UserApiTestCase):
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("put", self.detail_uri))
|
||||
|
||||
def test_patch_detail_not_allowed(self):
|
||||
raise SkipTest("Django 1.4's test client does not support patch")
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("patch", self.detail_uri))
|
||||
|
||||
def test_delete_detail_not_allowed(self):
|
||||
self.assertHttpMethodNotAllowed(self.request_with_auth("delete", self.detail_uri))
|
||||
|
||||
@@ -10,10 +10,11 @@ from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.csrf import csrf_exempt, csrf_protect, ensure_csrf_cookie
|
||||
from django_countries import countries
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx import locator
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from rest_framework import authentication, filters, generics, status, viewsets
|
||||
from rest_framework import authentication, generics, status, viewsets
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.views import APIView
|
||||
|
||||
@@ -1054,7 +1055,7 @@ class UserPreferenceViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
authentication_classes = (authentication.SessionAuthentication,)
|
||||
permission_classes = (ApiKeyHeaderPermission,)
|
||||
queryset = UserPreference.objects.all()
|
||||
filter_backends = (filters.DjangoFilterBackend,)
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filter_fields = ("key", "user")
|
||||
serializer_class = UserPreferenceSerializer
|
||||
paginate_by = 10
|
||||
|
||||
Reference in New Issue
Block a user