Commit 9ae5a483 by Tom Christie

Latest docs update

parent 66f25af5
...@@ -307,7 +307,9 @@ WSGIPassAuthorization On ...@@ -307,7 +307,9 @@ WSGIPassAuthorization On
'rest_framework.authtoken' 'rest_framework.authtoken'
) )
</code></pre> </code></pre>
<p>Make sure to run <code>manage.py syncdb</code> after changing your settings. The <code>authtoken</code> database tables are managed by south (see <a href="#schema-migrations">Schema migrations</a> below).</p> <hr />
<p><strong>Note:</strong> Make sure to run <code>manage.py syncdb</code> after changing your settings. The <code>rest_framework.authtoken</code> app provides both Django (from v1.7) and South database migrations. See <a href="#schema-migrations">Schema migrations</a> below.</p>
<hr />
<p>You'll also need to create tokens for your users.</p> <p>You'll also need to create tokens for your users.</p>
<pre class="prettyprint lang-py"><code>from rest_framework.authtoken.models import Token <pre class="prettyprint lang-py"><code>from rest_framework.authtoken.models import Token
...@@ -362,7 +364,10 @@ for user in User.objects.all(): ...@@ -362,7 +364,10 @@ for user in User.objects.all():
</code></pre> </code></pre>
<p>Note that the default <code>obtain_auth_token</code> view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the <code>obtain_auth_token</code> view, you can do so by overriding the <code>ObtainAuthToken</code> view class, and using that in your url conf instead.</p> <p>Note that the default <code>obtain_auth_token</code> view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the <code>obtain_auth_token</code> view, you can do so by overriding the <code>ObtainAuthToken</code> view class, and using that in your url conf instead.</p>
<h4 id="schema-migrations">Schema migrations</h4> <h4 id="schema-migrations">Schema migrations</h4>
<p>The <code>rest_framework.authtoken</code> app includes a south migration that will create the authtoken table.</p> <p>The <code>rest_framework.authtoken</code> app includes both Django native migrations (for Django versions &gt;1.7) and South migrations (for Django versions &lt;1.7) that will create the authtoken table.</p>
<hr />
<p><strong>Note</strong>: From REST Framework v2.4.0 using South with Django &lt;1.7 requires upgrading South v1.0+</p>
<hr />
<p>If you're using a <a href="https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model">custom user model</a> you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created.</p> <p>If you're using a <a href="https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model">custom user model</a> you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created.</p>
<p>You can do so by inserting a <code>needed_by</code> attribute in your user migration:</p> <p>You can do so by inserting a <code>needed_by</code> attribute in your user migration:</p>
<pre class="prettyprint lang-py"><code>class Migration: <pre class="prettyprint lang-py"><code>class Migration:
......
...@@ -213,6 +213,7 @@ a.fusion-poweredby { ...@@ -213,6 +213,7 @@ a.fusion-poweredby {
<li><a href="#drf-compound-fields">DRF Compound Fields</a></li> <li><a href="#drf-compound-fields">DRF Compound Fields</a></li>
<li><a href="#drf-extra-fields">DRF Extra Fields</a></li> <li><a href="#drf-extra-fields">DRF Extra Fields</a></li>
<li><a href="#django-rest-framework-gis">django-rest-framework-gis</a></li> <li><a href="#django-rest-framework-gis">django-rest-framework-gis</a></li>
<li><a href="#django-rest-framework-hstore">django-rest-framework-hstore</a></li>
<div class="promo"> <div class="promo">
...@@ -329,10 +330,11 @@ class UserSerializer(serializers.ModelSerializer): ...@@ -329,10 +330,11 @@ class UserSerializer(serializers.ModelSerializer):
<p>A Boolean representation.</p> <p>A Boolean representation.</p>
<p>Corresponds to <code>django.db.models.fields.BooleanField</code>.</p> <p>Corresponds to <code>django.db.models.fields.BooleanField</code>.</p>
<h2 id="charfield">CharField</h2> <h2 id="charfield">CharField</h2>
<p>A text representation, optionally validates the text to be shorter than <code>max_length</code> and longer than <code>min_length</code>.</p> <p>A text representation, optionally validates the text to be shorter than <code>max_length</code> and longer than <code>min_length</code>.
If <code>allow_none</code> is <code>False</code> (default), <code>None</code> values will be converted to an empty string.</p>
<p>Corresponds to <code>django.db.models.fields.CharField</code> <p>Corresponds to <code>django.db.models.fields.CharField</code>
or <code>django.db.models.fields.TextField</code>.</p> or <code>django.db.models.fields.TextField</code>.</p>
<p><strong>Signature:</strong> <code>CharField(max_length=None, min_length=None)</code></p> <p><strong>Signature:</strong> <code>CharField(max_length=None, min_length=None, allow_none=False)</code></p>
<h2 id="urlfield">URLField</h2> <h2 id="urlfield">URLField</h2>
<p>Corresponds to <code>django.db.models.fields.URLField</code>. Uses Django's <code>django.core.validators.URLValidator</code> for validation.</p> <p>Corresponds to <code>django.db.models.fields.URLField</code>. Uses Django's <code>django.core.validators.URLValidator</code> for validation.</p>
<p><strong>Signature:</strong> <code>URLField(max_length=200, min_length=None)</code></p> <p><strong>Signature:</strong> <code>URLField(max_length=200, min_length=None)</code></p>
...@@ -461,6 +463,8 @@ class ColourField(serializers.WritableField): ...@@ -461,6 +463,8 @@ class ColourField(serializers.WritableField):
<p>The <a href="https://github.com/Hipo/drf-extra-fields">drf-extra-fields</a> package provides extra serializer fields for REST framework, including <code>Base64ImageField</code> and <code>PointField</code> classes.</p> <p>The <a href="https://github.com/Hipo/drf-extra-fields">drf-extra-fields</a> package provides extra serializer fields for REST framework, including <code>Base64ImageField</code> and <code>PointField</code> classes.</p>
<h2 id="django-rest-framework-gis">django-rest-framework-gis</h2> <h2 id="django-rest-framework-gis">django-rest-framework-gis</h2>
<p>The <a href="https://github.com/djangonauts/django-rest-framework-gis">django-rest-framework-gis</a> package provides geographic addons for django rest framework like a <code>GeometryField</code> field and a GeoJSON serializer.</p> <p>The <a href="https://github.com/djangonauts/django-rest-framework-gis">django-rest-framework-gis</a> package provides geographic addons for django rest framework like a <code>GeometryField</code> field and a GeoJSON serializer.</p>
<h2 id="django-rest-framework-hstore">django-rest-framework-hstore</h2>
<p>The <a href="https://github.com/djangonauts/django-rest-framework-hstore">django-rest-framework-hstore</a> package provides an <code>HStoreField</code> to support <a href="https://github.com/djangonauts/django-hstore">django-hstore</a> <code>DictionaryField</code> model field.</p>
</div><!--/span--> </div><!--/span-->
</div><!--/row--> </div><!--/row-->
</div><!--/.fluid-container--> </div><!--/.fluid-container-->
......
...@@ -262,7 +262,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -262,7 +262,7 @@ class UserList(generics.ListCreateAPIView):
serializer = UserSerializer(queryset, many=True) serializer = UserSerializer(queryset, many=True)
return Response(serializer.data) return Response(serializer.data)
</code></pre> </code></pre>
<p>For very simple cases you might want to pass through any class attributes using the <code>.as_view()</code> method. For example, your URLconf might include something the following entry.</p> <p>For very simple cases you might want to pass through any class attributes using the <code>.as_view()</code> method. For example, your URLconf might include something like the following entry:</p>
<pre class="prettyprint lang-py"><code>url(r'^/users/', ListCreateAPIView.as_view(model=User), name='user-list') <pre class="prettyprint lang-py"><code>url(r'^/users/', ListCreateAPIView.as_view(model=User), name='user-list')
</code></pre> </code></pre>
<hr /> <hr />
...@@ -300,7 +300,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -300,7 +300,7 @@ class UserList(generics.ListCreateAPIView):
<h4 id="get_querysetself"><code>get_queryset(self)</code></h4> <h4 id="get_querysetself"><code>get_queryset(self)</code></h4>
<p>Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the <code>queryset</code> attribute, or the default queryset for the model if the <code>model</code> shortcut is being used.</p> <p>Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the <code>queryset</code> attribute, or the default queryset for the model if the <code>model</code> shortcut is being used.</p>
<p>This method should always be used rather than accessing <code>self.queryset</code> directly, as <code>self.queryset</code> gets evaluated only once, and those results are cached for all subsequent requests.</p> <p>This method should always be used rather than accessing <code>self.queryset</code> directly, as <code>self.queryset</code> gets evaluated only once, and those results are cached for all subsequent requests.</p>
<p>May be overridden to provide dynamic behavior such as returning a queryset that is specific to the user making the request.</p> <p>May be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>def get_queryset(self): <pre class="prettyprint lang-py"><code>def get_queryset(self):
user = self.request.user user = self.request.user
...@@ -308,7 +308,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -308,7 +308,7 @@ class UserList(generics.ListCreateAPIView):
</code></pre> </code></pre>
<h4 id="get_objectself"><code>get_object(self)</code></h4> <h4 id="get_objectself"><code>get_object(self)</code></h4>
<p>Returns an object instance that should be used for detail views. Defaults to using the <code>lookup_field</code> parameter to filter the base queryset.</p> <p>Returns an object instance that should be used for detail views. Defaults to using the <code>lookup_field</code> parameter to filter the base queryset.</p>
<p>May be overridden to provide more complex behavior such as object lookups based on more than one URL kwarg.</p> <p>May be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>def get_object(self): <pre class="prettyprint lang-py"><code>def get_object(self):
queryset = self.get_queryset() queryset = self.get_queryset()
...@@ -323,7 +323,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -323,7 +323,7 @@ class UserList(generics.ListCreateAPIView):
<p>Note that if your API doesn't include any object level permissions, you may optionally exclude the <code>self.check_object_permissions</code>, and simply return the object from the <code>get_object_or_404</code> lookup.</p> <p>Note that if your API doesn't include any object level permissions, you may optionally exclude the <code>self.check_object_permissions</code>, and simply return the object from the <code>get_object_or_404</code> lookup.</p>
<h4 id="get_filter_backendsself"><code>get_filter_backends(self)</code></h4> <h4 id="get_filter_backendsself"><code>get_filter_backends(self)</code></h4>
<p>Returns the classes that should be used to filter the queryset. Defaults to returning the <code>filter_backends</code> attribute.</p> <p>Returns the classes that should be used to filter the queryset. Defaults to returning the <code>filter_backends</code> attribute.</p>
<p>May be override to provide more complex behavior with filters, as using different (or even exlusive) lists of filter_backends depending on different criteria.</p> <p>May be overridden to provide more complex behavior with filters, such as using different (or even exlusive) lists of filter_backends depending on different criteria.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>def get_filter_backends(self): <pre class="prettyprint lang-py"><code>def get_filter_backends(self):
if "geo_route" in self.request.QUERY_PARAMS: if "geo_route" in self.request.QUERY_PARAMS:
...@@ -335,7 +335,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -335,7 +335,7 @@ class UserList(generics.ListCreateAPIView):
</code></pre> </code></pre>
<h4 id="get_serializer_classself"><code>get_serializer_class(self)</code></h4> <h4 id="get_serializer_classself"><code>get_serializer_class(self)</code></h4>
<p>Returns the class that should be used for the serializer. Defaults to returning the <code>serializer_class</code> attribute, or dynamically generating a serializer class if the <code>model</code> shortcut is being used.</p> <p>Returns the class that should be used for the serializer. Defaults to returning the <code>serializer_class</code> attribute, or dynamically generating a serializer class if the <code>model</code> shortcut is being used.</p>
<p>May be override to provide dynamic behavior such as using different serializers for read and write operations, or providing different serializers to different types of users.</p> <p>May be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>def get_serializer_class(self): <pre class="prettyprint lang-py"><code>def get_serializer_class(self):
if self.request.user.is_staff: if self.request.user.is_staff:
...@@ -344,7 +344,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -344,7 +344,7 @@ class UserList(generics.ListCreateAPIView):
</code></pre> </code></pre>
<h4 id="get_paginate_byself"><code>get_paginate_by(self)</code></h4> <h4 id="get_paginate_byself"><code>get_paginate_by(self)</code></h4>
<p>Returns the page size to use with pagination. By default this uses the <code>paginate_by</code> attribute, and may be overridden by the client if the <code>paginate_by_param</code> attribute is set.</p> <p>Returns the page size to use with pagination. By default this uses the <code>paginate_by</code> attribute, and may be overridden by the client if the <code>paginate_by_param</code> attribute is set.</p>
<p>You may want to override this method to provide more complex behavior such as modifying page sizes based on the media type of the response.</p> <p>You may want to override this method to provide more complex behavior, such as modifying page sizes based on the media type of the response.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>def get_paginate_by(self): <pre class="prettyprint lang-py"><code>def get_paginate_by(self):
if self.request.accepted_renderer.format == 'html': if self.request.accepted_renderer.format == 'html':
...@@ -378,7 +378,7 @@ class UserList(generics.ListCreateAPIView): ...@@ -378,7 +378,7 @@ class UserList(generics.ListCreateAPIView):
</ul> </ul>
<hr /> <hr />
<h1 id="mixins">Mixins</h1> <h1 id="mixins">Mixins</h1>
<p>The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods such as <code>.get()</code> and <code>.post()</code> directly. This allows for more flexible composition of behavior.</p> <p>The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as <code>.get()</code> and <code>.post()</code>, directly. This allows for more flexible composition of behavior.</p>
<h2 id="listmodelmixin">ListModelMixin</h2> <h2 id="listmodelmixin">ListModelMixin</h2>
<p>Provides a <code>.list(request, *args, **kwargs)</code> method, that implements listing a queryset.</p> <p>Provides a <code>.list(request, *args, **kwargs)</code> method, that implements listing a queryset.</p>
<p>If the queryset is populated, this returns a <code>200 OK</code> response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.</p> <p>If the queryset is populated, this returns a <code>200 OK</code> response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.</p>
......
...@@ -209,6 +209,7 @@ a.fusion-poweredby { ...@@ -209,6 +209,7 @@ a.fusion-poweredby {
<li class="main"><a href="#third-party-packages">Third party packages</a></li> <li class="main"><a href="#third-party-packages">Third party packages</a></li>
<li><a href="#mongoenginemodelserializer">MongoengineModelSerializer</a></li> <li><a href="#mongoenginemodelserializer">MongoengineModelSerializer</a></li>
<li><a href="#geofeaturemodelserializer">GeoFeatureModelSerializer</a></li> <li><a href="#geofeaturemodelserializer">GeoFeatureModelSerializer</a></li>
<li><a href="#hstoreserializer">HStoreSerializer</a></li>
<div class="promo"> <div class="promo">
...@@ -672,6 +673,8 @@ The <code>ModelSerializer</code> class lets you automatically create a Serialize ...@@ -672,6 +673,8 @@ The <code>ModelSerializer</code> class lets you automatically create a Serialize
<p>The <a href="https://github.com/umutbozkurt/django-rest-framework-mongoengine">django-rest-framework-mongoengine</a> package provides a <code>MongoEngineModelSerializer</code> serializer class that supports using MongoDB as the storage layer for Django REST framework.</p> <p>The <a href="https://github.com/umutbozkurt/django-rest-framework-mongoengine">django-rest-framework-mongoengine</a> package provides a <code>MongoEngineModelSerializer</code> serializer class that supports using MongoDB as the storage layer for Django REST framework.</p>
<h2 id="geofeaturemodelserializer">GeoFeatureModelSerializer</h2> <h2 id="geofeaturemodelserializer">GeoFeatureModelSerializer</h2>
<p>The <a href="https://github.com/djangonauts/django-rest-framework-gis">django-rest-framework-gis</a> package provides a <code>GeoFeatureModelSerializer</code> serializer class that supports GeoJSON both for read and write operations.</p> <p>The <a href="https://github.com/djangonauts/django-rest-framework-gis">django-rest-framework-gis</a> package provides a <code>GeoFeatureModelSerializer</code> serializer class that supports GeoJSON both for read and write operations.</p>
<h2 id="hstoreserializer">HStoreSerializer</h2>
<p>The <a href="https://github.com/djangonauts/django-rest-framework-hstore">django-rest-framework-hstore</a> package provides an <code>HStoreSerializer</code> to support <a href="https://github.com/djangonauts/django-hstore">django-hstore</a> <code>DictionaryField</code> model field and its <code>schema-mode</code> feature.</p>
</div><!--/span--> </div><!--/span-->
</div><!--/row--> </div><!--/row-->
</div><!--/.fluid-container--> </div><!--/.fluid-container-->
......
...@@ -434,6 +434,9 @@ If set to <code>None</code> then generic filtering is disabled.</p> ...@@ -434,6 +434,9 @@ If set to <code>None</code> then generic filtering is disabled.</p>
<h4 id="format_suffix_kwarg">FORMAT_SUFFIX_KWARG</h4> <h4 id="format_suffix_kwarg">FORMAT_SUFFIX_KWARG</h4>
<p>The name of a parameter in the URL conf that may be used to provide a format suffix.</p> <p>The name of a parameter in the URL conf that may be used to provide a format suffix.</p>
<p>Default: <code>'format'</code></p> <p>Default: <code>'format'</code></p>
<h4 id="num_proxies">NUM_PROXIES</h4>
<p>An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to <code>None</code> then less strict IP matching will be used by the throttle classes.</p>
<p>Default: <code>None</code></p>
</div><!--/span--> </div><!--/span-->
</div><!--/row--> </div><!--/row-->
</div><!--/.fluid-container--> </div><!--/.fluid-container-->
......
...@@ -229,7 +229,7 @@ If any throttle check fails an <code>exceptions.Throttled</code> exception will ...@@ -229,7 +229,7 @@ If any throttle check fails an <code>exceptions.Throttled</code> exception will
'DEFAULT_THROTTLE_RATES': { 'DEFAULT_THROTTLE_RATES': {
'anon': '100/day', 'anon': '100/day',
'user': '1000/day' 'user': '1000/day'
} }
} }
</code></pre> </code></pre>
<p>The rate descriptions used in <code>DEFAULT_THROTTLE_RATES</code> may include <code>second</code>, <code>minute</code>, <code>hour</code> or <code>day</code> as the throttle period.</p> <p>The rate descriptions used in <code>DEFAULT_THROTTLE_RATES</code> may include <code>second</code>, <code>minute</code>, <code>hour</code> or <code>day</code> as the throttle period.</p>
...@@ -257,6 +257,11 @@ def example_view(request, format=None): ...@@ -257,6 +257,11 @@ def example_view(request, format=None):
} }
return Response(content) return Response(content)
</code></pre> </code></pre>
<h2 id="how-clients-are-identified">How clients are identified</h2>
<p>The <code>X-Forwarded-For</code> and <code>Remote-Addr</code> HTTP headers are used to uniquely identify client IP addresses for throttling. If the <code>X-Forwarded-For</code> header is present then it will be used, otherwise the value of the <code>Remote-Addr</code> header will be used.</p>
<p>If you need to strictly identify unique client IP addresses, you'll need to first configure the number of application proxies that the API runs behind by setting the <code>NUM_PROXIES</code> setting. This setting should be an integer of zero or more. If set to non-zero then the client IP will be identified as being the last IP address in the <code>X-Forwarded-For</code> header, once any application proxy IP addresses have first been excluded. If set to zero, then the <code>Remote-Addr</code> header will always be used as the identifying IP address.</p>
<p>It is important to understand that if you configure the <code>NUM_PROXIES</code> setting, then all clients behind a unique <a href="http://en.wikipedia.org/wiki/Network_address_translation">NAT'd</a> gateway will be treated as a single client.</p>
<p>Further context on how the <code>X-Forwarded-For</code> header works, and identifing a remote client IP can be <a href="http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster">found here</a>.</p>
<h2 id="setting-up-the-cache">Setting up the cache</h2> <h2 id="setting-up-the-cache">Setting up the cache</h2>
<p>The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate <a href="https://docs.djangoproject.com/en/dev/ref/settings/#caches">cache settings</a>. The default value of <code>LocMemCache</code> backend should be okay for simple setups. See Django's <a href="https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache">cache documentation</a> for more details.</p> <p>The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate <a href="https://docs.djangoproject.com/en/dev/ref/settings/#caches">cache settings</a>. The default value of <code>LocMemCache</code> backend should be okay for simple setups. See Django's <a href="https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache">cache documentation</a> for more details.</p>
<p>If you need to use a cache other than <code>'default'</code>, you can do so by creating a custom throttle class and setting the <code>cache</code> attribute. For example:</p> <p>If you need to use a cache other than <code>'default'</code>, you can do so by creating a custom throttle class and setting the <code>cache</code> attribute. For example:</p>
......
...@@ -186,7 +186,7 @@ a.fusion-poweredby { ...@@ -186,7 +186,7 @@ a.fusion-poweredby {
<ul class="nav nav-list side-nav well sidebar-nav-fixed"> <ul class="nav nav-list side-nav well sidebar-nav-fixed">
<li class="main"><a href="#viewsets">ViewSets</a></li> <li class="main"><a href="#viewsets">ViewSets</a></li>
<li><a href="#example">Example</a></li> <li><a href="#example">Example</a></li>
<li><a href="#marking-extra-methods-for-routing">Marking extra methods for routing</a></li> <li><a href="#marking-extra-actions-for-routing">Marking extra actions for routing</a></li>
<li class="main"><a href="#api-reference">API Reference</a></li> <li class="main"><a href="#api-reference">API Reference</a></li>
<li><a href="#viewset">ViewSet</a></li> <li><a href="#viewset">ViewSet</a></li>
<li><a href="#genericviewset">GenericViewSet</a></li> <li><a href="#genericviewset">GenericViewSet</a></li>
...@@ -263,7 +263,7 @@ urlpatterns = router.urls ...@@ -263,7 +263,7 @@ urlpatterns = router.urls
<li>By using routers, we no longer need to deal with wiring up the URL conf ourselves.</li> <li>By using routers, we no longer need to deal with wiring up the URL conf ourselves.</li>
</ul> </ul>
<p>Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.</p> <p>Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.</p>
<h2 id="marking-extra-methods-for-routing">Marking extra methods for routing</h2> <h2 id="marking-extra-actions-for-routing">Marking extra actions for routing</h2>
<p>The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style operations, as shown below:</p> <p>The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style operations, as shown below:</p>
<pre class="prettyprint lang-py"><code>class UserViewSet(viewsets.ViewSet): <pre class="prettyprint lang-py"><code>class UserViewSet(viewsets.ViewSet):
""" """
...@@ -292,12 +292,13 @@ urlpatterns = router.urls ...@@ -292,12 +292,13 @@ urlpatterns = router.urls
def destroy(self, request, pk=None): def destroy(self, request, pk=None):
pass pass
</code></pre> </code></pre>
<p>If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the <code>@link</code> or <code>@action</code> decorators. The <code>@link</code> decorator will route <code>GET</code> requests, and the <code>@action</code> decorator will route <code>POST</code> requests.</p> <p>If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the <code>@detail_route</code> or <code>@list_route</code> decorators.</p>
<p>The <code>@detail_route</code> decorator contains <code>pk</code> in its URL pattern and is intended for methods which require a single instance. The <code>@list_route</code> decorator is intended for methods which operate on a list of objects.</p>
<p>For example:</p> <p>For example:</p>
<pre class="prettyprint lang-py"><code>from django.contrib.auth.models import User <pre class="prettyprint lang-py"><code>from django.contrib.auth.models import User
from rest_framework import viewsets
from rest_framework import status from rest_framework import status
from rest_framework.decorators import action from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer from myapp.serializers import UserSerializer, PasswordSerializer
...@@ -308,7 +309,7 @@ class UserViewSet(viewsets.ModelViewSet): ...@@ -308,7 +309,7 @@ class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserSerializer serializer_class = UserSerializer
@action() @detail_route(methods=['post'])
def set_password(self, request, pk=None): def set_password(self, request, pk=None):
user = self.get_object() user = self.get_object()
serializer = PasswordSerializer(data=request.DATA) serializer = PasswordSerializer(data=request.DATA)
...@@ -319,14 +320,21 @@ class UserViewSet(viewsets.ModelViewSet): ...@@ -319,14 +320,21 @@ class UserViewSet(viewsets.ModelViewSet):
else: else:
return Response(serializer.errors, return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST) status=status.HTTP_400_BAD_REQUEST)
@list_route()
def recent_users(self, request):
recent_users = User.objects.all().order('-last_login')
page = self.paginate_queryset(recent_users)
serializer = self.get_pagination_serializer(page)
return Response(serializer.data)
</code></pre> </code></pre>
<p>The <code>@action</code> and <code>@link</code> decorators can additionally take extra arguments that will be set for the routed view only. For example...</p> <p>The decorators can additionally take extra arguments that will be set for the routed view only. For example...</p>
<pre class="prettyprint lang-py"><code> @action(permission_classes=[IsAdminOrIsSelf]) <pre class="prettyprint lang-py"><code> @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None): def set_password(self, request, pk=None):
... ...
</code></pre> </code></pre>
<p>The <code>@action</code> decorator will route <code>POST</code> requests by default, but may also accept other HTTP methods, by using the <code>methods</code> argument. For example:</p> <p>Theses decorators will route <code>GET</code> requests by default, but may also accept other HTTP methods, by using the <code>methods</code> argument. For example:</p>
<pre class="prettyprint lang-py"><code> @action(methods=['POST', 'DELETE']) <pre class="prettyprint lang-py"><code> @detail_route(methods=['post', 'delete'])
def unset_password(self, request, pk=None): def unset_password(self, request, pk=None):
... ...
</code></pre> </code></pre>
......
...@@ -252,7 +252,7 @@ a.fusion-poweredby { ...@@ -252,7 +252,7 @@ a.fusion-poweredby {
<p>REST framework requires the following:</p> <p>REST framework requires the following:</p>
<ul> <ul>
<li>Python (2.6.5+, 2.7, 3.2, 3.3)</li> <li>Python (2.6.5+, 2.7, 3.2, 3.3)</li>
<li>Django (1.3, 1.4, 1.5, 1.6)</li> <li>Django (1.4.2+, 1.5, 1.6, 1.7)</li>
</ul> </ul>
<p>The following packages are optional:</p> <p>The following packages are optional:</p>
<ul> <ul>
...@@ -389,16 +389,9 @@ urlpatterns = patterns('', ...@@ -389,16 +389,9 @@ urlpatterns = patterns('',
<li><a href="topics/credits">Credits</a></li> <li><a href="topics/credits">Credits</a></li>
</ul> </ul>
<h2 id="development">Development</h2> <h2 id="development">Development</h2>
<p>If you want to work on REST framework itself, clone the repository, then...</p> <p>See the <a href="topics/contributing">Contribution guidelines</a> for information on how to clone
<p>Build the docs:</p> the repository, run the test suite and contribute changes back to REST
<pre class="prettyprint lang-py"><code>./mkdocs.py Framework.</p>
</code></pre>
<p>Run the tests:</p>
<pre class="prettyprint lang-py"><code>./rest_framework/runtests/runtests.py
</code></pre>
<p>To run the tests against all supported configurations, first install <a href="http://testrun.org/tox/latest/">the tox testing tool</a> globally, using <code>pip install tox</code>, then simply run <code>tox</code>:</p>
<pre class="prettyprint lang-py"><code>tox
</code></pre>
<h2 id="support">Support</h2> <h2 id="support">Support</h2>
<p>For support please see the <a href="https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework">REST framework discussion group</a>, try the <code>#restframework</code> channel on <code>irc.freenode.net</code>, search <a href="https://botbot.me/freenode/restframework/">the IRC archives</a>, or raise a question on <a href="http://stackoverflow.com/">Stack Overflow</a>, making sure to include the <a href="http://stackoverflow.com/questions/tagged/django-rest-framework">'django-rest-framework'</a> tag.</p> <p>For support please see the <a href="https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework">REST framework discussion group</a>, try the <code>#restframework</code> channel on <code>irc.freenode.net</code>, search <a href="https://botbot.me/freenode/restframework/">the IRC archives</a>, or raise a question on <a href="http://stackoverflow.com/">Stack Overflow</a>, making sure to include the <a href="http://stackoverflow.com/questions/tagged/django-rest-framework">'django-rest-framework'</a> tag.</p>
<p><a href="http://dabapps.com/services/build/api-development/">Paid support is available</a> from <a href="http://dabapps.com">DabApps</a>, and can include work on REST framework core, or support with building your REST framework API. Please <a href="http://dabapps.com/contact/">contact DabApps</a> if you'd like to discuss commercial support options.</p> <p><a href="http://dabapps.com/services/build/api-development/">Paid support is available</a> from <a href="http://dabapps.com">DabApps</a>, and can include work on REST framework core, or support with building your REST framework API. Please <a href="http://dabapps.com/contact/">contact DabApps</a> if you'd like to discuss commercial support options.</p>
......
...@@ -255,11 +255,35 @@ a.fusion-poweredby { ...@@ -255,11 +255,35 @@ a.fusion-poweredby {
virtualenv env virtualenv env
source env/bin/activate source env/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
pip install -r optionals.txt pip install -r requirements-test.txt
# Run the tests # Run the tests
rest_framework/runtests/runtests.py ./runtests.py
</code></pre> </code></pre>
<h3 id="test-options">Test options</h3>
<p>Run using a more concise output style.</p>
<pre class="prettyprint lang-py"><code>./runtests -q
</code></pre>
<p>Run the tests using a more concise output style, no coverage, no flake8.</p>
<pre class="prettyprint lang-py"><code>./runtests --fast
</code></pre>
<p>Don't run the flake8 code linting.</p>
<pre class="prettyprint lang-py"><code>./runtests --nolint
</code></pre>
<p>Only run the flake8 code linting, don't run the tests.</p>
<pre class="prettyprint lang-py"><code>./runtests --lintonly
</code></pre>
<p>Run the tests for a given test case.</p>
<pre class="prettyprint lang-py"><code>./runtests MyTestCase
</code></pre>
<p>Run the tests for a given test method.</p>
<pre class="prettyprint lang-py"><code>./runtests MyTestCase.test_this_method
</code></pre>
<p>Shorter form to run the tests for a given test method.</p>
<pre class="prettyprint lang-py"><code>./runtests test_this_method
</code></pre>
<p>Note: The test case and test method matching is fuzzy and will sometimes run other tests that contain a partial string match to the given command line input.</p>
<h3 id="running-against-multiple-environments">Running against multiple environments</h3>
<p>You can also use the excellent <a href="http://tox.readthedocs.org/en/latest/">tox</a> testing tool to run the tests against all supported versions of Python and Django. Install <code>tox</code> globally, and then simply run:</p> <p>You can also use the excellent <a href="http://tox.readthedocs.org/en/latest/">tox</a> testing tool to run the tests against all supported versions of Python and Django. Install <code>tox</code> globally, and then simply run:</p>
<pre class="prettyprint lang-py"><code>tox <pre class="prettyprint lang-py"><code>tox
</code></pre> </code></pre>
......
...@@ -262,7 +262,7 @@ a.fusion-poweredby { ...@@ -262,7 +262,7 @@ a.fusion-poweredby {
<li><a href="https://opbeat.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-opbeat.png);">Opbeat</a></li> <li><a href="https://opbeat.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-opbeat.png);">Opbeat</a></li>
<li><a href="https://koordinates.com" rel="nofollow" style="background-image:url(../img/sponsors/2-koordinates.png);">Koordinates</a></li> <li><a href="https://koordinates.com" rel="nofollow" style="background-image:url(../img/sponsors/2-koordinates.png);">Koordinates</a></li>
<li><a href="http://pulsecode.ca" rel="nofollow" style="background-image:url(../img/sponsors/2-pulsecode.png);">Pulsecode Inc.</a></li> <li><a href="http://pulsecode.ca" rel="nofollow" style="background-image:url(../img/sponsors/2-pulsecode.png);">Pulsecode Inc.</a></li>
<li><a href="http://singinghorsestudio.com" rel="nofollow" style="background-image:url(../img/sponsors/2-singing-horse.png);">Singing Horse Studio. Ltd.</a></li> <li><a href="http://singinghorsestudio.com" rel="nofollow" style="background-image:url(../img/sponsors/2-singing-horse.png);">Singing Horse Studio Ltd.</a></li>
<li><a href="https://www.heroku.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-heroku.png);">Heroku</a></li> <li><a href="https://www.heroku.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-heroku.png);">Heroku</a></li>
<li><a href="https://www.galileo-press.de/" rel="nofollow" style="background-image:url(../img/sponsors/2-galileo_press.png);">Galileo Press</a></li> <li><a href="https://www.galileo-press.de/" rel="nofollow" style="background-image:url(../img/sponsors/2-galileo_press.png);">Galileo Press</a></li>
<li><a href="http://www.securitycompass.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-security_compass.png);">Security Compass</a></li> <li><a href="http://www.securitycompass.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-security_compass.png);">Security Compass</a></li>
...@@ -272,12 +272,10 @@ a.fusion-poweredby { ...@@ -272,12 +272,10 @@ a.fusion-poweredby {
<li><a href="http://crypticocorp.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-cryptico.png);">Cryptico Corp</a></li> <li><a href="http://crypticocorp.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-cryptico.png);">Cryptico Corp</a></li>
<li><a href="http://www.nexthub.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-nexthub.png);">NextHub</a></li> <li><a href="http://www.nexthub.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-nexthub.png);">NextHub</a></li>
<li><a href="https://www.compile.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-compile.png);">Compile</a></li> <li><a href="https://www.compile.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-compile.png);">Compile</a></li>
<li><a href="http://wusawork.org" rel="nofollow" style="background-image:url(../img/sponsors/2-wusawork.png);>WusaWork</a></li>
<li><a href="http://envisionlinux.org/blog" rel="nofollow">Envision Linux</a></li> <li><a href="http://envisionlinux.org/blog" rel="nofollow">Envision Linux</a></li>
</ul> </ul>
<div style="clear: both; padding-bottom: 40px;"></div>
<p><strong>Individual backers</strong>: Simon Haugk.</p>
<hr /> <hr />
<h3 id="silver-sponsors">Silver sponsors</h3> <h3 id="silver-sponsors">Silver sponsors</h3>
<p>The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank&nbsp;you to individuals who have choosen to privately support the project at this level.</p> <p>The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank&nbsp;you to individuals who have choosen to privately support the project at this level.</p>
...@@ -321,7 +319,7 @@ a.fusion-poweredby { ...@@ -321,7 +319,7 @@ a.fusion-poweredby {
<div style="clear: both; padding-bottom: 40px;"></div> <div style="clear: both; padding-bottom: 40px;"></div>
<p><strong>Individual backers</strong>: Paul Hallet, <a href="http://www.paulwhippconsulting.com/">Paul Whipp</a>, Dylan Roy, Jannis Leidel, <a href="https://linovia.com/en/">Xavier Ordoquy</a>, <a href="http://spielmannsolutions.com/">Johannes Spielmann</a>, <a href="http://brooklynhacker.com/">Rob Spectre</a>, <a href="http://chrisheisel.com/">Chris Heisel</a>, Marwan Alsabbagh, Haris Ali, Tuomas Toivonen.</p> <p><strong>Individual backers</strong>: Paul Hallett, <a href="http://www.paulwhippconsulting.com/">Paul Whipp</a>, Dylan Roy, Jannis Leidel, <a href="https://linovia.com/en/">Xavier Ordoquy</a>, <a href="http://spielmannsolutions.com/">Johannes Spielmann</a>, <a href="http://brooklynhacker.com/">Rob Spectre</a>, <a href="http://chrisheisel.com/">Chris Heisel</a>, Marwan Alsabbagh, Haris Ali, Tuomas Toivonen.</p>
<hr /> <hr />
<h3 id="advocates">Advocates</h3> <h3 id="advocates">Advocates</h3>
<p>The following individuals made a significant financial contribution to the development of Django REST framework 3, for which I can only offer a huge, warm and sincere thank you!</p> <p>The following individuals made a significant financial contribution to the development of Django REST framework 3, for which I can only offer a huge, warm and sincere thank you!</p>
......
...@@ -188,6 +188,7 @@ a.fusion-poweredby { ...@@ -188,6 +188,7 @@ a.fusion-poweredby {
<li><a href="#versioning">Versioning</a></li> <li><a href="#versioning">Versioning</a></li>
<li><a href="#deprecation-policy">Deprecation policy</a></li> <li><a href="#deprecation-policy">Deprecation policy</a></li>
<li><a href="#upgrading">Upgrading</a></li> <li><a href="#upgrading">Upgrading</a></li>
<li><a href="#24x-series">2.4.x series</a></li>
<li><a href="#23x-series">2.3.x series</a></li> <li><a href="#23x-series">2.3.x series</a></li>
<li><a href="#22x-series">2.2.x series</a></li> <li><a href="#22x-series">2.2.x series</a></li>
<li><a href="#21x-series">2.1.x series</a></li> <li><a href="#21x-series">2.1.x series</a></li>
...@@ -238,6 +239,31 @@ a.fusion-poweredby { ...@@ -238,6 +239,31 @@ a.fusion-poweredby {
<pre class="prettyprint lang-py"><code>pip freeze | grep djangorestframework <pre class="prettyprint lang-py"><code>pip freeze | grep djangorestframework
</code></pre> </code></pre>
<hr /> <hr />
<h2 id="24x-series">2.4.x series</h2>
<h3 id="240">2.4.0</h3>
<p><strong>Django version requirements</strong>: The lowest supported version of Django is now 1.4.2.</p>
<p><strong>South version requirements</strong>: This note applies to any users using the optional <code>authtoken</code> application, which includes an associated database migration. You must now <em>either</em> upgrade your <code>south</code> package to version 1.0, <em>or</em> instead use the built-in migration support available with Django 1.7.</p>
<ul>
<li>Added compatibility with Django 1.7's database migration support.</li>
<li>New test runner, using <code>py.test</code>.</li>
<li><code>@detail_route</code> and <code>@list_route</code> decorators replace <code>@action</code> and <code>@link</code>.</li>
<li>Support customizable view name and description functions, using the <code>VIEW_NAME_FUNCTION</code> and <code>VIEW_DESCRIPTION_FUNCTION</code> settings.</li>
<li>Added <code>NUM_PROXIES</code> setting for smarter client IP identification.</li>
<li>Added <code>MAX_PAGINATE_BY</code> setting and <code>max_paginate_by</code> generic view attribute.</li>
<li>Added <code>Retry-After</code> header to throttled responses, as per <a href="http://tools.ietf.org/html/rfc6585">RFC 6585</a>. This should now be used in preference to the custom <code>X-Trottle-Wait-Seconds</code> header which will be fully deprecated in 3.0.</li>
<li>Added <code>cache</code> attribute to throttles to allow overriding of default cache.</li>
<li>Added <code>lookup_value_regex</code> attribute to routers, to allow the URL argument matching to be constrainted by the user.</li>
<li>Added <code>allow_none</code> option to <code>CharField</code>.</li>
<li>Support Django's standard <code>status_code</code> class attribute on responses.</li>
<li>More intuitive behavior on the test client, as <code>client.logout()</code> now also removes any credentials that have been set.</li>
<li>Bugfix: <code>?page_size=0</code> query parameter now falls back to default page size for view, instead of always turning pagination off.</li>
<li>Bugfix: Always uppercase <code>X-Http-Method-Override</code> methods.</li>
<li>Bugfix: Copy <code>filter_backends</code> list before returning it, in order to prevent view code from mutating the class attribute itself.</li>
<li>Bugfix: Set the <code>.action</code> attribute on viewsets when introspected by <code>OPTIONS</code> for testing permissions on the view.</li>
<li>Bugfix: Ensure <code>ValueError</code> raised during deserialization results in a error list rather than a single error. This is now consistent with other validation errors.</li>
<li>Bugfix: Fix <code>cache_format</code> typo on throttle classes, was <code>"throtte_%(scope)s_%(ident)s"</code>. Note that this will invalidate existing throttle caches.</li>
</ul>
<hr />
<h2 id="23x-series">2.3.x series</h2> <h2 id="23x-series">2.3.x series</h2>
<h3 id="2314">2.3.14</h3> <h3 id="2314">2.3.14</h3>
<p><strong>Date</strong>: 12th June 2014</p> <p><strong>Date</strong>: 12th June 2014</p>
...@@ -356,9 +382,9 @@ a.fusion-poweredby { ...@@ -356,9 +382,9 @@ a.fusion-poweredby {
<li>Added <code>trailing_slash</code> option to routers.</li> <li>Added <code>trailing_slash</code> option to routers.</li>
<li>Include support for <code>HttpStreamingResponse</code>.</li> <li>Include support for <code>HttpStreamingResponse</code>.</li>
<li>Support wider range of default serializer validation when used with custom model fields.</li> <li>Support wider range of default serializer validation when used with custom model fields.</li>
<li>UTF-8 Support for browsable API descriptions. </li> <li>UTF-8 Support for browsable API descriptions.</li>
<li>OAuth2 provider uses timezone aware datetimes when supported.</li> <li>OAuth2 provider uses timezone aware datetimes when supported.</li>
<li>Bugfix: Return error correctly when OAuth non-existent consumer occurs. </li> <li>Bugfix: Return error correctly when OAuth non-existent consumer occurs.</li>
<li>Bugfix: Allow <code>FileUploadParser</code> to correctly filename if provided as URL kwarg.</li> <li>Bugfix: Allow <code>FileUploadParser</code> to correctly filename if provided as URL kwarg.</li>
<li>Bugfix: Fix <code>ScopedRateThrottle</code>.</li> <li>Bugfix: Fix <code>ScopedRateThrottle</code>.</li>
</ul> </ul>
...@@ -395,7 +421,7 @@ a.fusion-poweredby { ...@@ -395,7 +421,7 @@ a.fusion-poweredby {
<li>Added SearchFilter</li> <li>Added SearchFilter</li>
<li>Added OrderingFilter</li> <li>Added OrderingFilter</li>
<li>Added GenericViewSet</li> <li>Added GenericViewSet</li>
<li>Bugfix: Multiple <code>@action</code> and <code>@link</code> methods now allowed on viewsets. </li> <li>Bugfix: Multiple <code>@action</code> and <code>@link</code> methods now allowed on viewsets.</li>
<li>Bugfix: Fix API Root view issue with DjangoModelPermissions</li> <li>Bugfix: Fix API Root view issue with DjangoModelPermissions</li>
</ul> </ul>
<h3 id="232">2.3.2</h3> <h3 id="232">2.3.2</h3>
...@@ -440,7 +466,7 @@ a.fusion-poweredby { ...@@ -440,7 +466,7 @@ a.fusion-poweredby {
<li>Long HTTP headers in browsable API are broken in multiple lines when possible.</li> <li>Long HTTP headers in browsable API are broken in multiple lines when possible.</li>
<li>Bugfix: Fix regression with DjangoFilterBackend not worthing correctly with single object views.</li> <li>Bugfix: Fix regression with DjangoFilterBackend not worthing correctly with single object views.</li>
<li>Bugfix: OAuth should fail hard when invalid token used.</li> <li>Bugfix: OAuth should fail hard when invalid token used.</li>
<li>Bugfix: Fix serializer potentially returning <code>None</code> object for models that define <code>__bool__</code> or <code>__len__</code>. </li> <li>Bugfix: Fix serializer potentially returning <code>None</code> object for models that define <code>__bool__</code> or <code>__len__</code>.</li>
</ul> </ul>
<h3 id="225">2.2.5</h3> <h3 id="225">2.2.5</h3>
<p><strong>Date</strong>: 26th March 2013</p> <p><strong>Date</strong>: 26th March 2013</p>
......
...@@ -219,7 +219,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -219,7 +219,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
</code></pre> </code></pre>
<p>Here we've used the <code>ReadOnlyModelViewSet</code> class to automatically provide the default 'read-only' operations. We're still setting the <code>queryset</code> and <code>serializer_class</code> attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.</p> <p>Here we've used the <code>ReadOnlyModelViewSet</code> class to automatically provide the default 'read-only' operations. We're still setting the <code>queryset</code> and <code>serializer_class</code> attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.</p>
<p>Next we're going to replace the <code>SnippetList</code>, <code>SnippetDetail</code> and <code>SnippetHighlight</code> view classes. We can remove the three views, and again replace them with a single class.</p> <p>Next we're going to replace the <code>SnippetList</code>, <code>SnippetDetail</code> and <code>SnippetHighlight</code> view classes. We can remove the three views, and again replace them with a single class.</p>
<pre class="prettyprint lang-py"><code>from rest_framework.decorators import link <pre class="prettyprint lang-py"><code>from rest_framework.decorators import detail_route
class SnippetViewSet(viewsets.ModelViewSet): class SnippetViewSet(viewsets.ModelViewSet):
""" """
...@@ -233,7 +233,7 @@ class SnippetViewSet(viewsets.ModelViewSet): ...@@ -233,7 +233,7 @@ class SnippetViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticatedOrReadOnly, permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,) IsOwnerOrReadOnly,)
@link(renderer_classes=[renderers.StaticHTMLRenderer]) @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs): def highlight(self, request, *args, **kwargs):
snippet = self.get_object() snippet = self.get_object()
return Response(snippet.highlighted) return Response(snippet.highlighted)
...@@ -242,8 +242,8 @@ class SnippetViewSet(viewsets.ModelViewSet): ...@@ -242,8 +242,8 @@ class SnippetViewSet(viewsets.ModelViewSet):
obj.owner = self.request.user obj.owner = self.request.user
</code></pre> </code></pre>
<p>This time we've used the <code>ModelViewSet</code> class in order to get the complete set of default read and write operations.</p> <p>This time we've used the <code>ModelViewSet</code> class in order to get the complete set of default read and write operations.</p>
<p>Notice that we've also used the <code>@link</code> decorator to create a custom action, named <code>highlight</code>. This decorator can be used to add any custom endpoints that don't fit into the standard <code>create</code>/<code>update</code>/<code>delete</code> style.</p> <p>Notice that we've also used the <code>@detail_route</code> decorator to create a custom action, named <code>highlight</code>. This decorator can be used to add any custom endpoints that don't fit into the standard <code>create</code>/<code>update</code>/<code>delete</code> style.</p>
<p>Custom actions which use the <code>@link</code> decorator will respond to <code>GET</code> requests. We could have instead used the <code>@action</code> decorator if we wanted an action that responded to <code>POST</code> requests.</p> <p>Custom actions which use the <code>@detail_route</code> decorator will respond to <code>GET</code> requests. We can use the <code>methods</code> argument if we wanted an action that responded to <code>POST</code> requests.</p>
<h2 id="binding-viewsets-to-urls-explicitly">Binding ViewSets to URLs explicitly</h2> <h2 id="binding-viewsets-to-urls-explicitly">Binding ViewSets to URLs explicitly</h2>
<p>The handler methods only get bound to the actions when we define the URLConf. <p>The handler methods only get bound to the actions when we define the URLConf.
To see what's going on under the hood let's first explicitly create a set of views from our ViewSets.</p> To see what's going on under the hood let's first explicitly create a set of views from our ViewSets.</p>
......
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