From 60e6bba12b6843b01194b6534fb56129ec0b2140 Mon Sep 17 00:00:00 2001 From: Alec Perkins Date: Sun, 9 Sep 2012 16:46:05 -0400 Subject: Browsable API doc topic --- docs/tutorial/2-requests-and-responses.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 89f92c4b..2c11d5ef 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -133,7 +133,12 @@ Now go and open the API in a web browser, by visiting [http://127.0.0.1:8000/][3 **Note: Right now the Browseable API only works with the CBV's. Need to fix that.** -**TODO: Describe browseable API awesomeness** +### Browsability + +Because the API chooses a return format based on what the client asks for, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a browser. This allows for the API to be easily browsable and usable by humans. + +See the [browsable api][4] topic for more information about the browsable API feature and how to customize it. + ## What's next? @@ -142,4 +147,5 @@ In [tutorial part 3][4], we'll start using class based views, and see how generi [json-url]: http://example.com/api/items/4.json [2]: 1-serialization.md [3]: http://127.0.0.1:8000/ -[4]: 3-class-based-views.md \ No newline at end of file +[4]: ../topics/browsable-api.md +[5]: 3-class-based-views.md \ No newline at end of file -- cgit v1.2.3 From eb761be9d058dbfb9214f200b941496524dc0ded Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 14 Sep 2012 12:43:14 +0100 Subject: Flesh out resources/routers part of tutorial --- docs/tutorial/6-resource-orientated-projects.md | 37 +++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/6-resource-orientated-projects.md b/docs/tutorial/6-resource-orientated-projects.md index 4282c25d..0d0cfac5 100644 --- a/docs/tutorial/6-resource-orientated-projects.md +++ b/docs/tutorial/6-resource-orientated-projects.md @@ -1,3 +1,7 @@ +In REST framework Resources classes are just View classes that don't have any handler methods bound to them. This allows us to seperate out the behaviour of the classes from how that behaviour should be bound to a set of URLs. + +For instance, given our serializers + serializers.py class BlogPostSerializer(URLModelSerializer): @@ -8,21 +12,44 @@ serializers.py class Meta: model = Comment +We can re-write our 4 sets of views into something more compact... + resources.py class BlogPostResource(ModelResource): serializer_class = BlogPostSerializer model = BlogPost - permissions = [AdminOrAnonReadonly()] - throttles = [AnonThrottle(rate='5/min')] + permissions_classes = (permissions.IsAuthenticatedOrReadOnly,) + throttle_classes = (throttles.UserRateThrottle,) class CommentResource(ModelResource): serializer_class = CommentSerializer model = Comment - permissions = [AdminOrAnonReadonly()] - throttles = [AnonThrottle(rate='5/min')] + permissions_classes = (permissions.IsAuthenticatedOrReadOnly,) + throttle_classes = (throttles.UserRateThrottle,) + +The handler methods only get bound to the actions when we define the URLConf. Here's our urls.py: + + comment_root = CommentResource.as_view(actions={ + 'get': 'list', + 'post': 'create' + }) + comment_instance = CommentInstance.as_view(actions={ + 'get': 'retrieve', + 'put': 'update', + 'delete': 'destroy' + }) + ... # And for blog post + + urlpatterns = patterns('blogpost.views', + url(r'^$', comment_root), + url(r'^(?P[0-9]+)$', comment_instance) + ... # And for blog post + ) + +## Using Routers -Now that we're using Resources rather than Views, we don't need to design the urlconf ourselves. The conventions for wiring up resources into views and urls are handled automatically. All we need to do is register the appropriate resources with a router, and let it do the rest. Here's our re-wired `urls.py` file. +Right now that hasn't really saved us a lot of code. However, now that we're using Resources rather than Views, we actually don't need to design the urlconf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using `Router` classes. All we need to do is register the appropriate resources with a router, and let it do the rest. Here's our re-wired `urls.py` file. from blog import resources from djangorestframework.routers import DefaultRouter -- cgit v1.2.3 From 308677037f1b1f2edbd2527beac8505033c98bdc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 17 Sep 2012 20:19:45 +0100 Subject: Tweak docs, fix .error_data -> .errors --- docs/tutorial/1-serialization.md | 4 ++-- docs/tutorial/2-requests-and-responses.md | 4 ++-- docs/tutorial/3-class-based-views.md | 29 +++++++++++++++-------------- 3 files changed, 19 insertions(+), 18 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 610d8ed1..34990084 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -194,7 +194,7 @@ The root of our API is going to be a view that supports listing all the existing comment.save() return JSONResponse(serializer.data, status=201) 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. @@ -219,7 +219,7 @@ We'll also need a view which corrosponds to an individual comment, and can be us comment.save() return JSONResponse(serializer.data) else: - return JSONResponse(serializer.error_data, status=400) + return JSONResponse(serializer.errors, status=400) elif request.method == 'DELETE': comment.delete() diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 2c11d5ef..ffc5f269 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -61,7 +61,7 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On comment.save() return Response(serializer.data, status=status.HTTP_201_CREATED) 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. @@ -87,7 +87,7 @@ Our instance view is an improvement over the previous example. It's a little mo comment.save() return Response(serializer.data) 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': comment.delete() diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 24785179..3c8f1207 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -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_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. class CommentInstance(APIView): @@ -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.save() 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): comment = self.get_object(pk) comment.delete() 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. + +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[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. ## Using mixins @@ -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): 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`. 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 def delete(self, 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. ## Using generic class based views @@ -134,16 +140,11 @@ Using the mixin classes we've rewritten the views to use slightly less code than model = Comment serializer_class = CommentSerializer - comment_root = CommentRoot.as_view() - class CommentInstance(generics.InstanceAPIView): model = Comment 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. 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. -- cgit v1.2.3 From 575630d7c34b8ee23dad379c4bbd01eba477e4a2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 19 Sep 2012 13:02:10 +0100 Subject: Use named links in tutorial docs --- docs/tutorial/2-requests-and-responses.md | 16 +++++++-------- docs/tutorial/3-class-based-views.md | 8 ++++---- .../4-authentication-permissions-and-throttling.md | 6 ++++-- .../5-relationships-and-hyperlinked-apis.md | 6 ++++-- docs/tutorial/6-resource-orientated-projects.md | 24 +++++++++++----------- 5 files changed, 32 insertions(+), 28 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index ffc5f269..906f11d0 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -125,11 +125,11 @@ We don't necessarily need to add these extra url patterns in, but it gives us a ## How's it looking? -Go ahead and test the API from the command line, as we did in [tutorial part 1][2]. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests. +Go ahead and test the API from the command line, as we did in [tutorial part 1][tut-1]. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests. **TODO: Describe using accept headers, content-type headers, and format suffixed URLs** -Now go and open the API in a web browser, by visiting [http://127.0.0.1:8000/][3]." +Now go and open the API in a web browser, by visiting [http://127.0.0.1:8000/][devserver]." **Note: Right now the Browseable API only works with the CBV's. Need to fix that.** @@ -137,15 +137,15 @@ Now go and open the API in a web browser, by visiting [http://127.0.0.1:8000/][3 Because the API chooses a return format based on what the client asks for, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a browser. This allows for the API to be easily browsable and usable by humans. -See the [browsable api][4] topic for more information about the browsable API feature and how to customize it. +See the [browsable api][browseable-api] topic for more information about the browsable API feature and how to customize it. ## What's next? -In [tutorial part 3][4], we'll start using class based views, and see how generic views reduce the amount of code we need to write. +In [tutorial part 3][tut-3], we'll start using class based views, and see how generic views reduce the amount of code we need to write. [json-url]: http://example.com/api/items/4.json -[2]: 1-serialization.md -[3]: http://127.0.0.1:8000/ -[4]: ../topics/browsable-api.md -[5]: 3-class-based-views.md \ No newline at end of file +[devserver]: http://127.0.0.1:8000/ +[browseable-api]: ../topics/browsable-api.md +[tut-1]: 1-serialization.md +[tut-3]: 3-class-based-views.md \ No newline at end of file diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 3c8f1207..79866f0d 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -1,6 +1,6 @@ # Tutorial 3: Class Based Views -We can also write our API views using class based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code [DRY][1]. +We can also write our API views using class based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code [DRY][dry]. ## Rewriting our API using class based views @@ -147,7 +147,7 @@ Using the mixin classes we've rewritten the views to use slightly less code than 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][tut-4], 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. -[1]: http://en.wikipedia.org/wiki/Don't_repeat_yourself -[2]: 4-authentication-permissions-and-throttling.md +[dry]: http://en.wikipedia.org/wiki/Don't_repeat_yourself +[tut-4]: 4-authentication-permissions-and-throttling.md diff --git a/docs/tutorial/4-authentication-permissions-and-throttling.md b/docs/tutorial/4-authentication-permissions-and-throttling.md index 5c37ae13..c8d7cbd3 100644 --- a/docs/tutorial/4-authentication-permissions-and-throttling.md +++ b/docs/tutorial/4-authentication-permissions-and-throttling.md @@ -1,3 +1,5 @@ -[part 5][5] +# Tutorial 4: Authentication & Permissions -[5]: 5-relationships-and-hyperlinked-apis.md \ No newline at end of file +Nothing to see here. Onwards to [part 5][tut-5]. + +[tut-5]: 5-relationships-and-hyperlinked-apis.md \ No newline at end of file diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 3d9598d7..a76f81e8 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -1,9 +1,11 @@ +# Tutorial 5 - Relationships & Hyperlinked APIs + **TODO** * Create BlogPost model * Demonstrate nested relationships * Demonstrate and describe hyperlinked relationships -[part 6][1] +Onwards to [part 6][tut-6]. -[1]: 6-resource-orientated-projects.md +[tut-6]: 6-resource-orientated-projects.md diff --git a/docs/tutorial/6-resource-orientated-projects.md b/docs/tutorial/6-resource-orientated-projects.md index 0d0cfac5..7da409fb 100644 --- a/docs/tutorial/6-resource-orientated-projects.md +++ b/docs/tutorial/6-resource-orientated-projects.md @@ -1,18 +1,15 @@ -In REST framework Resources classes are just View classes that don't have any handler methods bound to them. This allows us to seperate out the behaviour of the classes from how that behaviour should be bound to a set of URLs. +# Tutorial 6 - Resources -For instance, given our serializers +Resource classes are just View classes that don't have any handler methods bound to them. The actions on a resource are defined, -serializers.py +This allows us to: - class BlogPostSerializer(URLModelSerializer): - class Meta: - model = BlogPost +* Encapsulate common behaviour accross a class of views, in a single Resource class. +* Seperate out the actions of a Resource from the specfics of how those actions should be bound to a particular set of URLs. - class CommentSerializer(URLModelSerializer): - class Meta: - model = Comment +## Refactoring to use Resources, not Views -We can re-write our 4 sets of views into something more compact... +For instance, we can re-write our 4 sets of views into something more compact... resources.py @@ -28,6 +25,7 @@ resources.py permissions_classes = (permissions.IsAuthenticatedOrReadOnly,) throttle_classes = (throttles.UserRateThrottle,) +## Binding Resources to URLs explicitly The handler methods only get bound to the actions when we define the URLConf. Here's our urls.py: comment_root = CommentResource.as_view(actions={ @@ -71,6 +69,8 @@ We've reached the end of our tutorial. If you want to get more involved in the * Contribute on GitHub by reviewing issues, and submitting issues or pull requests. * Join the REST framework group, and help build the community. -* Follow me [on Twitter](https://twitter.com/_tomchristie) and say hi. +* Follow me [on Twitter][twitter] and say hi. -Now go build something great. \ No newline at end of file +**Now go build some awesome things.** + +[twitter]: https://twitter.com/_tomchristie \ No newline at end of file -- cgit v1.2.3 From 4b691c402707775c3048a90531024f3bc5be6f91 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:06:27 +0100 Subject: Change package name: djangorestframework -> rest_framework --- docs/tutorial/1-serialization.md | 16 ++++++++-------- docs/tutorial/2-requests-and-responses.md | 10 +++++----- docs/tutorial/3-class-based-views.md | 14 +++++++------- docs/tutorial/6-resource-orientated-projects.md | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 34990084..e3656bd0 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -45,11 +45,11 @@ The simplest way to get up and running will probably be to use an `sqlite3` data } } -We'll also need to add our new `blog` app and the `djangorestframework` app to `INSTALLED_APPS`. +We'll also need to add our new `blog` app and the `rest_framework` app to `INSTALLED_APPS`. INSTALLED_APPS = ( ... - 'djangorestframework', + 'rest_framework', 'blog' ) @@ -81,7 +81,7 @@ Don't forget to sync the database for the first time. We're going to create a simple Web API that we can use to edit these comment objects with. The first thing we need is a way of serializing and deserializing the objects into representations such as `json`. We do this by declaring serializers, that work very similarly to Django's forms. Create a file in the project named `serializers.py` and add the following. from blog import models - from djangorestframework import serializers + from rest_framework import serializers class CommentSerializer(serializers.Serializer): @@ -114,8 +114,8 @@ Okay, once we've got a few imports out of the way, we'd better create a few comm from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework.renderers import JSONRenderer - from djangorestframework.parsers import JSONParser + from rest_framework.renderers import JSONRenderer + from rest_framework.parsers import JSONParser c1 = Comment(email='leila@example.com', content='nothing to say') c2 = Comment(email='tom@example.com', content='foo bar') @@ -159,8 +159,8 @@ Edit the `blog/views.py` file, and add the following. from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework.renderers import JSONRenderer - from djangorestframework.parsers import JSONParser + from rest_framework.renderers import JSONRenderer + from rest_framework.parsers import JSONParser from django.http import HttpResponse @@ -251,4 +251,4 @@ Our API views don't do anything particularly special at the moment, beyond serve We'll see how we can start to improve things in [part 2 of the tutorial][tut-2]. [virtualenv]: http://www.virtualenv.org/en/latest/index.html -[tut-2]: 2-requests-and-responses.md \ No newline at end of file +[tut-2]: 2-requests-and-responses.md diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 906f11d0..d889b1e0 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -40,9 +40,9 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import status - from djangorestframework.decorators import api_view - from djangorestframework.response import Response + from rest_framework import status + from rest_framework.decorators import api_view + from rest_framework.response import Response @api_view(['GET', 'POST']) def comment_root(request): @@ -112,7 +112,7 @@ and Now update the `urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. from django.conf.urls import patterns, url - from djangorestframework.urlpatterns import format_suffix_patterns + from rest_framework.urlpatterns import format_suffix_patterns urlpatterns = patterns('blogpost.views', url(r'^$', 'comment_root'), @@ -148,4 +148,4 @@ In [tutorial part 3][tut-3], we'll start using class based views, and see how ge [devserver]: http://127.0.0.1:8000/ [browseable-api]: ../topics/browsable-api.md [tut-1]: 1-serialization.md -[tut-3]: 3-class-based-views.md \ No newline at end of file +[tut-3]: 3-class-based-views.md diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 79866f0d..8db29308 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -9,9 +9,9 @@ We'll start by rewriting the root view as a class based view. All this involves from blog.models import Comment from blog.serializers import CommentSerializer from django.http import Http404 - from djangorestframework.views import APIView - from djangorestframework.response import Response - from djangorestframework import status + from rest_framework.views import APIView + from rest_framework.response import Response + from rest_framework import status class CommentRoot(APIView): @@ -68,7 +68,7 @@ That's looking good. Again, it's still pretty similar to the function based vie 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 rest_framework.urlpatterns import format_suffix_patterns from blogpost import views urlpatterns = patterns('', @@ -90,8 +90,8 @@ Let's take a look at how we can compose our views by using the mixin classes. from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import mixins - from djangorestframework import generics + from rest_framework import mixins + from rest_framework import generics class CommentRoot(mixins.ListModelMixin, mixins.CreateModelMixin, @@ -133,7 +133,7 @@ Using the mixin classes we've rewritten the views to use slightly less code than from blog.models import Comment from blog.serializers import CommentSerializer - from djangorestframework import generics + from rest_framework import generics class CommentRoot(generics.RootAPIView): diff --git a/docs/tutorial/6-resource-orientated-projects.md b/docs/tutorial/6-resource-orientated-projects.md index 7da409fb..3c3e7fed 100644 --- a/docs/tutorial/6-resource-orientated-projects.md +++ b/docs/tutorial/6-resource-orientated-projects.md @@ -50,7 +50,7 @@ The handler methods only get bound to the actions when we define the URLConf. He Right now that hasn't really saved us a lot of code. However, now that we're using Resources rather than Views, we actually don't need to design the urlconf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using `Router` classes. All we need to do is register the appropriate resources with a router, and let it do the rest. Here's our re-wired `urls.py` file. from blog import resources - from djangorestframework.routers import DefaultRouter + from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(resources.BlogPostResource) @@ -73,4 +73,4 @@ We've reached the end of our tutorial. If you want to get more involved in the **Now go build some awesome things.** -[twitter]: https://twitter.com/_tomchristie \ No newline at end of file +[twitter]: https://twitter.com/_tomchristie -- cgit v1.2.3 From 921c5840aa64c184bcfa6cc2344d0fdca406548b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 25 Sep 2012 12:21:35 +0100 Subject: Fix incorrect bit of tutorial --- docs/tutorial/3-class-based-views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 8db29308..25d5773f 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -53,7 +53,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be comment = self.get_object(pk) serializer = CommentSerializer(request.DATA, instance=comment) if serializer.is_valid(): - comment = serializer.deserialized + comment = serializer.object comment.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -- cgit v1.2.3 From 4fb57d28e60c02593f14ba7cdebed4e478371512 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 25 Sep 2012 12:27:46 +0100 Subject: Add csrf note --- docs/tutorial/1-serialization.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index e3656bd0..04942834 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -159,9 +159,10 @@ Edit the `blog/views.py` file, and add the following. from blog.models import Comment from blog.serializers import CommentSerializer + from django.http import HttpResponse + from django.views.decorators.csrf import csrf_exempt from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser - from django.http import HttpResponse class JSONResponse(HttpResponse): @@ -177,6 +178,7 @@ Edit the `blog/views.py` file, and add the following. The root of our API is going to be a view that supports listing all the existing comments, or creating a new comment. + @csrf_exempt def comment_root(request): """ List all comments, or create a new comment. @@ -196,8 +198,11 @@ The root of our API is going to be a view that supports listing all the existing else: return JSONResponse(serializer.errors, status=400) +Note that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as `csrf_exempt`. This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now. + We'll also need a view which corrosponds to an individual comment, and can be used to retrieve, update or delete the comment. + @csrf_exempt def comment_instance(request, pk): """ Retrieve, update or delete a comment instance. -- cgit v1.2.3 From 6fc5581a8fba45fe22920e65b2d0790d483a8378 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 25 Sep 2012 13:40:16 +0100 Subject: Add readonly 'id' field --- docs/tutorial/1-serialization.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'docs/tutorial') diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 04942834..cd4b7558 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -67,7 +67,7 @@ For the purposes of this tutorial we're going to start by creating a simple `Com from django.db import models - class Comment(models.Model): + class Comment(models.Model): email = models.EmailField() content = models.CharField(max_length=200) created = models.DateTimeField(auto_now_add=True) @@ -85,6 +85,7 @@ We're going to create a simple Web API that we can use to edit these comment obj class CommentSerializer(serializers.Serializer): + id = serializers.IntegerField(readonly=True) email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() @@ -128,13 +129,13 @@ We've now got a few comment instances to play with. Let's take a look at serial serializer = CommentSerializer(instance=c1) serializer.data - # {'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774, tzinfo=)} + # {'id': 1, 'email': u'leila@example.com', 'content': u'nothing to say', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774, tzinfo=)} At this point we've translated the model instance into python native datatypes. To finalise the serialization process we render the data into `json`. stream = JSONRenderer().render(serializer.data) stream - # '{"email": "leila@example.com", "content": "nothing to say", "created": "2012-08-22T16:20:09.822"}' + # '{"id": 1, "email": "leila@example.com", "content": "nothing to say", "created": "2012-08-22T16:20:09.822"}' Deserialization is similar. First we parse a stream into python native datatypes... -- cgit v1.2.3