diff options
| -rw-r--r-- | docs/index.md | 2 | ||||
| -rw-r--r-- | docs/tutorial/1-serialization.md | 24 | ||||
| -rw-r--r-- | docs/tutorial/quickstart.md | 3 | ||||
| -rw-r--r-- | rest_framework/renderers.py | 4 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 28 | ||||
| -rw-r--r-- | rest_framework/viewsets.py | 6 | ||||
| -rw-r--r-- | tests/test_viewsets.py | 35 |
7 files changed, 73 insertions, 29 deletions
diff --git a/docs/index.md b/docs/index.md index e0ba2332..52e42fc9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,7 +4,7 @@ <a href="https://twitter.com/share" class="twitter-share-button" data-url="django-rest-framework.org" data-text="Checking out the totally awesome Django REST framework! http://www.django-rest-framework.org" data-count="none"></a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script> -<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master" class="travis-build-image"> +<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master" class="travis-build-image"> </p> --- diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index a3c19858..eb0a00c0 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -101,7 +101,7 @@ The first thing we need to get started on our Web API is to provide a way of ser class SnippetSerializer(serializers.Serializer): pk = serializers.IntegerField(read_only=True) - title = serializers.CharField(required=False, + title = serializers.CharField(required=False, allow_blank=True max_length=100) code = serializers.CharField(style={'type': 'textarea'}) linenos = serializers.BooleanField(required=False) @@ -110,21 +110,21 @@ The first thing we need to get started on our Web API is to provide a way of ser style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly') - def create(self, validated_attrs): + def create(self, validated_data): """ Create and return a new `Snippet` instance, given the validated data. """ - return Snippet.objects.create(**validated_attrs) + return Snippet.objects.create(**validated_data) - def update(self, instance, validated_attrs): + def update(self, instance, validated_data): """ Update and return an existing `Snippet` instance, given the validated data. """ - instance.title = validated_attrs.get('title', instance.title) - instance.code = validated_attrs.get('code', instance.code) - instance.linenos = validated_attrs.get('linenos', instance.linenos) - instance.language = validated_attrs.get('language', instance.language) - instance.style = validated_attrs.get('style', instance.style) + instance.title = validated_data.get('title', instance.title) + instance.code = validated_data.get('code', instance.code) + instance.linenos = validated_data.get('linenos', instance.linenos) + instance.language = validated_data.get('language', instance.language) + instance.style = validated_data.get('style', instance.style) instance.save() return instance @@ -181,7 +181,7 @@ Deserialization is similar. First we parse a stream into Python native datatype serializer = SnippetSerializer(data=data) serializer.is_valid() # True - serializer.object + serializer.save() # <Snippet: Snippet object> Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer. @@ -210,7 +210,7 @@ One nice property that serializers have is that you can inspect all the fields i >>> from snippets.serializers import SnippetSerializer >>> serializer = SnippetSerializer() - >>> print repr(serializer) # In python 3 use `print(repr(serializer))` + >>> print(repr(serializer)) SnippetSerializer(): id = IntegerField(label='ID', read_only=True) title = CharField(allow_blank=True, max_length=100, required=False) @@ -301,7 +301,7 @@ We'll also need a view which corresponds to an individual snippet, and can be us Finally we need to wire these views up. Create the `snippets/urls.py` file: - from django.conf.urls import patterns, url + from django.conf.urls import url from snippets import views urlpatterns = [ diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index 3e1ce0a9..d0703381 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -19,10 +19,9 @@ Create a new Django project named `tutorial`, then start a new app called `quick pip install djangorestframework # Set up a new project with a single application - django-admin.py startproject tutorial + django-admin.py startproject tutorial . cd tutorial django-admin.py startapp quickstart - cd .. Now sync your database for the first time: diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 64ad5a06..07f1c628 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -379,6 +379,10 @@ class HTMLFormRenderer(BaseRenderer): 'base_template': 'input.html', 'input_type': 'time' }, + serializers.FileField: { + 'base_template': 'input.html', + 'input_type': 'file' + }, serializers.BooleanField: { 'base_template': 'checkbox.html' }, diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d417ca80..af8aeb48 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -608,20 +608,20 @@ class ModelSerializer(Serializer): }) _related_class = PrimaryKeyRelatedField - def create(self, validated_attrs): + def create(self, validated_data): """ We have a bit of extra checking around this in order to provide descriptive messages when something goes wrong, but this method is essentially just: - return ExampleModel.objects.create(**validated_attrs) + return ExampleModel.objects.create(**validated_data) If there are many to many fields present on the instance then they cannot be set until the model is instantiated, in which case the implementation is like so: - example_relationship = validated_attrs.pop('example_relationship') - instance = ExampleModel.objects.create(**validated_attrs) + example_relationship = validated_data.pop('example_relationship') + instance = ExampleModel.objects.create(**validated_data) instance.example_relationship = example_relationship return instance @@ -633,8 +633,8 @@ class ModelSerializer(Serializer): # If we don't do this explicitly they'd likely get a confusing # error at the point of calling `Model.objects.create()`. assert not any( - isinstance(field, BaseSerializer) and not field.read_only - for field in self.fields.values() + isinstance(field, BaseSerializer) and (key in validated_attrs) + for key, field in self.fields.items() ), ( 'The `.create()` method does not suport nested writable fields ' 'by default. Write an explicit `.create()` method for serializer ' @@ -644,17 +644,17 @@ class ModelSerializer(Serializer): ModelClass = self.Meta.model - # Remove many-to-many relationships from validated_attrs. + # Remove many-to-many relationships from validated_data. # They are not valid arguments to the default `.create()` method, # as they require that the instance has already been saved. info = model_meta.get_field_info(ModelClass) many_to_many = {} for field_name, relation_info in info.relations.items(): - if relation_info.to_many and (field_name in validated_attrs): - many_to_many[field_name] = validated_attrs.pop(field_name) + if relation_info.to_many and (field_name in validated_data): + many_to_many[field_name] = validated_data.pop(field_name) try: - instance = ModelClass.objects.create(**validated_attrs) + instance = ModelClass.objects.create(**validated_data) except TypeError as exc: msg = ( 'Got a `TypeError` when calling `%s.objects.create()`. ' @@ -679,10 +679,10 @@ class ModelSerializer(Serializer): return instance - def update(self, instance, validated_attrs): + def update(self, instance, validated_data): assert not any( - isinstance(field, BaseSerializer) and not field.read_only - for field in self.fields.values() + isinstance(field, BaseSerializer) and (key in validated_attrs) + for key, field in self.fields.items() ), ( 'The `.update()` method does not suport nested writable fields ' 'by default. Write an explicit `.update()` method for serializer ' @@ -690,7 +690,7 @@ class ModelSerializer(Serializer): (self.__class__.__module__, self.__class__.__name__) ) - for attr, value in validated_attrs.items(): + for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 84b4bd8d..70d14695 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -48,6 +48,12 @@ class ViewSetMixin(object): # eg. 'List' or 'Instance'. cls.suffix = None + # actions must not be empty + if not actions: + raise TypeError("The `actions` argument must be provided when " + "calling `.as_view()` on a ViewSet. For example " + "`.as_view({'get': 'list'})`") + # sanitize keyword arguments for key in initkwargs: if key in cls.http_method_names: diff --git a/tests/test_viewsets.py b/tests/test_viewsets.py new file mode 100644 index 00000000..4d18a955 --- /dev/null +++ b/tests/test_viewsets.py @@ -0,0 +1,35 @@ +from django.test import TestCase +from rest_framework import status +from rest_framework.response import Response +from rest_framework.test import APIRequestFactory +from rest_framework.viewsets import GenericViewSet + + +factory = APIRequestFactory() + + +class BasicViewSet(GenericViewSet): + def list(self, request, *args, **kwargs): + return Response({'ACTION': 'LIST'}) + + +class InitializeViewSetsTestCase(TestCase): + def test_initialize_view_set_with_actions(self): + request = factory.get('/', '', content_type='application/json') + my_view = BasicViewSet.as_view(actions={ + 'get': 'list', + }) + + response = my_view(request) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {'ACTION': 'LIST'}) + + def test_initialize_view_set_with_empty_actions(self): + try: + BasicViewSet.as_view() + except TypeError as e: + self.assertEqual(str(e), "The `actions` argument must be provided " + "when calling `.as_view()` on a ViewSet. " + "For example `.as_view({'get': 'list'})`") + else: + self.fail("actions must not be empty.") |
