diff --git a/README.md b/README.md index f5e05bfda..f665481ad 100644 --- a/README.md +++ b/README.md @@ -42,39 +42,10 @@ Add `'rest_framework'` to your `INSTALLED_APPS` setting. '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 -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: 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 router = routers.DefaultRouter() - router.register(r'users', views.UserViewSet, name='user') - router.register(r'groups', views.GroupViewSet, name='group') + router.register(r'users', views.UserViewSet) + router.register(r'groups', views.GroupViewSet) # 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')) ) +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 Full documentation for the project is available at [http://django-rest-framework.org][docs]. diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 4a24b7c73..5de12bdb5 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -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. * `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**: 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. * `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 @@ -160,7 +163,7 @@ The following classes are the concrete generic views. If you're using generic v Used for **create-only** endpoints. -Provides `post` method handlers. +Provides a `post` method handler. Extends: [GenericAPIView], [CreateModelMixin] diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 7495b91c1..6588d7e51 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -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`. router = routers.SimpleRouter() - router.register(r'users', UserViewSet, name='user') - router.register(r'accounts', AccountViewSet, name='account') + router.register(r'users', UserViewSet) + router.register(r'accounts', AccountViewSet) 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. * `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: @@ -101,6 +104,8 @@ The following example will only route to the `list` and `retrieve` actions, and ## 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 diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 8af35bb8d..d98f37d84 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -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. router = DefaultRouter() - router.register(r'users', UserViewSet, name='user') + router.register(r'users', UserViewSet) 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: diff --git a/docs/index.md b/docs/index.md index 7436794b3..b7acb9798 100644 --- a/docs/index.md +++ b/docs/index.md @@ -113,8 +113,8 @@ Here's our project's root `urls.py` module: # Routers provide an easy way of automatically determining the URL conf router = routers.DefaultRouter() - router.register(r'users', views.UserViewSet, name='user') - router.register(r'groups', views.GroupViewSet, name='group') + router.register(r'users', views.UserViewSet) + router.register(r'groups', views.GroupViewSet) # Wire up our API using automatic URL routing. diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 25a974a1f..4b01d3e00 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -105,8 +105,8 @@ Here's our re-wired `urls.py` file. # Create a router and register our viewsets with it. router = DefaultRouter() - router.register(r'snippets', views.SnippetViewSet, name='snippet') - router.register(r'users', views.UserViewSet, name='user') + router.register(r'snippets', views.SnippetViewSet) + router.register(r'users', views.UserViewSet) # The API URLs are now determined automatically by the router. # 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')) ) -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. @@ -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. -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 diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index 74084541d..a77161057 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -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. - from django.contrib.auth.models import User, Group, Permission + from django.contrib.auth.models import User, Group 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): - permissions = serializers.ManySlugRelatedField( - slug_field='codename', - queryset=Permission.objects.all() - ) - class Meta: 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. -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 Right, we'd better write some views then. Open `quickstart/views.py` and get typing. from django.contrib.auth.models import User, Group - from rest_framework import generics - from rest_framework.decorators import api_view - from rest_framework.reverse import reverse - from rest_framework.response import Response + from rest_framework import viewsets from quickstart.serializers import UserSerializer, GroupSerializer - @api_view(['GET']) - def api_root(request, format=None): + class UserViewSet(viewsets.ModelViewSet): """ - The entry endpoint of our API. + API endpoint that allows users to be viewed or edited. """ - return Response({ - '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 + queryset = User.objects.all() 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 - serializer_class = UserSerializer - - - class GroupList(generics.ListCreateAPIView): - """ - API endpoint that represents a list of groups. - """ - model = Group - serializer_class = GroupSerializer - - - class GroupDetail(generics.RetrieveUpdateDestroyAPIView): - """ - API endpoint that represents a single group. - """ - model = Group + queryset = Group.objects.all() 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. +Rather that write multiple views we're grouping together all the common behavior into classes called `ViewSets`. + +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. ## 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 rest_framework.urlpatterns import format_suffix_patterns - from quickstart.views import UserList, UserDetail, GroupList, GroupDetail - + from rest_framework import routers + from quickstart import views - urlpatterns = patterns('quickstart.views', - url(r'^$', 'api_root'), - url(r'^users/$', UserList.as_view(), name='user-list'), - url(r'^users/(?P\d+)/$', UserDetail.as_view(), name='user-detail'), - url(r'^groups/$', GroupList.as_view(), name='group-list'), - url(r'^groups/(?P\d+)/$', GroupDetail.as_view(), name='group-detail'), - ) + router = routers.DefaultRouter() + router.register(r'users', views.UserViewSet) + router.register(r'groups', views.GroupViewSet) - - # Format suffixes - urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'api']) - - - # Default login/logout views - urlpatterns += patterns('', + # Wire up our API using automatic URL routing. + # Additionally, we include login URLs for the browseable API. + urlpatterns = patterns('', + url(r'^', include(router.urls)), 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. - -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. +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. ## Settings