aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/authentication.py14
-rw-r--r--rest_framework/compat.py15
-rw-r--r--rest_framework/filters.py4
-rw-r--r--rest_framework/renderers.py37
-rw-r--r--rest_framework/response.py4
-rw-r--r--rest_framework/serializers.py2
-rw-r--r--rest_framework/templates/rest_framework/base.html4
-rw-r--r--rest_framework/tests/test_authentication.py9
-rw-r--r--rest_framework/tests/test_fields.py18
-rw-r--r--rest_framework/tests/test_serializer.py2
10 files changed, 76 insertions, 33 deletions
diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
index da9ca510..887ef5d7 100644
--- a/rest_framework/authentication.py
+++ b/rest_framework/authentication.py
@@ -310,6 +310,13 @@ class OAuth2Authentication(BaseAuthentication):
auth = get_authorization_header(request).split()
+ if len(auth) == 1:
+ msg = 'Invalid bearer header. No credentials provided.'
+ raise exceptions.AuthenticationFailed(msg)
+ elif len(auth) > 2:
+ msg = 'Invalid bearer header. Token string should not contain spaces.'
+ raise exceptions.AuthenticationFailed(msg)
+
if auth and auth[0].lower() == b'bearer':
access_token = auth[1]
elif 'access_token' in request.POST:
@@ -319,13 +326,6 @@ class OAuth2Authentication(BaseAuthentication):
else:
return None
- if len(auth) == 1:
- msg = 'Invalid bearer header. No credentials provided.'
- raise exceptions.AuthenticationFailed(msg)
- elif len(auth) > 2:
- msg = 'Invalid bearer header. Token string should not contain spaces.'
- raise exceptions.AuthenticationFailed(msg)
-
return self.authenticate_credentials(request, access_token)
def authenticate_credentials(self, request, access_token):
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index fdf12448..9ad8b0d2 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -48,12 +48,15 @@ try:
except ImportError:
django_filters = None
-# guardian is optional
-try:
- import guardian
- import guardian.shortcuts # Fixes #1624
-except ImportError:
- guardian = None
+# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
+# Fixes (#1712). We keep the try/except for the test suite.
+guardian = None
+if 'guardian' in settings.INSTALLED_APPS:
+ try:
+ import guardian
+ import guardian.shortcuts # Fixes #1624
+ except ImportError:
+ pass
# cStringIO only if it's available, otherwise StringIO
diff --git a/rest_framework/filters.py b/rest_framework/filters.py
index 96d15eb9..c3b846ae 100644
--- a/rest_framework/filters.py
+++ b/rest_framework/filters.py
@@ -116,6 +116,10 @@ class OrderingFilter(BaseFilterBackend):
def get_ordering(self, request):
"""
Ordering is set by a comma delimited ?ordering=... query parameter.
+
+ The `ordering` query parameter can be overridden by setting
+ the `ordering_param` value on the OrderingFilter or by
+ specifying an `ORDERING_PARAM` value in the API settings.
"""
params = request.QUERY_PARAMS.get(self.ordering_param)
if params:
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 484961ad..7048d87d 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -54,32 +54,37 @@ class JSONRenderer(BaseRenderer):
format = 'json'
encoder_class = encoders.JSONEncoder
ensure_ascii = True
- charset = None
- # JSON is a binary encoding, that can be encoded as utf-8, utf-16 or utf-32.
+
+ # We don't set a charset because JSON is a binary encoding,
+ # that can be encoded as utf-8, utf-16 or utf-32.
# See: http://www.ietf.org/rfc/rfc4627.txt
# Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/
+ charset = None
+
+ def get_indent(self, accepted_media_type, renderer_context):
+ if accepted_media_type:
+ # If the media type looks like 'application/json; indent=4',
+ # then pretty print the result.
+ base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
+ try:
+ return max(min(int(params['indent']), 8), 0)
+ except (KeyError, ValueError, TypeError):
+ pass
+
+ # If 'indent' is provided in the context, then pretty print the result.
+ # E.g. If we're being called by the BrowsableAPIRenderer.
+ return renderer_context.get('indent', None)
+
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
- Render `data` into JSON.
+ Render `data` into JSON, returning a bytestring.
"""
if data is None:
return bytes()
- # If 'indent' is provided in the context, then pretty print the result.
- # E.g. If we're being called by the BrowsableAPIRenderer.
renderer_context = renderer_context or {}
- indent = renderer_context.get('indent', None)
-
- if accepted_media_type:
- # If the media type looks like 'application/json; indent=4',
- # then pretty print the result.
- base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
- indent = params.get('indent', indent)
- try:
- indent = max(min(int(indent), 8), 0)
- except (ValueError, TypeError):
- indent = None
+ indent = self.get_indent(accepted_media_type, renderer_context)
ret = json.dumps(data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii)
diff --git a/rest_framework/response.py b/rest_framework/response.py
index 1dc6abcf..5c02ea50 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -5,6 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string.
The appropriate renderer is called during Django's template response rendering.
"""
from __future__ import unicode_literals
+import django
from django.core.handlers.wsgi import STATUS_CODE_TEXT
from django.template.response import SimpleTemplateResponse
from rest_framework.compat import six
@@ -15,6 +16,9 @@ class Response(SimpleTemplateResponse):
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
+ # TODO: remove that once Django 1.3 isn't supported
+ if django.VERSION >= (1, 4):
+ rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects']
def __init__(self, data=None, status=200,
template_name=None, headers=None,
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index c2b414d7..43d339da 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -977,7 +977,7 @@ class ModelSerializer(Serializer):
try:
setattr(instance, key, val)
except ValueError:
- self._errors[key] = self.error_messages['required']
+ self._errors[key] = [self.error_messages['required']]
# Any relations that cannot be set until we've
# saved the model get hidden away on these
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 7067ee2f..e96fa8ec 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -93,7 +93,7 @@
{% endif %}
{% if options_form %}
- <form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
+ <form class="button-form" action="{{ request.get_full_path }}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" />
<button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
@@ -101,7 +101,7 @@
{% endif %}
{% if delete_form %}
- <form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
+ <form class="button-form" action="{{ request.get_full_path }}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
<button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button>
diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py
index a1c43d9c..34bf2910 100644
--- a/rest_framework/tests/test_authentication.py
+++ b/rest_framework/tests/test_authentication.py
@@ -550,6 +550,15 @@ class OAuth2Tests(TestCase):
self.assertEqual(response.status_code, 401)
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
+ def test_get_form_with_wrong_authorization_header_token_missing(self):
+ """Ensure that a missing token lead to the correct HTTP error status code"""
+ auth = "Bearer"
+ response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
+ self.assertEqual(response.status_code, 401)
+ response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
+ self.assertEqual(response.status_code, 401)
+
+ @unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
def test_get_form_passing_auth(self):
"""Ensure GETing form over OAuth with correct client credentials succeed"""
auth = self._create_authorization_header()
diff --git a/rest_framework/tests/test_fields.py b/rest_framework/tests/test_fields.py
index b04b947f..17d12f23 100644
--- a/rest_framework/tests/test_fields.py
+++ b/rest_framework/tests/test_fields.py
@@ -1002,3 +1002,21 @@ class BooleanField(TestCase):
bool_field = serializers.BooleanField(required=True)
self.assertFalse(BooleanRequiredSerializer(data={}).is_valid())
+
+
+class SerializerMethodFieldTest(TestCase):
+ """
+ Tests for the SerializerMethodField field_to_native() behavior
+ """
+ class SerializerTest(serializers.Serializer):
+ def get_my_test(self, obj):
+ return obj.my_test[0:5]
+
+ class Example():
+ my_test = 'Hey, this is a test !'
+
+ def test_field_to_native(self):
+ s = serializers.SerializerMethodField('get_my_test')
+ s.initialize(self.SerializerTest(), 'name')
+ result = s.field_to_native(self.Example(), None)
+ self.assertEqual(result, 'Hey, ')
diff --git a/rest_framework/tests/test_serializer.py b/rest_framework/tests/test_serializer.py
index 91248ce7..fb2eac0b 100644
--- a/rest_framework/tests/test_serializer.py
+++ b/rest_framework/tests/test_serializer.py
@@ -685,7 +685,7 @@ class ModelValidationTests(TestCase):
photo_serializer = PhotoSerializer(instance=photo, data={'album': ''}, partial=True)
self.assertFalse(photo_serializer.is_valid())
self.assertTrue('album' in photo_serializer.errors)
- self.assertEqual(photo_serializer.errors['album'], photo_serializer.error_messages['required'])
+ self.assertEqual(photo_serializer.errors['album'], [photo_serializer.error_messages['required']])
def test_foreign_key_with_partial(self):
"""