Commit 30867703 by Tom Christie

Tweak docs, fix .error_data -> .errors

parent 549ebdc1
...@@ -59,7 +59,7 @@ class UpdateModelMixin(object): ...@@ -59,7 +59,7 @@ class UpdateModelMixin(object):
self.object = serializer.object self.object = serializer.object
self.object.save() self.object.save()
return Response(serializer.data) return Response(serializer.data)
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class DestroyModelMixin(object): class DestroyModelMixin(object):
......
# REST, Hypermedia & HATEOAS
> You keep using that word "REST". I do not think it means what you think it means. > You keep using that word "REST". I do not think it means what you think it means.
> >
> — Mike Amundsen, [talking at REST fest 2012][cite]. > — Mike Amundsen, [REST fest 2012 keynote][cite].
# REST, Hypermedia & HATEOAS
First off, the disclaimer. The name "Django REST framework" was choosen with a view to making sure the project would be easily found by developers. Throughout the documentation we try to use the more simple and technically correct terminology of "Web APIs". First off, the disclaimer. The name "Django REST framework" was choosen with a view to making sure the project would be easily found by developers. Throughout the documentation we try to use the more simple and technically correct terminology of "Web APIs".
......
...@@ -194,7 +194,7 @@ The root of our API is going to be a view that supports listing all the existing ...@@ -194,7 +194,7 @@ The root of our API is going to be a view that supports listing all the existing
comment.save() comment.save()
return JSONResponse(serializer.data, status=201) return JSONResponse(serializer.data, status=201)
else: else:
return JSONResponse(serializer.error_data, status=400) return JSONResponse(serializer.errors, status=400)
We'll also need a view which corrosponds to an individual comment, and can be used to retrieve, update or delete the comment. We'll also need a view which corrosponds to an individual comment, and can be used to retrieve, update or delete the comment.
...@@ -219,7 +219,7 @@ We'll also need a view which corrosponds to an individual comment, and can be us ...@@ -219,7 +219,7 @@ We'll also need a view which corrosponds to an individual comment, and can be us
comment.save() comment.save()
return JSONResponse(serializer.data) return JSONResponse(serializer.data)
else: else:
return JSONResponse(serializer.error_data, status=400) return JSONResponse(serializer.errors, status=400)
elif request.method == 'DELETE': elif request.method == 'DELETE':
comment.delete() comment.delete()
......
...@@ -61,7 +61,7 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On ...@@ -61,7 +61,7 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On
comment.save() comment.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
else: else:
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Our instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious. Our instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious.
...@@ -87,7 +87,7 @@ Our instance view is an improvement over the previous example. It's a little mo ...@@ -87,7 +87,7 @@ Our instance view is an improvement over the previous example. It's a little mo
comment.save() comment.save()
return Response(serializer.data) return Response(serializer.data)
else: else:
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE': elif request.method == 'DELETE':
comment.delete() comment.delete()
......
...@@ -31,8 +31,6 @@ We'll start by rewriting the root view as a class based view. All this involves ...@@ -31,8 +31,6 @@ We'll start by rewriting the root view as a class based view. All this involves
return Response(serializer.serialized, status=status.HTTP_201_CREATED) return Response(serializer.serialized, status=status.HTTP_201_CREATED)
return Response(serializer.serialized_errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.serialized_errors, status=status.HTTP_400_BAD_REQUEST)
comment_root = CommentRoot.as_view()
So far, so good. It looks pretty similar to the previous case, but we've got better seperation between the different HTTP methods. We'll also need to update the instance view. So far, so good. It looks pretty similar to the previous case, but we've got better seperation between the different HTTP methods. We'll also need to update the instance view.
class CommentInstance(APIView): class CommentInstance(APIView):
...@@ -58,16 +56,28 @@ So far, so good. It looks pretty similar to the previous case, but we've got be ...@@ -58,16 +56,28 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
comment = serializer.deserialized comment = serializer.deserialized
comment.save() comment.save()
return Response(serializer.data) return Response(serializer.data)
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None): def delete(self, request, pk, format=None):
comment = self.get_object(pk) comment = self.get_object(pk)
comment.delete() comment.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
comment_instance = CommentInstance.as_view()
That's looking good. Again, it's still pretty similar to the function based view right now. That's looking good. Again, it's still pretty similar to the function based view right now.
We'll also need to refactor our URLconf slightly now we're using class based views.
from django.conf.urls import patterns, url
from djangorestframework.urlpatterns import format_suffix_patterns
from blogpost import views
urlpatterns = patterns('',
url(r'^$', views.CommentRoot.as_view()),
url(r'^(?P<pk>[0-9]+)$', views.CommentInstance.as_view())
)
urlpatterns = format_suffix_patterns(urlpatterns)
Okay, we're done. If you run the development server everything should be working just as before. Okay, we're done. If you run the development server everything should be working just as before.
## Using mixins ## Using mixins
...@@ -95,8 +105,6 @@ Let's take a look at how we can compose our views by using the mixin classes. ...@@ -95,8 +105,6 @@ Let's take a look at how we can compose our views by using the mixin classes.
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
comment_root = CommentRoot.as_view()
We'll take a moment to examine exactly what's happening here - We're building our view using `MultipleObjectBaseView`, and adding in `ListModelMixin` and `CreateModelMixin`. We'll take a moment to examine exactly what's happening here - We're building our view using `MultipleObjectBaseView`, and adding in `ListModelMixin` and `CreateModelMixin`.
The base class provides the core functionality, and the mixin classes provide the `.list()` and `.create()` actions. We're then explictly binding the `get` and `post` methods to the appropriate actions. Simple enough stuff so far. The base class provides the core functionality, and the mixin classes provide the `.list()` and `.create()` actions. We're then explictly binding the `get` and `post` methods to the appropriate actions. Simple enough stuff so far.
...@@ -117,8 +125,6 @@ The base class provides the core functionality, and the mixin classes provide th ...@@ -117,8 +125,6 @@ The base class provides the core functionality, and the mixin classes provide th
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) return self.destroy(request, *args, **kwargs)
comment_instance = CommentInstance.as_view()
Pretty similar. This time we're using the `SingleObjectBaseView` class to provide the core functionality, and adding in mixins to provide the `.retrieve()`, `.update()` and `.destroy()` actions. Pretty similar. This time we're using the `SingleObjectBaseView` class to provide the core functionality, and adding in mixins to provide the `.retrieve()`, `.update()` and `.destroy()` actions.
## Using generic class based views ## Using generic class based views
...@@ -134,16 +140,11 @@ Using the mixin classes we've rewritten the views to use slightly less code than ...@@ -134,16 +140,11 @@ Using the mixin classes we've rewritten the views to use slightly less code than
model = Comment model = Comment
serializer_class = CommentSerializer serializer_class = CommentSerializer
comment_root = CommentRoot.as_view()
class CommentInstance(generics.InstanceAPIView): class CommentInstance(generics.InstanceAPIView):
model = Comment model = Comment
serializer_class = CommentSerializer serializer_class = CommentSerializer
comment_instance = CommentInstance.as_view()
Wow, that's pretty concise. We've got a huge amount for free, and our code looks like good, clean, idomatic Django. Wow, that's pretty concise. We've got a huge amount for free, and our code looks like good, clean, idomatic Django.
Next we'll move onto [part 4 of the tutorial][2], where we'll take a look at how we can customize the behavior of our views to support a range of authentication, permissions, throttling and other aspects. Next we'll move onto [part 4 of the tutorial][2], where we'll take a look at how we can customize the behavior of our views to support a range of authentication, permissions, throttling and other aspects.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment