Deserialization is similar. First we parse a stream into Python native datatypes...
Deserialization is similar. First we parse a stream into Python native datatypes...
# This import will use either `StringIO.StringIO` or `io.BytesIO`
# as appropriate, depending on if we're running Python 2 or Python 3.
...
...
@@ -196,7 +196,7 @@ Deserialization is similar. First we parse a stream into Python native datatype
# True
serializer.object
# <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.
We can also serialize querysets instead of model instances. To do so we simply add a `many=True` flag to the serializer arguments.
...
...
@@ -264,7 +264,7 @@ The root of our API is going to be a view that supports listing all the existing
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.
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 corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet.
...
...
@@ -277,11 +277,11 @@ We'll also need a view which corresponds to an individual snippet, and can be us
@@ -33,7 +33,7 @@ The wrappers also provide behaviour such as returning `405 Method Not Allowed` r
## Pulling it all together
Okay, let's go ahead and start using these new components to write a few views.
Okay, let's go ahead and start using these new components to write a few views.
We don't need our `JSONResponse` class in `views.py` anymore, so go ahead and delete that. Once that's done we can start refactoring our views slightly.
...
...
@@ -69,7 +69,7 @@ Here is the view for an individual snippet, in the `views.py` module.
def snippet_detail(request, pk):
"""
Retrieve, update or delete a snippet instance.
"""
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
...
...
@@ -115,7 +115,7 @@ Now update the `urls.py` file slightly, to append a set of `format_suffix_patter
So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in `views.py`.
So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in `views.py`.
class SnippetDetail(APIView):
"""
...
...
@@ -72,7 +72,7 @@ We'll also need to refactor our `urls.py` slightly now we're using class based v
@@ -73,12 +73,12 @@ We'll also add a couple of views to `views.py`. We'd like to just use read-only
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
Make sure to also import the `UserSerializer` class
from snippets.serializers import UserSerializer
...
...
@@ -129,7 +129,7 @@ Then, add the following property to **both** the `SnippetList` and `SnippetDetai
If you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.
We can add a login view for use with the browsable API, by editing the URLconf in our project-level urls.py file.
We can add a login view for use with the browsable API, by editing the URLconf in our project-level `urls.py` file.
Add the following import at the top of the file:
...
...
@@ -157,8 +157,8 @@ To do that we're going to need to create a custom permission.
In the snippets app, create a new file, `permissions.py`
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
...
...
@@ -201,7 +201,7 @@ If we try to create a snippet without authenticating, we'll get an error:
We can make a successful request by including the username and password of one of the users we created earlier.
curl -X POST http://127.0.0.1:8000/snippets/ -d "code=print 789" -u tom:password
At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.
At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.
## Creating an endpoint for the root of our API
Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the `@api_view` decorator we introduced earlier.
Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the `@api_view` decorator we introduced earlier. In your `snippets/views.py` add:
from rest_framework import renderers
from rest_framework.decorators import api_view
...
...
@@ -29,7 +29,7 @@ Unlike all our other API endpoints, we don't want to use JSON, but instead just
The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.
Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method. In your `snippets.views` add:
Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method. In your `snippets/views.py` add:
from rest_framework import renderers
from rest_framework.response import Response
...
...
@@ -37,13 +37,13 @@ Instead of using a concrete generic view, we'll use the base class for represent
@@ -105,7 +105,7 @@ If we're going to have a hyperlinked API, we need to make sure we name our URL p
* Our user serializer includes a field that refers to `'snippet-detail'`.
* Our snippet and user serializers include `'url'` fields that by default will refer to `'{model_name}-detail'`, which in this case will be `'snippet-detail'` and `'user-detail'`.
After adding all those names into our URLconf, our final `'urls.py'` file should look something like this:
After adding all those names into our URLconf, our final `snippets/urls.py` file should look something like this: