Simplifying bits of docs

This commit is contained in:
Tom Christie 2013-05-02 12:08:05 +01:00
parent 387250bee4
commit 74beaefd12
7 changed files with 71 additions and 116 deletions

View File

@ -42,39 +42,10 @@ Add `'rest_framework'` to your `INSTALLED_APPS` setting.
'rest_framework', 'rest_framework',
) )
If you're intending to use the browseable API you'll probably also want to add REST framework's login and logout views. Add the following to your root `urls.py` file.
urlpatterns = patterns('',
...
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
)
Note that the URL path can be whatever you want, but you must include `'rest_framework.urls'` with the `'rest_framework'` namespace.
# Example # Example
Let's take a look at a quick example of using REST framework to build a simple model-backed API. Let's take a look at a quick example of using REST framework to build a simple model-backed API for accessing users and groups.
We'll create a read-write API for accessing users and groups.
Any global settings for a REST framework API are kept in a single configuration dictionary named `REST_FRAMEWORK`. Start off by adding the following to your `settings.py` module:
REST_FRAMEWORK = {
# Use hyperlinked styles by default.
# Only used if the `serializer_class` attribute is not set on a view.
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.HyperlinkedModelSerializer',
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_APPS`.
We're ready to create our API now.
Here's our project's root `urls.py` module: Here's our project's root `urls.py` module:
from django.conf.urls.defaults import url, patterns, include from django.conf.urls.defaults import url, patterns, include
@ -91,8 +62,8 @@ Here's our project's root `urls.py` module:
# Routers provide an easy way of automatically determining the URL conf # Routers provide an easy way of automatically determining the URL conf
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet, name='user') router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet, name='group') router.register(r'groups', views.GroupViewSet)
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.
@ -102,6 +73,27 @@ Here's our project's root `urls.py` module:
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
) )
We'd also like to configure a couple of settings for our API.
Add the following to your `settings.py` module:
REST_FRAMEWORK = {
# Use hyperlinked styles by default.
# Only used if the `serializer_class` attribute is not set on a view.
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.HyperlinkedModelSerializer',
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_APPS` setting.
That's it, we're done!
# Documentation & Support # Documentation & Support
Full documentation for the project is available at [http://django-rest-framework.org][docs]. Full documentation for the project is available at [http://django-rest-framework.org][docs].

View File

@ -62,6 +62,10 @@ The following attributes control the basic view behavior.
* `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the `get_serializer_class()` method. * `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the `get_serializer_class()` method.
* `lookup_field` - The field that should be used to lookup individual model instances. Defaults to `'pk'`. The URL conf should include a keyword argument corresponding to this value. More complex lookup styles can be supported by overriding the `get_object()` method. * `lookup_field` - The field that should be used to lookup individual model instances. Defaults to `'pk'`. The URL conf should include a keyword argument corresponding to this value. More complex lookup styles can be supported by overriding the `get_object()` method.
**Shortcuts**:
* `model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class.
**Pagination**: **Pagination**:
The following attibutes are used to control pagination when used with list views. The following attibutes are used to control pagination when used with list views.
@ -75,7 +79,6 @@ The following attibutes are used to control pagination when used with list views
* `filter_backend` - The filter backend class that should be used for filtering the queryset. Defaults to the same value as the `FILTER_BACKEND` setting. * `filter_backend` - The filter backend class that should be used for filtering the queryset. Defaults to the same value as the `FILTER_BACKEND` setting.
* `allow_empty` - Determines if an empty list should successfully display zero results, or return a 404 response. Defaults to `True`, meaning empty lists will return sucessful `200 OK` responses, with zero results. * `allow_empty` - Determines if an empty list should successfully display zero results, or return a 404 response. Defaults to `True`, meaning empty lists will return sucessful `200 OK` responses, with zero results.
* `model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class.
### Methods ### Methods
@ -160,7 +163,7 @@ The following classes are the concrete generic views. If you're using generic v
Used for **create-only** endpoints. Used for **create-only** endpoints.
Provides `post` method handlers. Provides a `post` method handler.
Extends: [GenericAPIView], [CreateModelMixin] Extends: [GenericAPIView], [CreateModelMixin]

View File

@ -15,15 +15,18 @@ REST framework adds support for automatic URL routing to Django, and provides yo
Here's an example of a simple URL conf, that uses `DefaultRouter`. Here's an example of a simple URL conf, that uses `DefaultRouter`.
router = routers.SimpleRouter() router = routers.SimpleRouter()
router.register(r'users', UserViewSet, name='user') router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet, name='account') router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls urlpatterns = router.urls
There are three arguments to the `register()` method: There are two mandatory arguments to the `register()` method:
* `prefix` - The URL prefix to use for this set of routes. * `prefix` - The URL prefix to use for this set of routes.
* `viewset` - The viewset class. * `viewset` - The viewset class.
* `name` - The base to use for the URL names that are created.
Optionally, you may also specify an additional argument:
* `base_name` - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the `model` or `queryset` attribute on the viewset, if it has one.
The example above would generate the following URL patterns: The example above would generate the following URL patterns:
@ -101,6 +104,8 @@ The following example will only route to the `list` and `retrieve` actions, and
## Advanced custom routers ## Advanced custom routers
If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls()` method. The method should insect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute. If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method. The method should insect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.
You may also want to override the `get_default_base_name(self, viewset)` method, or else always explicitly set the `base_name` argument when registering your viewsets with the router.
[cite]: http://guides.rubyonrails.org/routing.html [cite]: http://guides.rubyonrails.org/routing.html

View File

@ -42,7 +42,7 @@ If we need to, we can bind this viewset into two seperate views, like so:
Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated. Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.
router = DefaultRouter() router = DefaultRouter()
router.register(r'users', UserViewSet, name='user') router.register(r'users', UserViewSet)
urlpatterns = router.urls urlpatterns = router.urls
Rather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example: Rather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:

View File

@ -113,8 +113,8 @@ Here's our project's root `urls.py` module:
# Routers provide an easy way of automatically determining the URL conf # Routers provide an easy way of automatically determining the URL conf
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet, name='user') router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet, name='group') router.register(r'groups', views.GroupViewSet)
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.

View File

@ -105,8 +105,8 @@ Here's our re-wired `urls.py` file.
# Create a router and register our viewsets with it. # Create a router and register our viewsets with it.
router = DefaultRouter() router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet, name='snippet') router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet, name='user') router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router. # The API URLs are now determined automatically by the router.
# Additionally, we include the login URLs for the browseable API. # Additionally, we include the login URLs for the browseable API.
@ -115,7 +115,7 @@ Here's our re-wired `urls.py` file.
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
) )
Registering the viewsets with the router is similar to providing a urlpattern. We include three arguments - the URL prefix for the views, the viewset itself, and the base name that should be used for constructing the URL names, such as `snippet-list`. Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.
The `DefaultRouter` class we're using also automatically creates the API root view for us, so we can now delete the `api_root` method from our `views` module. The `DefaultRouter` class we're using also automatically creates the API root view for us, so we can now delete the `api_root` method from our `views` module.
@ -123,7 +123,7 @@ The `DefaultRouter` class we're using also automatically creates the API root vi
Using viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf. Using viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.
That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using view sets is less explicit than building your views individually. That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.
## Reviewing our work ## Reviewing our work

View File

@ -8,7 +8,7 @@ Create a new Django project, and start a new app called `quickstart`. Once you'
First up we're going to define some serializers in `quickstart/serializers.py` that we'll use for our data representations. First up we're going to define some serializers in `quickstart/serializers.py` that we'll use for our data representations.
from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.models import User, Group
from rest_framework import serializers from rest_framework import serializers
@ -19,109 +19,64 @@ First up we're going to define some serializers in `quickstart/serializers.py` t
class GroupSerializer(serializers.HyperlinkedModelSerializer): class GroupSerializer(serializers.HyperlinkedModelSerializer):
permissions = serializers.ManySlugRelatedField(
slug_field='codename',
queryset=Permission.objects.all()
)
class Meta: class Meta:
model = Group model = Group
fields = ('url', 'name', 'permissions') fields = ('url', 'name')
Notice that we're using hyperlinked relations in this case, with `HyperlinkedModelSerializer`. You can also use primary key and various other relationships, but hyperlinking is good RESTful design. Notice that we're using hyperlinked relations in this case, with `HyperlinkedModelSerializer`. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.
We've also overridden the `permission` field on the `GroupSerializer`. In this case we don't want to use a hyperlinked representation, but instead use the list of permission codenames associated with the group, so we've used a `ManySlugRelatedField`, using the `codename` field for the representation.
## Views ## Views
Right, we'd better write some views then. Open `quickstart/views.py` and get typing. Right, we'd better write some views then. Open `quickstart/views.py` and get typing.
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from rest_framework import generics from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.reverse import reverse
from rest_framework.response import Response
from quickstart.serializers import UserSerializer, GroupSerializer from quickstart.serializers import UserSerializer, GroupSerializer
@api_view(['GET']) class UserViewSet(viewsets.ModelViewSet):
def api_root(request, format=None):
""" """
The entry endpoint of our API. API endpoint that allows users to be viewed or edited.
""" """
return Response({ queryset = User.objects.all()
'users': reverse('user-list', request=request),
'groups': reverse('group-list', request=request),
})
class UserList(generics.ListCreateAPIView):
"""
API endpoint that represents a list of users.
"""
model = User
serializer_class = UserSerializer serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView): class GroupViewSet(viewsets.ModelViewSet):
""" """
API endpoint that represents a single user. API endpoint that allows groups to be viewed or edited.
""" """
model = User queryset = Group.objects.all()
serializer_class = UserSerializer
class GroupList(generics.ListCreateAPIView):
"""
API endpoint that represents a list of groups.
"""
model = Group
serializer_class = GroupSerializer serializer_class = GroupSerializer
Rather that write multiple views we're grouping together all the common behavior into classes called `ViewSets`.
class GroupDetail(generics.RetrieveUpdateDestroyAPIView): We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.
"""
API endpoint that represents a single group.
"""
model = Group
serializer_class = GroupSerializer
Let's take a moment to look at what we've done here before we move on. We have one function-based view representing the root of the API, and four class-based views which map to our database models, and specify which serializers should be used for representing that data. Pretty simple stuff.
## URLs ## URLs
Okay, let's wire this baby up. On to `quickstart/urls.py`... Okay, now let's wire up the API URLs. On to `quickstart/urls.py`...
from django.conf.urls import patterns, url, include from django.conf.urls import patterns, url, include
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework import routers
from quickstart.views import UserList, UserDetail, GroupList, GroupDetail from quickstart import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
urlpatterns = patterns('quickstart.views', # Wire up our API using automatic URL routing.
url(r'^$', 'api_root'), # Additionally, we include login URLs for the browseable API.
url(r'^users/$', UserList.as_view(), name='user-list'), urlpatterns = patterns('',
url(r'^users/(?P<pk>\d+)/$', UserDetail.as_view(), name='user-detail'), url(r'^', include(router.urls)),
url(r'^groups/$', GroupList.as_view(), name='group-list'),
url(r'^groups/(?P<pk>\d+)/$', GroupDetail.as_view(), name='group-detail'),
)
# Format suffixes
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'api'])
# Default login/logout views
urlpatterns += patterns('',
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
) )
There's a few things worth noting here. Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
Firstly the names `user-detail` and `group-detail` are important. We're using the default hyperlinked relationships without explicitly specifying the view names, so we need to use names of the style `{modelname}-detail` to represent the model instance views. Again, if we need more control over the API URLs we can simply drop down to using regular class based views, and writing the URL conf explicitly.
Secondly, we're modifying the urlpatterns using `format_suffix_patterns`, to append optional `.json` style suffixes to our URLs. Note that we're also including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browseable API.
Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browseable API.
## Settings ## Settings