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:
John Eskew
2017-06-21 09:48:19 -04:00
parent 28bdfd4fdf
commit 2cd62c7bfb
18 changed files with 89 additions and 175 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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(

View File

@@ -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)

View File

@@ -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):

View File

@@ -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))

View File

@@ -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