diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 2ccf7d721..7fda98e67 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -360,6 +360,10 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management. +## django-rest-knox + +[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into). + [cite]: http://jacobian.org/writing/rest-worst-practices/ [http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 [http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 @@ -400,3 +404,4 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a [mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 [djoser]: https://github.com/sunscrapers/djoser [django-rest-auth]: https://github.com/Tivix/django-rest-auth +[django-rest-knox]: https://github.com/James1345/django-rest-knox diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 70739689d..ca540a13b 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1032,6 +1032,9 @@ The following third party packages are also available. The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases. +## Serpy +The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed. + ## MongoengineModelSerializer The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework. @@ -1050,6 +1053,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ [django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/ [marshmallow]: https://marshmallow.readthedocs.org/en/latest/ +[serpy]: https://github.com/clarkduvall/serpy [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index c54af0cec..890f8d561 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -181,7 +181,7 @@ We can also serialize querysets instead of model instances. To do so we simply serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data - # [{'pk': 1, 'title': u'', 'code': u'foo = "bar"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}, {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}] + # [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] ## Using ModelSerializers diff --git a/requirements/requirements-packaging.txt b/requirements/requirements-packaging.txt index 1efb2f836..7510dbd79 100644 --- a/requirements/requirements-packaging.txt +++ b/requirements/requirements-packaging.txt @@ -5,4 +5,4 @@ wheel==0.24.0 twine==1.4.0 # Transifex client for managing translation resources. -transifex-client==0.10 +transifex-client==0.11b3 diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 159784ea3..7c48c621e 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -608,10 +608,13 @@ class BooleanField(Field): super(BooleanField, self).__init__(**kwargs) def to_internal_value(self, data): - if data in self.TRUE_VALUES: - return True - elif data in self.FALSE_VALUES: - return False + try: + if data in self.TRUE_VALUES: + return True + elif data in self.FALSE_VALUES: + return False + except TypeError: # Input is an unhashable type + pass self.fail('invalid', input=data) def to_representation(self, value): diff --git a/tests/test_fields.py b/tests/test_fields.py index c1d3e3a49..6048f49d0 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -466,6 +466,18 @@ class TestBooleanField(FieldValues): } field = serializers.BooleanField() + def test_disallow_unhashable_collection_types(self): + inputs = ( + [], + {}, + ) + field = serializers.BooleanField() + for input_value in inputs: + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(input_value) + expected = ['"{0}" is not a valid boolean.'.format(input_value)] + assert exc_info.value.detail == expected + class TestNullBooleanField(FieldValues): """ diff --git a/tests/test_response.py b/tests/test_response.py index 1dd5d5ea0..b53688e5c 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -5,6 +5,7 @@ from django.test import TestCase from django.utils import six from rest_framework import generics, routers, serializers, status, viewsets +from rest_framework.parsers import JSONParser from rest_framework.renderers import ( BaseRenderer, BrowsableAPIRenderer, JSONRenderer ) @@ -79,6 +80,14 @@ class MockViewSettingContentType(APIView): return Response(DUMMYCONTENT, status=DUMMYSTATUS, content_type='setbyview') +class JSONView(APIView): + parser_classes = (JSONParser,) + + def post(self, request, **kwargs): + assert request.data + return Response(DUMMYCONTENT) + + class HTMLView(APIView): renderer_classes = (BrowsableAPIRenderer, ) @@ -114,6 +123,7 @@ urlpatterns = [ url(r'^.*\.(?P.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^html$', HTMLView.as_view()), + url(r'^json$', JSONView.as_view()), url(r'^html1$', HTMLView1.as_view()), url(r'^html_new_model$', HTMLNewModelView.as_view()), url(r'^html_new_model_viewset', include(new_model_viewset_router.urls)), @@ -203,6 +213,25 @@ class RendererIntegrationTests(TestCase): self.assertEqual(resp.status_code, DUMMYSTATUS) +class UnsupportedMediaTypeTests(TestCase): + urls = 'tests.test_response' + + def test_should_allow_posting_json(self): + response = self.client.post('/json', data='{"test": 123}', content_type='application/json') + + self.assertEqual(response.status_code, 200) + + def test_should_not_allow_posting_xml(self): + response = self.client.post('/json', data='123', content_type='application/xml') + + self.assertEqual(response.status_code, 415) + + def test_should_not_allow_posting_a_form(self): + response = self.client.post('/json', data={'test': 123}) + + self.assertEqual(response.status_code, 415) + + class Issue122Tests(TestCase): """ Tests that covers #122.