aboutsummaryrefslogtreecommitdiffstats
path: root/api-guide
diff options
context:
space:
mode:
authorTom Christie2014-11-03 11:21:29 +0000
committerTom Christie2014-11-03 11:21:29 +0000
commitdd14c6c88ba210bac8349041b8db486576539249 (patch)
tree81b1e22fb8080a8d3915ae6a5db95edf87a3bfb6 /api-guide
parentce165805481988e95887bf62c45cd616af5bba0f (diff)
downloaddjango-rest-framework-dd14c6c88ba210bac8349041b8db486576539249.tar.bz2
Latest docs release
Diffstat (limited to 'api-guide')
-rw-r--r--api-guide/authentication.html15
-rw-r--r--api-guide/format-suffixes.html11
-rw-r--r--api-guide/metadata.html324
-rw-r--r--api-guide/validators.html388
4 files changed, 728 insertions, 10 deletions
diff --git a/api-guide/authentication.html b/api-guide/authentication.html
index 3118ff34..420a1874 100644
--- a/api-guide/authentication.html
+++ b/api-guide/authentication.html
@@ -206,6 +206,7 @@ a.fusion-poweredby {
<li><a href="#json-web-token-authentication">JSON Web Token Authentication</a></li>
<li><a href="#hawk-http-authentication">Hawk HTTP Authentication</a></li>
<li><a href="#http-signature-authentication">HTTP Signature Authentication</a></li>
+<li><a href="#djoser">Djoser</a></li>
<div class="promo">
@@ -337,12 +338,13 @@ print token.key
<hr />
<h4 id="generating-tokens">Generating Tokens</h4>
<p>If you want every user to have an automatically generated Token, you can simply catch the User's <code>post_save</code> signal.</p>
-<pre class="prettyprint lang-py"><code>from django.contrib.auth import get_user_model
+<pre class="prettyprint lang-py"><code>from django.conf import settings
+from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
-@receiver(post_save, sender=get_user_model())
+@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
@@ -356,9 +358,10 @@ for user in User.objects.all():
Token.objects.get_or_create(user=user)
</code></pre>
<p>When using <code>TokenAuthentication</code>, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the <code>obtain_auth_token</code> view to your URLconf:</p>
-<pre class="prettyprint lang-py"><code>urlpatterns += patterns('',
- url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token')
-)
+<pre class="prettyprint lang-py"><code>from rest_framework.authtoken import views
+urlpatterns += [
+ url(r'^api-token-auth/', views.obtain_auth_token)
+]
</code></pre>
<p>Note that the URL part of the pattern can be whatever you want to use.</p>
<p>The <code>obtain_auth_token</code> view will return a JSON response when valid <code>username</code> and <code>password</code> fields are POSTed to the view using form data or JSON:</p>
@@ -508,6 +511,8 @@ class ExampleAuthentication(authentication.BaseAuthentication):
<p>The <a href="http://hawkrest.readthedocs.org/en/latest/">HawkREST</a> library builds on the <a href="http://mohawk.readthedocs.org/en/latest/">Mohawk</a> library to let you work with <a href="https://github.com/hueniverse/hawk">Hawk</a> signed requests and responses in your API. <a href="https://github.com/hueniverse/hawk">Hawk</a> lets two parties securely communicate with each other using messages signed by a shared key. It is based on <a href="http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05">HTTP MAC access authentication</a> (which was based on parts of <a href="http://oauth.net/core/1.0a">OAuth 1.0</a>).</p>
<h2 id="http-signature-authentication">HTTP Signature Authentication</h2>
<p>HTTP Signature (currently a <a href="https://datatracker.ietf.org/doc/draft-cavage-http-signatures/">IETF draft</a>) provides a way to achieve origin authentication and message integrity for HTTP messages. Similar to <a href="http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html">Amazon's HTTP Signature scheme</a>, used by many of its services, it permits stateless, per-request authentication. <a href="https://github.com/etoccalino/">Elvio Toccalino</a> maintains the <a href="https://github.com/etoccalino/django-rest-framework-httpsignature">djangorestframework-httpsignature</a> package which provides an easy to use HTTP Signature Authentication mechanism.</p>
+<h2 id="djoser">Djoser</h2>
+<p><a href="https://github.com/sunscrapers/djoser">Djoser</a> library provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation. The package works with a custom user model and it uses token based authentication. This is a ready to use REST implementation of Django authentication system.</p>
</div><!--/span-->
</div><!--/row-->
</div><!--/.fluid-container-->
diff --git a/api-guide/format-suffixes.html b/api-guide/format-suffixes.html
index 47ed1a5c..0531f75c 100644
--- a/api-guide/format-suffixes.html
+++ b/api-guide/format-suffixes.html
@@ -219,12 +219,13 @@ used all the time.</p>
</ul>
<p>Example:</p>
<pre class="prettyprint lang-py"><code>from rest_framework.urlpatterns import format_suffix_patterns
+from blog import views
-urlpatterns = patterns('blog.views',
- url(r'^/$', 'api_root'),
- url(r'^comments/$', 'comment_list'),
- url(r'^comments/(?P&lt;pk&gt;[0-9]+)/$', 'comment_detail')
-)
+urlpatterns = [
+ url(r'^/$', views.apt_root),
+ url(r'^comments/$', views.comment_list),
+ url(r'^comments/(?P&lt;pk&gt;[0-9]+)/$', views.comment_detail)
+]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
</code></pre>
diff --git a/api-guide/metadata.html b/api-guide/metadata.html
new file mode 100644
index 00000000..1ca656b7
--- /dev/null
+++ b/api-guide/metadata.html
@@ -0,0 +1,324 @@
+<!DOCTYPE html>
+<html lang="en">
+<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta charset="utf-8">
+ <title>Metadata - Django REST framework</title>
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//img/favicon.ico" rel="icon" type="image/x-icon">
+ <link rel="canonical" href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/metadata.html"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="Django, API, REST, Metadata, Custom metadata classes">
+ <meta name="author" content="Tom Christie">
+
+ <!-- Le styles -->
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/prettify.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/bootstrap.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/bootstrap-responsive.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/default.css" rel="stylesheet">
+
+ <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-18852272-2']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ <style>
+span.fusion-wrap a {
+ display: block;
+ margin-top: 10px;
+ color: black;
+}
+
+a.fusion-poweredby {
+ display: block;
+ margin-top: 10px;
+}
+@media (max-width: 767px) {
+ div.promo {display: none;}
+}
+</style>
+ </head>
+ <body onload="prettyPrint()" class="metadata-page">
+
+ <div class="wrapper">
+
+ <div class="navbar navbar-inverse navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container-fluid">
+ <a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
+ <a class="repo-link btn btn-inverse btn-small disabled" href="#">Next <i class="icon-arrow-right icon-white"></i></a>
+ <a class="repo-link btn btn-inverse btn-small disabled" href="#"><i class="icon-arrow-left icon-white"></i> Previous</a>
+ <a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" href="file:///Users/tomchristie/GitHub/django-rest-framework/html/index.html">Django REST framework</a>
+ <div class="nav-collapse collapse">
+ <ul class="nav">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html/index.html">Home</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/quickstart.html">Quickstart</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/1-serialization.html">1 - Serialization</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/2-requests-and-responses.html">2 - Requests and responses</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/3-class-based-views.html">3 - Class based views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/4-authentication-and-permissions.html">4 - Authentication and permissions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/5-relationships-and-hyperlinked-apis.html">5 - Relationships and hyperlinked APIs</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/6-viewsets-and-routers.html">6 - Viewsets and routers</a></li>
+ </ul>
+ </li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/requests.html">Requests</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/responses.html">Responses</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/views.html">Views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/generic-views.html">Generic views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/viewsets.html">Viewsets</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/routers.html">Routers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/parsers.html">Parsers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/renderers.html">Renderers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/serializers.html">Serializers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/fields.html">Serializer fields</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/relations.html">Serializer relations</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/validators.html">Validators</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/authentication.html">Authentication</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/permissions.html">Permissions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/throttling.html">Throttling</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/filtering.html">Filtering</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/pagination.html">Pagination</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/content-negotiation.html">Content negotiation</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/format-suffixes.html">Format suffixes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/reverse.html">Returning URLs</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/exceptions.html">Exceptions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/status-codes.html">Status codes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/testing.html">Testing</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/settings.html">Settings</a></li>
+ </ul>
+ </li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/documenting-your-api.html">Documenting your API</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/ajax-csrf-cors.html">AJAX, CSRF & CORS</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/browser-enhancements.html">Browser enhancements</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/browsable-api.html">The Browsable API</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/rest-hypermedia-hateoas.html">REST, Hypermedia & HATEOAS</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/third-party-resources.html">Third Party Resources</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/contributing.html">Contributing to REST framework</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/rest-framework-2-announcement.html">2.0 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.2-announcement.html">2.2 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.3-announcement.html">2.3 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.4-announcement.html">2.4 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/kickstarter-announcement.html">Kickstarter Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/release-notes.html">Release Notes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/credits.html">Credits</a></li>
+ </ul>
+ </li>
+ </ul>
+ <ul class="nav pull-right">
+ <!-- TODO
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Version: 2.0.0 <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="#">Trunk</a></li>
+ <li><a href="#">2.0.0</a></li>
+ </ul>
+ </li>
+ -->
+ </ul>
+ </div><!--/.nav-collapse -->
+ </div>
+ </div>
+ </div>
+
+ <div class="body-content">
+ <div class="container-fluid">
+
+<!-- Search Modal -->
+<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+ <h3 id="myModalLabel">Documentation search</h3>
+ </div>
+ <div class="modal-body">
+ <!-- Custom google search -->
+ <script>
+ (function() {
+ var cx = '015016005043623903336:rxraeohqk6w';
+ var gcse = document.createElement('script');
+ gcse.type = 'text/javascript';
+ gcse.async = true;
+ gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
+ '//www.google.com/cse/cse.js?cx=' + cx;
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(gcse, s);
+ })();
+ </script>
+ <gcse:search></gcse:search>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
+ </div>
+</div>
+
+ <div class="row-fluid">
+
+ <div class="span3">
+ <!-- TODO
+ <p style="margin-top: -12px">
+ <a class="btn btn-mini btn-primary" style="width: 60px">&laquo; previous</a>
+ <a class="btn btn-mini btn-primary" style="float: right; margin-right: 8px; width: 60px;">next &raquo;</a>
+ </p>
+ -->
+ <div id="table-of-contents">
+ <ul class="nav nav-list side-nav well sidebar-nav-fixed">
+ <li class="main"><a href="#metadata">Metadata</a></li>
+<li><a href="#setting-the-metadata-scheme">Setting the metadata scheme</a></li>
+<li><a href="#creating-schema-endpoints">Creating schema endpoints</a></li>
+<li class="main"><a href="#custom-metadata-classes">Custom metadata classes</a></li>
+<li><a href="#example">Example</a></li>
+
+ <div class="promo">
+
+ </div>
+</ul>
+
+ </div>
+ </div>
+
+ <div id="main-content" class="span9">
+ <p><a class="github" href="https://github.com/tomchristie/django-rest-framework/tree/master/rest_framework/metadata.py"><span class="label label-info">metadata.py</span></a></p>
+<h1 id="metadata">Metadata</h1>
+<blockquote>
+<p>[The <code>OPTIONS</code>] method allows a client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.</p>
+<p>&mdash; <a href="http://tools.ietf.org/html/rfc7231#section-4.3.7">RFC7231, Section 4.3.7.</a></p>
+</blockquote>
+<p>REST framework includes a configurable mechanism for determining how your API should respond to <code>OPTIONS</code> requests. This allows you to return API schema or other resource information.</p>
+<p>There are not currently any widely adopted conventions for exactly what style of response should be returned for HTTP <code>OPTIONS</code> requests, so we provide an ad-hoc style that returns some useful information.</p>
+<p>Here's an example response that demonstrates the information that is returned by default.</p>
+<pre class="prettyprint lang-py"><code>HTTP 200 OK
+Allow: GET, POST, HEAD, OPTIONS
+Content-Type: application/json
+
+{
+ "name": "To Do List",
+ "description": "List existing 'To Do' items, or create a new item.",
+ "renders": [
+ "application/json",
+ "text/html"
+ ],
+ "parses": [
+ "application/json",
+ "application/x-www-form-urlencoded",
+ "multipart/form-data"
+ ],
+ "actions": {
+ "POST": {
+ "note": {
+ "type": "string",
+ "required": false,
+ "read_only": false,
+ "label": "title",
+ "max_length": 100
+ }
+ }
+ }
+}
+</code></pre>
+<h2 id="setting-the-metadata-scheme">Setting the metadata scheme</h2>
+<p>You can set the metadata class globally using the <code>'DEFAULT_METADATA_CLASS'</code> settings key:</p>
+<pre class="prettyprint lang-py"><code>REST_FRAMEWORK = {
+ 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
+}
+</code></pre>
+<p>Or you can set the metadata class individually for a view:</p>
+<pre class="prettyprint lang-py"><code>class APIRoot(APIView):
+ metadata_class = APIRootMetadata
+
+ def get(self, request, format=None):
+ return Response({
+ ...
+ })
+</code></pre>
+<p>The REST framework package only includes a single metadata class implementation, named <code>SimpleMetadata</code>. If you want to use an alternative style you'll need to implement a custom metadata class.</p>
+<h2 id="creating-schema-endpoints">Creating schema endpoints</h2>
+<p>If you have specific requirements for creating schema endpoints that are accessed with regular <code>GET</code> requests, you might consider re-using the metadata API for doing so.</p>
+<p>For example, the following additional route could be used on a viewset to provide a linkable schema endpoint.</p>
+<pre class="prettyprint lang-py"><code>@list_route(methods=['GET'])
+def schema(self, request):
+ meta = self.metadata_class()
+ data = meta.determine_metadata(request, self)
+ return Response(data)
+</code></pre>
+<p>There are a couple of reasons that you might choose to take this approach, including that <code>OPTIONS</code> responses <a href="https://www.mnot.net/blog/2012/10/29/NO_OPTIONS">are not cacheable</a>.</p>
+<hr />
+<h1 id="custom-metadata-classes">Custom metadata classes</h1>
+<p>If you want to provide a custom metadata class you should override <code>BaseMetadata</code> and implement the <code>determine_metadata(self, request, view)</code> method.</p>
+<p>Useful things that you might want to do could include returning schema information, using a format such as <a href="http://json-schema.org/">JSON schema</a>, or returning debug information to admin users.</p>
+<h2 id="example">Example</h2>
+<p>The following class could be used to limit the information that is returned to <code>OPTIONS</code> requests.</p>
+<pre class="prettyprint lang-py"><code>class MinimalMetadata(BaseMetadata):
+ """
+ Don't include field and other information for `OPTIONS` requests.
+ Just return the name and description.
+ """
+ def determine_metadata(self, request, view):
+ return {
+ 'name': view.get_view_name(),
+ 'description': view.get_view_description()
+ }
+</code></pre>
+ </div><!--/span-->
+ </div><!--/row-->
+ </div><!--/.fluid-container-->
+ </div><!--/.body content-->
+
+ <div id="push"></div>
+ </div><!--/.wrapper -->
+
+ <footer class="span12">
+ <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a></p>
+ </footer>
+
+ <!-- Le javascript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/jquery-1.8.1-min.js"></script>
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/prettify-1.0.js"></script>
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/bootstrap-2.1.1-min.js"></script>
+
+ <script>
+ //$('.side-nav').scrollspy()
+ var shiftWindow = function() { scrollBy(0, -50) };
+ if (location.hash) shiftWindow();
+ window.addEventListener("hashchange", shiftWindow);
+
+ $('.dropdown-menu').on('click touchstart', function(event) {
+ event.stopPropagation();
+ });
+
+ // Dynamically force sidenav to no higher than browser window
+ $('.side-nav').css('max-height', window.innerHeight - 130);
+
+ $(function(){
+ $(window).resize(function(){
+ $('.side-nav').css('max-height', window.innerHeight - 130);
+ });
+ });
+ </script>
+</body></html>
diff --git a/api-guide/validators.html b/api-guide/validators.html
new file mode 100644
index 00000000..de19736e
--- /dev/null
+++ b/api-guide/validators.html
@@ -0,0 +1,388 @@
+<!DOCTYPE html>
+<html lang="en">
+<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta charset="utf-8">
+ <title>Validators - Django REST framework</title>
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//img/favicon.ico" rel="icon" type="image/x-icon">
+ <link rel="canonical" href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/validators.html"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="Django, API, REST, Validators, Writing custom validators">
+ <meta name="author" content="Tom Christie">
+
+ <!-- Le styles -->
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/prettify.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/bootstrap.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/bootstrap-responsive.css" rel="stylesheet">
+ <link href="file:///Users/tomchristie/GitHub/django-rest-framework/html//css/default.css" rel="stylesheet">
+
+ <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+ <!--[if lt IE 9]>
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-18852272-2']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ <style>
+span.fusion-wrap a {
+ display: block;
+ margin-top: 10px;
+ color: black;
+}
+
+a.fusion-poweredby {
+ display: block;
+ margin-top: 10px;
+}
+@media (max-width: 767px) {
+ div.promo {display: none;}
+}
+</style>
+ </head>
+ <body onload="prettyPrint()" class="validators-page">
+
+ <div class="wrapper">
+
+ <div class="navbar navbar-inverse navbar-fixed-top">
+ <div class="navbar-inner">
+ <div class="container-fluid">
+ <a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
+ <a class="repo-link btn btn-inverse btn-small " href="../api-guide/authentication.html">Next <i class="icon-arrow-right icon-white"></i></a>
+ <a class="repo-link btn btn-inverse btn-small " href="../api-guide/relations.html"><i class="icon-arrow-left icon-white"></i> Previous</a>
+ <a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+ <a class="brand" href="file:///Users/tomchristie/GitHub/django-rest-framework/html/index.html">Django REST framework</a>
+ <div class="nav-collapse collapse">
+ <ul class="nav">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html/index.html">Home</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/quickstart.html">Quickstart</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/1-serialization.html">1 - Serialization</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/2-requests-and-responses.html">2 - Requests and responses</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/3-class-based-views.html">3 - Class based views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/4-authentication-and-permissions.html">4 - Authentication and permissions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/5-relationships-and-hyperlinked-apis.html">5 - Relationships and hyperlinked APIs</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//tutorial/6-viewsets-and-routers.html">6 - Viewsets and routers</a></li>
+ </ul>
+ </li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/requests.html">Requests</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/responses.html">Responses</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/views.html">Views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/generic-views.html">Generic views</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/viewsets.html">Viewsets</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/routers.html">Routers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/parsers.html">Parsers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/renderers.html">Renderers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/serializers.html">Serializers</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/fields.html">Serializer fields</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/relations.html">Serializer relations</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/validators.html">Validators</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/authentication.html">Authentication</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/permissions.html">Permissions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/throttling.html">Throttling</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/filtering.html">Filtering</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/pagination.html">Pagination</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/content-negotiation.html">Content negotiation</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/format-suffixes.html">Format suffixes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/reverse.html">Returning URLs</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/exceptions.html">Exceptions</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/status-codes.html">Status codes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/testing.html">Testing</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//api-guide/settings.html">Settings</a></li>
+ </ul>
+ </li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/documenting-your-api.html">Documenting your API</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/ajax-csrf-cors.html">AJAX, CSRF & CORS</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/browser-enhancements.html">Browser enhancements</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/browsable-api.html">The Browsable API</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/rest-hypermedia-hateoas.html">REST, Hypermedia & HATEOAS</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/third-party-resources.html">Third Party Resources</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/contributing.html">Contributing to REST framework</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/rest-framework-2-announcement.html">2.0 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.2-announcement.html">2.2 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.3-announcement.html">2.3 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/2.4-announcement.html">2.4 Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/kickstarter-announcement.html">Kickstarter Announcement</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/release-notes.html">Release Notes</a></li>
+ <li><a href="file:///Users/tomchristie/GitHub/django-rest-framework/html//topics/credits.html">Credits</a></li>
+ </ul>
+ </li>
+ </ul>
+ <ul class="nav pull-right">
+ <!-- TODO
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Version: 2.0.0 <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a href="#">Trunk</a></li>
+ <li><a href="#">2.0.0</a></li>
+ </ul>
+ </li>
+ -->
+ </ul>
+ </div><!--/.nav-collapse -->
+ </div>
+ </div>
+ </div>
+
+ <div class="body-content">
+ <div class="container-fluid">
+
+<!-- Search Modal -->
+<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+ <h3 id="myModalLabel">Documentation search</h3>
+ </div>
+ <div class="modal-body">
+ <!-- Custom google search -->
+ <script>
+ (function() {
+ var cx = '015016005043623903336:rxraeohqk6w';
+ var gcse = document.createElement('script');
+ gcse.type = 'text/javascript';
+ gcse.async = true;
+ gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
+ '//www.google.com/cse/cse.js?cx=' + cx;
+ var s = document.getElementsByTagName('script')[0];
+ s.parentNode.insertBefore(gcse, s);
+ })();
+ </script>
+ <gcse:search></gcse:search>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
+ </div>
+</div>
+
+ <div class="row-fluid">
+
+ <div class="span3">
+ <!-- TODO
+ <p style="margin-top: -12px">
+ <a class="btn btn-mini btn-primary" style="width: 60px">&laquo; previous</a>
+ <a class="btn btn-mini btn-primary" style="float: right; margin-right: 8px; width: 60px;">next &raquo;</a>
+ </p>
+ -->
+ <div id="table-of-contents">
+ <ul class="nav nav-list side-nav well sidebar-nav-fixed">
+ <li class="main"><a href="#validators">Validators</a></li>
+<li><a href="#uniquevalidator">UniqueValidator</a></li>
+<li><a href="#uniquefordatevalidator">UniqueForDateValidator</a></li>
+<li><a href="#uniqueformonthvalidator">UniqueForMonthValidator</a></li>
+<li><a href="#uniqueforyearvalidator">UniqueForYearValidator</a></li>
+<li class="main"><a href="#writing-custom-validators">Writing custom validators</a></li>
+<li><a href="#function-based">Function based</a></li>
+<li><a href="#class-based">Class based</a></li>
+
+ <div class="promo">
+
+ </div>
+</ul>
+
+ </div>
+ </div>
+
+ <div id="main-content" class="span9">
+ <p><a class="github" href="https://github.com/tomchristie/django-rest-framework/tree/master/rest_framework/validators.py"><span class="label label-info">validators.py</span></a></p>
+<h1 id="validators">Validators</h1>
+<blockquote>
+<p>Validators can be useful for re-using validation logic between different types of fields.</p>
+<p>&mdash; <a href="https://docs.djangoproject.com/en/dev/ref/validators/">Django documentation</a></p>
+</blockquote>
+<p>Most of the time you're dealing with validation in REST framework you'll simply be relying on the default field validation, or writing explicit validation methods on serializer or field classes.</p>
+<p>Sometimes you'll want to place your validation logic into reusable components, so that it can easily be reused throughout your codebase. This can be achieved by using validator functions and validator classes.</p>
+<h2 id="validation-in-rest-framework">Validation in REST framework</h2>
+<p>Validation in Django REST framework serializers is handled a little differently to how validation works in Django's <code>ModelForm</code> class.</p>
+<p>With <code>ModelForm</code> the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class. This is advantageous for the following reasons:</p>
+<ul>
+<li>It introduces a proper separation of concerns, making your code behaviour more obvious.</li>
+<li>It is easy to switch between using shortcut <code>ModelSerializer</code> classes and using explicit <code>Serializer</code> classes. Any validation behaviour being used for <code>ModelSerializer</code> is simple to replicate.</li>
+<li>Printing the <code>repr</code> of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behaviour being called on the model instance.</li>
+</ul>
+<p>When you're using <code>ModelSerializer</code> all of this is handled automatically for you. If you want to drop down to using a <code>Serializer</code> classes instead, then you need to define the validation rules explicitly.</p>
+<h4 id="example">Example</h4>
+<p>As an example of how REST framework uses explicit validation, we'll take a simple model class that has a field with a uniqueness constraint.</p>
+<pre class="prettyprint lang-py"><code>class CustomerReportRecord(models.Model):
+ time_raised = models.DateTimeField(default=timezone.now, editable=False)
+ reference = models.CharField(unique=True, max_length=20)
+ description = models.TextField()
+</code></pre>
+<p>Here's a basic <code>ModelSerializer</code> that we can use for creating or updating instances of <code>CustomerReportRecord</code>:</p>
+<pre class="prettyprint lang-py"><code>class CustomerReportSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = CustomerReportRecord
+</code></pre>
+<p>If we open up the Django shell using <code>manage.py shell</code> we can now </p>
+<pre class="prettyprint lang-py"><code>&gt;&gt;&gt; from project.example.serializers import CustomerReportSerializer
+&gt;&gt;&gt; serializer = CustomerReportSerializer()
+&gt;&gt;&gt; print(repr(serializer))
+CustomerReportSerializer():
+ id = IntegerField(label='ID', read_only=True)
+ time_raised = DateTimeField(read_only=True)
+ reference = CharField(max_length=20, validators=[&lt;UniqueValidator(queryset=CustomerReportRecord.objects.all())&gt;])
+ description = CharField(style={'type': 'textarea'})
+</code></pre>
+<p>The interesting bit here is the <code>reference</code> field. We can see that the uniqueness constraint is being explicitly enforced by a validator on the serializer field.</p>
+<p>Because of this more explicit style REST framework includes a few validator classes that are not available in core Django. These classes are detailed below.</p>
+<hr />
+<h2 id="uniquevalidator">UniqueValidator</h2>
+<p>This validator can be used to enforce the <code>unique=True</code> constraint on model fields.
+It takes a single required argument, and an optional <code>messages</code> argument:</p>
+<ul>
+<li><code>queryset</code> <em>required</em> - This is the queryset against which uniqueness should be enforced.</li>
+<li><code>message</code> - The error message that should be used when validation fails.</li>
+</ul>
+<p>This validator should be applied to <em>serializer fields</em>, like so:</p>
+<pre class="prettyprint lang-py"><code>slug = SlugField(
+ max_length=100,
+ validators=[UniqueValidator(queryset=BlogPost.objects.all())]
+)
+</code></pre>
+<h2 id="uniquetogethervalidator">UniqueTogetherValidator</h2>
+<p>This validator can be used to enforce <code>unique_together</code> constraints on model instances.
+It has two required arguments, and a single optional <code>messages</code> argument:</p>
+<ul>
+<li><code>queryset</code> <em>required</em> - This is the queryset against which uniqueness should be enforced.</li>
+<li><code>fields</code> <em>required</em> - A list or tuple of field names which should make a unique set. These must exist as fields on the serializer class.</li>
+<li><code>message</code> - The error message that should be used when validation fails.</li>
+</ul>
+<p>The validator should be applied to <em>serializer classes</em>, like so:</p>
+<pre class="prettyprint lang-py"><code>class ExampleSerializer(serializers.Serializer):
+ # ...
+ class Meta:
+ # ToDo items belong to a parent list, and have an ordering defined
+ # by the 'position' field. No two items in a given list may share
+ # the same position.
+ validators = [
+ UniqueTogetherValidator(
+ queryset=ToDoItem.objects.all(),
+ fields=('list', 'position')
+ )
+ ]
+</code></pre>
+<h2 id="uniquefordatevalidator">UniqueForDateValidator</h2>
+<h2 id="uniqueformonthvalidator">UniqueForMonthValidator</h2>
+<h2 id="uniqueforyearvalidator">UniqueForYearValidator</h2>
+<p>These validators can be used to enforce the <code>unique_for_date</code>, <code>unique_for_month</code> and <code>unique_for_year</code> constraints on model instances. They take the following arguments:</p>
+<ul>
+<li><code>queryset</code> <em>required</em> - This is the queryset against which uniqueness should be enforced.</li>
+<li><code>field</code> <em>required</em> - A field name against which uniqueness in the given date range will be validated. This must exist as a field on the serializer class.</li>
+<li><code>date_field</code> <em>required</em> - A field name which will be used to determine date range for the uniqueness constrain. This must exist as a field on the serializer class.</li>
+<li><code>message</code> - The error message that should be used when validation fails.</li>
+</ul>
+<p>The validator should be applied to <em>serializer classes</em>, like so:</p>
+<pre class="prettyprint lang-py"><code>class ExampleSerializer(serializers.Serializer):
+ # ...
+ class Meta:
+ # Blog posts should have a slug that is unique for the current year.
+ validators = [
+ UniqueForYearValidator(
+ queryset=BlogPostItem.objects.all(),
+ field='slug',
+ date_field='published'
+ )
+ ]
+</code></pre>
+<p>The date field that is used for the validation is always required to be present on the serializer class. You can't simply rely on a model class <code>default=...</code>, because the value being used for the default wouldn't be generated until after the validation has run.</p>
+<p>There are a couple of styles you may want to use for this depending on how you want your API to behave. If you're using <code>ModelSerializer</code> you'll probably simply rely on the defaults that REST framework generates for you, but if you are using <code>Serializer</code> or simply want more explicit control, use on of the styles demonstrated below.</p>
+<h4 id="using-with-a-writable-date-field">Using with a writable date field.</h4>
+<p>If you want the date field to be writable the only thing worth noting is that you should ensure that it is always available in the input data, either by setting a <code>default</code> argument, or by setting <code>required=True</code>.</p>
+<pre class="prettyprint lang-py"><code>published = serializers.DateTimeField(required=True)
+</code></pre>
+<h4 id="using-with-a-read-only-date-field">Using with a read-only date field.</h4>
+<p>If you want the date field to be visible, but not editable by the user, then set <code>read_only=True</code> and additionally set a <code>default=...</code> argument.</p>
+<pre class="prettyprint lang-py"><code>published = serializers.DateTimeField(read_only=True, default=timezone.now)
+</code></pre>
+<p>The field will not be writable to the user, but the default value will still be passed through to the <code>validated_data</code>.</p>
+<h4 id="using-with-a-hidden-date-field">Using with a hidden date field.</h4>
+<p>If you want the date field to be entirely hidden from the user, then use <code>HiddenField</code>. This field type does not accept user input, but instead always returns it's default value to the <code>validated_data</code> in the serializer.</p>
+<pre class="prettyprint lang-py"><code>published = serializers.HiddenField(default=timezone.now)
+</code></pre>
+<hr />
+<h1 id="writing-custom-validators">Writing custom validators</h1>
+<p>You can use any of Django's existing validators, or write your own custom validators.</p>
+<h2 id="function-based">Function based</h2>
+<p>A validator may be any callable that raises a <code>serializers.ValidationError</code> on failure.</p>
+<pre class="prettyprint lang-py"><code>def even_number(value):
+ if value % 2 != 0:
+ raise serializers.ValidationError('This field must be an even number.')
+</code></pre>
+<h2 id="class-based">Class based</h2>
+<p>To write a class based validator, use the <code>__call__</code> method. Class based validators are useful as they allow you to parameterize and reuse behavior.</p>
+<pre class="prettyprint lang-py"><code>class MultipleOf:
+ def __init__(self, base):
+ self.base = base
+
+ def __call__(self, value):
+ if value % self.base != 0
+ message = 'This field must be a multiple of %d.' % self.base
+ raise serializers.ValidationError(message)
+</code></pre>
+<h4 id="using-set_context">Using <code>set_context()</code></h4>
+<p>In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a <code>set_context</code> method on a class based validator.</p>
+<pre class="prettyprint lang-py"><code>def set_context(self, serializer_field):
+ # Determine if this is an update or a create operation.
+ # In `__call__` we can then use that information to modify the validation behavior.
+ self.is_update = serializer_field.parent.instance is not None
+</code></pre>
+ </div><!--/span-->
+ </div><!--/row-->
+ </div><!--/.fluid-container-->
+ </div><!--/.body content-->
+
+ <div id="push"></div>
+ </div><!--/.wrapper -->
+
+ <footer class="span12">
+ <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a></p>
+ </footer>
+
+ <!-- Le javascript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/jquery-1.8.1-min.js"></script>
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/prettify-1.0.js"></script>
+ <script src="file:///Users/tomchristie/GitHub/django-rest-framework/html//js/bootstrap-2.1.1-min.js"></script>
+
+ <script>
+ //$('.side-nav').scrollspy()
+ var shiftWindow = function() { scrollBy(0, -50) };
+ if (location.hash) shiftWindow();
+ window.addEventListener("hashchange", shiftWindow);
+
+ $('.dropdown-menu').on('click touchstart', function(event) {
+ event.stopPropagation();
+ });
+
+ // Dynamically force sidenav to no higher than browser window
+ $('.side-nav').css('max-height', window.innerHeight - 130);
+
+ $(function(){
+ $(window).resize(function(){
+ $('.side-nav').css('max-height', window.innerHeight - 130);
+ });
+ });
+ </script>
+</body></html>