Merge 'tomchristie/restframework2' into 'browsable-bootstrap'

This commit is contained in:
Alec Perkins 2012-09-09 13:23:07 -04:00
commit 4500103337
57 changed files with 938 additions and 289 deletions

View File

@ -4,6 +4,8 @@
**Author:** Tom Christie. [Follow me on Twitter][twitter] **Author:** Tom Christie. [Follow me on Twitter][twitter]
[![Build Status](https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=restframework2)][travis]
# Overview # Overview
This branch is the redesign of Django REST framework. It is a work in progress. This branch is the redesign of Django REST framework. It is a work in progress.
@ -79,7 +81,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=restframework2
[twitter]: https://twitter.com/_tomchristie [twitter]: https://twitter.com/_tomchristie
[docs]: http://tomchristie.github.com/django-rest-framework/ [docs]: http://tomchristie.github.com/django-rest-framework/
[urlobject]: https://github.com/zacharyvoase/urlobject [urlobject]: https://github.com/zacharyvoase/urlobject

View File

@ -103,4 +103,38 @@ class SessionAuthentication(BaseAuthentication):
return (user, None) return (user, None)
# TODO: TokenAuthentication, DigestAuthentication, OAuthAuthentication class TokenAuthentication(BaseAuthentication):
"""
Use a token model for authentication.
A custom token model may be used here, but must have the following minimum
properties:
* key -- The string identifying the token
* user -- The user to which the token belongs
* revoked -- The status of the token
The token key should be passed in as a string to the "Authorization" HTTP
header. For example:
Authorization: 0123456789abcdef0123456789abcdef
"""
model = None
def authenticate(self, request):
key = request.META.get('HTTP_AUTHORIZATION', '').strip()
if self.model is None:
from djangorestframework.tokenauth.models import BasicToken
self.model = BasicToken
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
return None
if token.user.is_active and not token.revoked:
return (token.user, token)
# TODO: DigestAuthentication, OAuthAuthentication

View File

@ -366,6 +366,59 @@ else:
return self._accept(request) return self._accept(request)
# timezone support is new in Django 1.4
try:
from django.utils import timezone
except ImportError:
timezone = None
# dateparse is ALSO new in Django 1.4
try:
from django.utils.dateparse import parse_date, parse_datetime
except ImportError:
import datetime
import re
date_re = re.compile(
r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$'
)
datetime_re = re.compile(
r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})'
r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?'
r'(?P<tzinfo>Z|[+-]\d{1,2}:\d{1,2})?$'
)
time_re = re.compile(
r'(?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?'
)
def parse_date(value):
match = date_re.match(value)
if match:
kw = dict((k, int(v)) for k, v in match.groupdict().iteritems())
return datetime.date(**kw)
def parse_time(value):
match = time_re.match(value)
if match:
kw = match.groupdict()
if kw['microsecond']:
kw['microsecond'] = kw['microsecond'].ljust(6, '0')
kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
return datetime.time(**kw)
def parse_datetime(value):
"""Parse datetime, but w/o the timezone awareness in 1.4"""
match = datetime_re.match(value)
if match:
kw = match.groupdict()
if kw['microsecond']:
kw['microsecond'] = kw['microsecond'].ljust(6, '0')
kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
return datetime.datetime(**kw)
# Markdown is optional # Markdown is optional
try: try:

View File

@ -8,10 +8,10 @@ from django.core.exceptions import ValidationError
from django.conf import settings from django.conf import settings
from django.db import DEFAULT_DB_ALIAS from django.db import DEFAULT_DB_ALIAS
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.utils import timezone
from django.utils.dateparse import parse_date, parse_datetime
from django.utils.encoding import is_protected_type, smart_unicode from django.utils.encoding import is_protected_type, smart_unicode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from djangorestframework.compat import parse_date, parse_datetime
from djangorestframework.compat import timezone
def is_simple_callable(obj): def is_simple_callable(obj):
@ -317,7 +317,7 @@ class DateField(Field):
if value is None: if value is None:
return value return value
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
if settings.USE_TZ and timezone.is_aware(value): if timezone and settings.USE_TZ and timezone.is_aware(value):
# Convert aware datetimes to the default time zone # Convert aware datetimes to the default time zone
# before casting them to dates (#17742). # before casting them to dates (#17742).
default_timezone = timezone.get_default_timezone() default_timezone = timezone.get_default_timezone()

View File

@ -22,7 +22,7 @@ class CreateModelMixin(object):
self.object = serializer.object self.object = serializer.object
self.object.save() self.object.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ListModelMixin(object): class ListModelMixin(object):

View File

@ -16,6 +16,7 @@ from djangorestframework.utils import encoders
from djangorestframework.utils.breadcrumbs import get_breadcrumbs from djangorestframework.utils.breadcrumbs import get_breadcrumbs
from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches
from djangorestframework import VERSION from djangorestframework import VERSION
from djangorestframework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField
import string import string
@ -233,33 +234,31 @@ class DocumentingTemplateRenderer(BaseRenderer):
In the absence on of the Resource having an associated form then In the absence on of the Resource having an associated form then
provide a form that can be used to submit arbitrary content. provide a form that can be used to submit arbitrary content.
""" """
if not hasattr(self.view, 'get_serializer'): # No serializer, no form.
return
# We need to map our Fields to Django's Fields.
field_mapping = dict([
[FloatField.__name__, forms.FloatField],
[IntegerField.__name__, forms.IntegerField],
[DateTimeField.__name__, forms.DateTimeField],
[DateField.__name__, forms.DateField],
[EmailField.__name__, forms.EmailField],
[CharField.__name__, forms.CharField],
[BooleanField.__name__, forms.BooleanField]
])
# Get the form instance if we have one bound to the input # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
form_instance = None fields = {}
if method == getattr(view, 'method', view.request.method).lower(): object, data = None, None
form_instance = getattr(view, 'bound_form_instance', None) if hasattr(self.view, 'object'):
object = self.view.object
if not form_instance and hasattr(view, 'get_bound_form'): serializer = self.view.get_serializer(instance=object)
# Otherwise if we have a response that is valid against the form then use that for k, v in serializer.fields.items():
if view.response.has_content_body: fields[k] = field_mapping[v.__class__.__name__]()
try: OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields)
form_instance = view.get_bound_form(view.response.cleaned_content, method=method) if object and not self.view.request.method == 'DELETE': # Don't fill in the form when the object is deleted
if form_instance and not form_instance.is_valid(): data = serializer.data
form_instance = None form_instance = OnTheFlyForm(data)
except Exception:
form_instance = None
# If we still don't have a form instance then try to get an unbound form
if not form_instance:
try:
form_instance = view.get_bound_form(method=method)
except Exception:
pass
# If we still don't have a form instance then try to get an unbound form which can tunnel arbitrary content types
if not form_instance:
form_instance = self._get_generic_content_form(view)
return form_instance return form_instance
def _get_generic_content_form(self, view): def _get_generic_content_form(self, view):

View File

@ -90,6 +90,7 @@ INSTALLED_APPS = (
# Uncomment the next line to enable admin documentation: # Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs', # 'django.contrib.admindocs',
'djangorestframework', 'djangorestframework',
'djangorestframework.tokenauth',
) )
STATIC_URL = '/static/' STATIC_URL = '/static/'

View File

@ -88,7 +88,10 @@ def import_from_string(val, setting):
module_path, class_name = '.'.join(parts[:-1]), parts[-1] module_path, class_name = '.'.join(parts[:-1]), parts[-1]
module = importlib.import_module(module_path) module = importlib.import_module(module_path)
return getattr(module, class_name) return getattr(module, class_name)
except: except Exception, e:
import traceback
tb = traceback.format_exc()
import pdb; pdb.set_trace()
msg = "Could not import '%s' for API setting '%s'" % (val, setting) msg = "Could not import '%s' for API setting '%s'" % (val, setting)
raise ImportError(msg) raise ImportError(msg)

View File

@ -8,6 +8,9 @@ from django.http import HttpResponse
from djangorestframework.views import APIView from djangorestframework.views import APIView
from djangorestframework import permissions from djangorestframework import permissions
from djangorestframework.tokenauth.models import BasicToken
from djangorestframework.authentication import TokenAuthentication
import base64 import base64
@ -20,6 +23,8 @@ class MockView(APIView):
def put(self, request): def put(self, request):
return HttpResponse({'a': 1, 'b': 2, 'c': 3}) return HttpResponse({'a': 1, 'b': 2, 'c': 3})
MockView.authentication += (TokenAuthentication,)
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^$', MockView.as_view()), (r'^$', MockView.as_view()),
) )
@ -104,3 +109,45 @@ class SessionAuthTests(TestCase):
""" """
response = self.csrf_client.post('/', {'example': 'example'}) response = self.csrf_client.post('/', {'example': 'example'})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
class TokenAuthTests(TestCase):
"""Token authentication"""
urls = 'djangorestframework.tests.authentication'
def setUp(self):
self.csrf_client = Client(enforce_csrf_checks=True)
self.username = 'john'
self.email = 'lennon@thebeatles.com'
self.password = 'password'
self.user = User.objects.create_user(self.username, self.email, self.password)
self.key = 'abcd1234'
self.token = BasicToken.objects.create(key=self.key, user=self.user)
def test_post_form_passing_token_auth(self):
"""Ensure POSTing json over token auth with correct credentials passes and does not require CSRF"""
auth = self.key
response = self.csrf_client.post('/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
def test_post_json_passing_token_auth(self):
"""Ensure POSTing form over token auth with correct credentials passes and does not require CSRF"""
auth = self.key
response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
def test_post_form_failing_token_auth(self):
"""Ensure POSTing form over token auth without correct credentials fails"""
response = self.csrf_client.post('/', {'example': 'example'})
self.assertEqual(response.status_code, 403)
def test_post_json_failing_token_auth(self):
"""Ensure POSTing json over token auth without correct credentials fails"""
response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json')
self.assertEqual(response.status_code, 403)
def test_token_has_auto_assigned_key_if_none_provided(self):
"""Ensure creating a token with no key will auto-assign a key"""
token = BasicToken.objects.create(user=self.user)
self.assertEqual(len(token.key), 32)

View File

@ -55,27 +55,27 @@ class MockView(APIView):
def get(self, request, **kwargs): def get(self, request, **kwargs):
response = Response(DUMMYCONTENT, status=DUMMYSTATUS) response = Response(DUMMYCONTENT, status=DUMMYSTATUS)
return self.render(response) return response
class MockGETView(APIView): class MockGETView(APIView):
def get(self, request, **kwargs): def get(self, request, **kwargs):
return {'foo': ['bar', 'baz']} return Response({'foo': ['bar', 'baz']})
class HTMLView(APIView): class HTMLView(APIView):
renderers = (DocumentingHTMLRenderer, ) renderers = (DocumentingHTMLRenderer, )
def get(self, request, **kwargs): def get(self, request, **kwargs):
return 'text' return Response('text')
class HTMLView1(APIView): class HTMLView1(APIView):
renderers = (DocumentingHTMLRenderer, JSONRenderer) renderers = (DocumentingHTMLRenderer, JSONRenderer)
def get(self, request, **kwargs): def get(self, request, **kwargs):
return 'text' return Response('text')
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderers=[RendererA, RendererB])), url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderers=[RendererA, RendererB])),
@ -88,7 +88,7 @@ urlpatterns = patterns('',
) )
class RendererIntegrationTests(TestCase): class RendererEndToEndTests(TestCase):
""" """
End-to-end testing of renderers using an RendererMixin on a generic view. End-to-end testing of renderers using an RendererMixin on a generic view.
""" """
@ -216,18 +216,6 @@ class JSONRendererTests(TestCase):
self.assertEquals(strip_trailing_whitespace(content), _indented_repr) self.assertEquals(strip_trailing_whitespace(content), _indented_repr)
class MockGETView(APIView):
def get(self, request, *args, **kwargs):
return Response({'foo': ['bar', 'baz']})
urlpatterns = patterns('',
url(r'^jsonp/jsonrenderer$', MockGETView.as_view(renderers=[JSONRenderer, JSONPRenderer])),
url(r'^jsonp/nojsonrenderer$', MockGETView.as_view(renderers=[JSONPRenderer])),
)
class JSONPRendererTests(TestCase): class JSONPRendererTests(TestCase):
""" """
Tests specific to the JSONP Renderer Tests specific to the JSONP Renderer

View File

@ -94,7 +94,16 @@ class TestContentParsing(TestCase):
""" """
data = {'qwerty': 'uiop'} data = {'qwerty': 'uiop'}
parsers = (FormParser, MultiPartParser) parsers = (FormParser, MultiPartParser)
request = factory.put('/', data, parsers=parsers)
from django import VERSION
if VERSION >= (1, 5):
from django.test.client import MULTIPART_CONTENT, BOUNDARY, encode_multipart
request = factory.put('/', encode_multipart(BOUNDARY, data), parsers=parsers,
content_type=MULTIPART_CONTENT)
else:
request = factory.put('/', data, parsers=parsers)
self.assertEqual(request.DATA.items(), data.items()) self.assertEqual(request.DATA.items(), data.items())
def test_standard_behaviour_determines_non_form_content_PUT(self): def test_standard_behaviour_determines_non_form_content_PUT(self):

View File

@ -0,0 +1,15 @@
import uuid
from django.db import models
class BasicToken(models.Model):
"""
The default authorization token model class.
"""
key = models.CharField(max_length=32, primary_key=True, blank=True)
user = models.ForeignKey('auth.User')
revoked = models.BooleanField(default=False)
def save(self, *args, **kwargs):
if not self.key:
self.key = uuid.uuid4().hex
return super(BasicToken, self).save(*args, **kwargs)

View File

View File

@ -3,8 +3,8 @@ Helper classes for parsers.
""" """
import datetime import datetime
import decimal import decimal
from django.utils import timezone
from django.utils import simplejson as json from django.utils import simplejson as json
from djangorestframework.compat import timezone
class JSONEncoder(json.JSONEncoder): class JSONEncoder(json.JSONEncoder):
@ -25,7 +25,7 @@ class JSONEncoder(json.JSONEncoder):
elif isinstance(o, datetime.date): elif isinstance(o, datetime.date):
return o.isoformat() return o.isoformat()
elif isinstance(o, datetime.time): elif isinstance(o, datetime.time):
if timezone.is_aware(o): if timezone and timezone.is_aware(o):
raise ValueError("JSON can't represent timezone-aware times.") raise ValueError("JSON can't represent timezone-aware times.")
r = o.isoformat() r = o.isoformat()
if o.microsecond: if o.microsecond:

View File

@ -1,3 +1,5 @@
<a class="github" href="authentication.py"></a>
# Authentication # Authentication
Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted. Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted.
@ -8,7 +10,7 @@ Authentication will run the first time either the `request.user` or `request.aut
The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class. The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class.
The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with. The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with.
## How authentication is determined ## How authentication is determined
@ -36,7 +38,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi
def get(self, request, format=None): def get(self, request, format=None):
content = { content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance. 'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None 'auth': unicode(request.auth), # None
} }
return Response(content) return Response(content)
@ -49,7 +51,7 @@ Or, if you're using the `@api_view` decorator with function based views.
) )
def example_view(request, format=None): def example_view(request, format=None):
content = { content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance. 'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None 'auth': unicode(request.auth), # None
} }
return Response(content) return Response(content)
@ -65,16 +67,20 @@ If successfully authenticated, `UserBasicAuthentication` provides the following
* `request.user` will be a `django.contrib.auth.models.User` instance. * `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be `None`. * `request.auth` will be `None`.
## TokenBasicAuthentication ## TokenAuthentication
This policy uses [HTTP Basic Authentication][basicauth], signed against a token key and secret. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. This policy uses [HTTP Authentication][basicauth] with no authentication scheme. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. The token key should be passed in as a string to the "Authorization" HTTP header. For example:
**Note:** If you run `TokenBasicAuthentication` in production your API must be `https` only, or it will be completely insecure. curl http://my.api.org/ -X POST -H "Authorization: 0123456789abcdef0123456789abcdef"
If successfully authenticated, `TokenBasicAuthentication` provides the following credentials. **Note:** If you run `TokenAuthentication` in production your API must be `https` only, or it will be completely insecure.
If successfully authenticated, `TokenAuthentication` provides the following credentials.
* `request.user` will be a `django.contrib.auth.models.User` instance. * `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be a `djangorestframework.models.BasicToken` instance. * `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance.
To use the `TokenAuthentication` policy, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications and sync your database. To use your own token model, subclass the `djangorestframework.tokenauth.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. Refer to the `djangorestframework.tokenauth.models.BasicToken` model as an example.
## OAuthAuthentication ## OAuthAuthentication

View File

@ -1,3 +1,5 @@
<a class="github" href="exceptions.py"></a>
# Exceptions # Exceptions

View File

@ -1,3 +1,5 @@
<a class="github" href="parsers.py"></a>
# Parsers # Parsers
## .parse(request) ## .parse(request)

View File

@ -1 +1,3 @@
<a class="github" href="permissions.py"></a>
# Permissions

View File

@ -1,3 +1,5 @@
<a class="github" href="renderers.py"></a>
# Renderers # Renderers
## .render(response) ## .render(response)

View File

@ -1,3 +1,5 @@
<a class="github" href="request.py"></a>
# Requests # Requests
> If you're doing REST-based web service stuff ... you should ignore request.POST. > If you're doing REST-based web service stuff ... you should ignore request.POST.

View File

@ -1,3 +1,5 @@
<a class="github" href="response.py"></a>
# Responses # Responses
> Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process. > Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process.

View File

@ -1,3 +1,5 @@
<a class="github" href="reverse.py"></a>
# Returning URIs from your Web APIs # Returning URIs from your Web APIs
> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. > The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.

View File

@ -1,3 +1,5 @@
<a class="github" href="serializers.py"></a>
# Serializers # Serializers
> Expanding the usefulness of the serializers is something that we would > Expanding the usefulness of the serializers is something that we would

View File

@ -1,3 +1,5 @@
<a class="github" href="settings.py"></a>
# Settings # Settings
Configuration for REST framework is all namespaced inside the `API_SETTINGS` setting. Configuration for REST framework is all namespaced inside the `API_SETTINGS` setting.

View File

@ -1,3 +1,5 @@
<a class="github" href="status.py"></a>
# Status Codes # Status Codes
> 418 I'm a teapot - Any attempt to brew coffee with a teapot should result in the error code "418 I'm a teapot". The resulting entity body MAY be short and stout. > 418 I'm a teapot - Any attempt to brew coffee with a teapot should result in the error code "418 I'm a teapot". The resulting entity body MAY be short and stout.

View File

@ -1 +1,3 @@
<a class="github" href="throttling.py"></a>
# Throttling

View File

@ -1,9 +1,11 @@
<a class="github" href="views.py"></a>
# Views
> Django's class based views are a welcome departure from the old-style views. > Django's class based views are a welcome departure from the old-style views.
> >
> &mdash; [Reinout van Rees][cite] > &mdash; [Reinout van Rees][cite]
# Views
REST framework provides a simple `APIView` class, built on Django's `django.generics.views.View`. The `APIView` class ensures five main things: REST framework provides a simple `APIView` class, built on Django's `django.generics.views.View`. The `APIView` class ensures five main things:
1. Any requests inside the view will become `Request` instances. 1. Any requests inside the view will become `Request` instances.

View File

@ -1,3 +1,6 @@
<iframe src="http://ghbtns.com/github-btn.html?user=tomchristie&amp;repo=django-rest-framework&amp;type=watch&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="110px" height="20px"></iframe>
[![Build Status](https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=restframework2)][travis]
# Django REST framework # Django REST framework
**A toolkit for building well-connected, self-describing Web APIs.** **A toolkit for building well-connected, self-describing Web APIs.**
@ -131,6 +134,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=restframework2
[urlobject]: https://github.com/zacharyvoase/urlobject [urlobject]: https://github.com/zacharyvoase/urlobject
[markdown]: http://pypi.python.org/pypi/Markdown/ [markdown]: http://pypi.python.org/pypi/Markdown/
[yaml]: http://pypi.python.org/pypi/PyYAML [yaml]: http://pypi.python.org/pypi/PyYAML

View File

@ -1,5 +1,5 @@
/*! /*!
* Bootstrap Responsive v2.1.0 * Bootstrap Responsive v2.1.1
* *
* Copyright 2012 Twitter, Inc * Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0 * Licensed under the Apache License v2.0
@ -107,6 +107,7 @@
} }
[class*="span"] { [class*="span"] {
float: left; float: left;
min-height: 1px;
margin-left: 30px; margin-left: 30px;
} }
.container, .container,
@ -453,6 +454,7 @@
} }
[class*="span"] { [class*="span"] {
float: left; float: left;
min-height: 1px;
margin-left: 20px; margin-left: 20px;
} }
.container, .container,
@ -780,7 +782,8 @@
padding-left: 20px; padding-left: 20px;
} }
.navbar-fixed-top, .navbar-fixed-top,
.navbar-fixed-bottom { .navbar-fixed-bottom,
.navbar-static-top {
margin-right: -20px; margin-right: -20px;
margin-left: -20px; margin-left: -20px;
} }
@ -814,8 +817,11 @@
.row-fluid [class*="span"] { .row-fluid [class*="span"] {
display: block; display: block;
float: none; float: none;
width: auto; width: 100%;
margin-left: 0; margin-left: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
} }
.span12, .span12,
.row-fluid .span12 { .row-fluid .span12 {
@ -845,6 +851,9 @@
display: inline-block; display: inline-block;
width: auto; width: auto;
} }
.controls-row [class*="span"] + [class*="span"] {
margin-left: 0;
}
.modal { .modal {
position: fixed; position: fixed;
top: 20px; top: 20px;
@ -870,7 +879,7 @@
input[type="radio"] { input[type="radio"] {
border: 1px solid #ccc; border: 1px solid #ccc;
} }
.form-horizontal .control-group > label { .form-horizontal .control-label {
float: none; float: none;
width: auto; width: auto;
padding-top: 0; padding-top: 0;
@ -944,14 +953,14 @@
display: none; display: none;
} }
.nav-collapse .nav .nav-header { .nav-collapse .nav .nav-header {
color: #555555; color: #777777;
text-shadow: none; text-shadow: none;
} }
.nav-collapse .nav > li > a, .nav-collapse .nav > li > a,
.nav-collapse .dropdown-menu a { .nav-collapse .dropdown-menu a {
padding: 9px 15px; padding: 9px 15px;
font-weight: bold; font-weight: bold;
color: #555555; color: #777777;
-webkit-border-radius: 3px; -webkit-border-radius: 3px;
-moz-border-radius: 3px; -moz-border-radius: 3px;
border-radius: 3px; border-radius: 3px;
@ -1003,6 +1012,10 @@
.nav-collapse .dropdown-menu .divider { .nav-collapse .dropdown-menu .divider {
display: none; display: none;
} }
.nav-collapse .nav > li > .dropdown-menu:before,
.nav-collapse .nav > li > .dropdown-menu:after {
display: none;
}
.nav-collapse .navbar-form, .nav-collapse .navbar-form,
.nav-collapse .navbar-search { .nav-collapse .navbar-search {
float: none; float: none;
@ -1014,6 +1027,11 @@
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
} }
.navbar-inverse .nav-collapse .navbar-form,
.navbar-inverse .nav-collapse .navbar-search {
border-top-color: #111111;
border-bottom-color: #111111;
}
.navbar .nav-collapse .nav.pull-right { .navbar .nav-collapse .nav.pull-right {
float: none; float: none;
margin-left: 0; margin-left: 0;

View File

@ -1,5 +1,5 @@
/*! /*!
* Bootstrap v2.1.0 * Bootstrap v2.1.1
* *
* Copyright 2012 Twitter, Inc * Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0 * Licensed under the Apache License v2.0
@ -67,6 +67,7 @@ sub {
} }
img { img {
width: auto\9;
height: auto; height: auto;
max-width: 100%; max-width: 100%;
vertical-align: middle; vertical-align: middle;
@ -215,6 +216,7 @@ a:hover {
[class*="span"] { [class*="span"] {
float: left; float: left;
min-height: 1px;
margin-left: 20px; margin-left: 20px;
} }
@ -583,7 +585,7 @@ p {
.lead { .lead {
margin-bottom: 20px; margin-bottom: 20px;
font-size: 20px; font-size: 21px;
font-weight: 200; font-weight: 200;
line-height: 30px; line-height: 30px;
} }
@ -608,6 +610,22 @@ cite {
color: #999999; color: #999999;
} }
.text-warning {
color: #c09853;
}
.text-error {
color: #b94a48;
}
.text-info {
color: #3a87ad;
}
.text-success {
color: #468847;
}
h1, h1,
h2, h2,
h3, h3,
@ -725,9 +743,24 @@ dd {
margin-left: 10px; margin-left: 10px;
} }
.dl-horizontal {
*zoom: 1;
}
.dl-horizontal:before,
.dl-horizontal:after {
display: table;
line-height: 0;
content: "";
}
.dl-horizontal:after {
clear: both;
}
.dl-horizontal dt { .dl-horizontal dt {
float: left; float: left;
width: 120px; width: 160px;
overflow: hidden; overflow: hidden;
clear: left; clear: left;
text-align: right; text-align: right;
@ -736,7 +769,7 @@ dd {
} }
.dl-horizontal dd { .dl-horizontal dd {
margin-left: 130px; margin-left: 180px;
} }
hr { hr {
@ -945,8 +978,9 @@ input[type="color"],
} }
input, input,
textarea { textarea,
width: 210px; .uneditable-input {
width: 206px;
} }
textarea { textarea {
@ -1039,7 +1073,7 @@ input[type="file"] {
select { select {
width: 220px; width: 220px;
background-color: #ffffff; background-color: #ffffff;
border: 1px solid #bbb; border: 1px solid #cccccc;
} }
select[multiple], select[multiple],
@ -1301,14 +1335,17 @@ input[type="checkbox"][readonly] {
.control-group.warning select, .control-group.warning select,
.control-group.warning textarea { .control-group.warning textarea {
color: #c09853; color: #c09853;
}
.control-group.warning input,
.control-group.warning select,
.control-group.warning textarea {
border-color: #c09853; border-color: #c09853;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
} }
.control-group.warning .checkbox:focus,
.control-group.warning .radio:focus,
.control-group.warning input:focus, .control-group.warning input:focus,
.control-group.warning select:focus, .control-group.warning select:focus,
.control-group.warning textarea:focus { .control-group.warning textarea:focus {
@ -1337,14 +1374,17 @@ input[type="checkbox"][readonly] {
.control-group.error select, .control-group.error select,
.control-group.error textarea { .control-group.error textarea {
color: #b94a48; color: #b94a48;
}
.control-group.error input,
.control-group.error select,
.control-group.error textarea {
border-color: #b94a48; border-color: #b94a48;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
} }
.control-group.error .checkbox:focus,
.control-group.error .radio:focus,
.control-group.error input:focus, .control-group.error input:focus,
.control-group.error select:focus, .control-group.error select:focus,
.control-group.error textarea:focus { .control-group.error textarea:focus {
@ -1373,14 +1413,17 @@ input[type="checkbox"][readonly] {
.control-group.success select, .control-group.success select,
.control-group.success textarea { .control-group.success textarea {
color: #468847; color: #468847;
}
.control-group.success input,
.control-group.success select,
.control-group.success textarea {
border-color: #468847; border-color: #468847;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
} }
.control-group.success .checkbox:focus,
.control-group.success .radio:focus,
.control-group.success input:focus, .control-group.success input:focus,
.control-group.success select:focus, .control-group.success select:focus,
.control-group.success textarea:focus { .control-group.success textarea:focus {
@ -1397,6 +1440,45 @@ input[type="checkbox"][readonly] {
border-color: #468847; border-color: #468847;
} }
.control-group.info > label,
.control-group.info .help-block,
.control-group.info .help-inline {
color: #3a87ad;
}
.control-group.info .checkbox,
.control-group.info .radio,
.control-group.info input,
.control-group.info select,
.control-group.info textarea {
color: #3a87ad;
}
.control-group.info input,
.control-group.info select,
.control-group.info textarea {
border-color: #3a87ad;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.control-group.info input:focus,
.control-group.info select:focus,
.control-group.info textarea:focus {
border-color: #2d6987;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
}
.control-group.info .input-prepend .add-on,
.control-group.info .input-append .add-on {
color: #3a87ad;
background-color: #d9edf7;
border-color: #3a87ad;
}
input:focus:required:invalid, input:focus:required:invalid,
textarea:focus:required:invalid, textarea:focus:required:invalid,
select:focus:required:invalid { select:focus:required:invalid {
@ -1503,7 +1585,6 @@ select:focus:required:invalid:focus {
.input-prepend .add-on, .input-prepend .add-on,
.input-append .btn, .input-append .btn,
.input-prepend .btn { .input-prepend .btn {
margin-left: -1px;
vertical-align: top; vertical-align: top;
-webkit-border-radius: 0; -webkit-border-radius: 0;
-moz-border-radius: 0; -moz-border-radius: 0;
@ -1536,6 +1617,11 @@ select:focus:required:invalid:focus {
border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px;
} }
.input-append .add-on,
.input-append .btn {
margin-left: -1px;
}
.input-append .add-on:last-child, .input-append .add-on:last-child,
.input-append .btn:last-child { .input-append .btn:last-child {
-webkit-border-radius: 0 3px 3px 0; -webkit-border-radius: 0 3px 3px 0;
@ -1706,7 +1792,7 @@ legend + .control-group {
.form-horizontal .control-label { .form-horizontal .control-label {
float: left; float: left;
width: 140px; width: 160px;
padding-top: 5px; padding-top: 5px;
text-align: right; text-align: right;
} }
@ -1714,21 +1800,26 @@ legend + .control-group {
.form-horizontal .controls { .form-horizontal .controls {
*display: inline-block; *display: inline-block;
*padding-left: 20px; *padding-left: 20px;
margin-left: 160px; margin-left: 180px;
*margin-left: 0; *margin-left: 0;
} }
.form-horizontal .controls:first-child { .form-horizontal .controls:first-child {
*padding-left: 160px; *padding-left: 180px;
} }
.form-horizontal .help-block { .form-horizontal .help-block {
margin-top: 10px;
margin-bottom: 0; margin-bottom: 0;
} }
.form-horizontal input + .help-block,
.form-horizontal select + .help-block,
.form-horizontal textarea + .help-block {
margin-top: 10px;
}
.form-horizontal .form-actions { .form-horizontal .form-actions {
padding-left: 160px; padding-left: 180px;
} }
table { table {
@ -1853,7 +1944,7 @@ table {
.table-bordered colgroup + tbody tr:first-child td:last-child { .table-bordered colgroup + tbody tr:first-child td:last-child {
-webkit-border-top-right-radius: 4px; -webkit-border-top-right-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
-moz-border-right-topleft: 4px; -moz-border-radius-topleft: 4px;
} }
.table-striped tbody tr:nth-child(odd) td, .table-striped tbody tr:nth-child(odd) td,
@ -1873,145 +1964,145 @@ table [class*=span],
margin-left: 0; margin-left: 0;
} }
table .span1 { .table .span1 {
float: none; float: none;
width: 44px; width: 44px;
margin-left: 0; margin-left: 0;
} }
table .span2 { .table .span2 {
float: none; float: none;
width: 124px; width: 124px;
margin-left: 0; margin-left: 0;
} }
table .span3 { .table .span3 {
float: none; float: none;
width: 204px; width: 204px;
margin-left: 0; margin-left: 0;
} }
table .span4 { .table .span4 {
float: none; float: none;
width: 284px; width: 284px;
margin-left: 0; margin-left: 0;
} }
table .span5 { .table .span5 {
float: none; float: none;
width: 364px; width: 364px;
margin-left: 0; margin-left: 0;
} }
table .span6 { .table .span6 {
float: none; float: none;
width: 444px; width: 444px;
margin-left: 0; margin-left: 0;
} }
table .span7 { .table .span7 {
float: none; float: none;
width: 524px; width: 524px;
margin-left: 0; margin-left: 0;
} }
table .span8 { .table .span8 {
float: none; float: none;
width: 604px; width: 604px;
margin-left: 0; margin-left: 0;
} }
table .span9 { .table .span9 {
float: none; float: none;
width: 684px; width: 684px;
margin-left: 0; margin-left: 0;
} }
table .span10 { .table .span10 {
float: none; float: none;
width: 764px; width: 764px;
margin-left: 0; margin-left: 0;
} }
table .span11 { .table .span11 {
float: none; float: none;
width: 844px; width: 844px;
margin-left: 0; margin-left: 0;
} }
table .span12 { .table .span12 {
float: none; float: none;
width: 924px; width: 924px;
margin-left: 0; margin-left: 0;
} }
table .span13 { .table .span13 {
float: none; float: none;
width: 1004px; width: 1004px;
margin-left: 0; margin-left: 0;
} }
table .span14 { .table .span14 {
float: none; float: none;
width: 1084px; width: 1084px;
margin-left: 0; margin-left: 0;
} }
table .span15 { .table .span15 {
float: none; float: none;
width: 1164px; width: 1164px;
margin-left: 0; margin-left: 0;
} }
table .span16 { .table .span16 {
float: none; float: none;
width: 1244px; width: 1244px;
margin-left: 0; margin-left: 0;
} }
table .span17 { .table .span17 {
float: none; float: none;
width: 1324px; width: 1324px;
margin-left: 0; margin-left: 0;
} }
table .span18 { .table .span18 {
float: none; float: none;
width: 1404px; width: 1404px;
margin-left: 0; margin-left: 0;
} }
table .span19 { .table .span19 {
float: none; float: none;
width: 1484px; width: 1484px;
margin-left: 0; margin-left: 0;
} }
table .span20 { .table .span20 {
float: none; float: none;
width: 1564px; width: 1564px;
margin-left: 0; margin-left: 0;
} }
table .span21 { .table .span21 {
float: none; float: none;
width: 1644px; width: 1644px;
margin-left: 0; margin-left: 0;
} }
table .span22 { .table .span22 {
float: none; float: none;
width: 1724px; width: 1724px;
margin-left: 0; margin-left: 0;
} }
table .span23 { .table .span23 {
float: none; float: none;
width: 1804px; width: 1804px;
margin-left: 0; margin-left: 0;
} }
table .span24 { .table .span24 {
float: none; float: none;
width: 1884px; width: 1884px;
margin-left: 0; margin-left: 0;
@ -2025,10 +2116,30 @@ table .span24 {
background-color: #f2dede; background-color: #f2dede;
} }
.table tbody tr.warning td {
background-color: #fcf8e3;
}
.table tbody tr.info td { .table tbody tr.info td {
background-color: #d9edf7; background-color: #d9edf7;
} }
.table-hover tbody tr.success:hover td {
background-color: #d0e9c6;
}
.table-hover tbody tr.error:hover td {
background-color: #ebcccc;
}
.table-hover tbody tr.warning:hover td {
background-color: #faf2cc;
}
.table-hover tbody tr.info:hover td {
background-color: #c4e3f3;
}
[class^="icon-"], [class^="icon-"],
[class*=" icon-"] { [class*=" icon-"] {
display: inline-block; display: inline-block;
@ -2046,8 +2157,14 @@ table .span24 {
/* White icons with optional class, or on hover/active states of certain elements */ /* White icons with optional class, or on hover/active states of certain elements */
.icon-white, .icon-white,
.nav > .active > a > [class^="icon-"], .nav-tabs > .active > a > [class^="icon-"],
.nav > .active > a > [class*=" icon-"], .nav-tabs > .active > a > [class*=" icon-"],
.nav-pills > .active > a > [class^="icon-"],
.nav-pills > .active > a > [class*=" icon-"],
.nav-list > .active > a > [class^="icon-"],
.nav-list > .active > a > [class*=" icon-"],
.navbar-inverse .nav > .active > a > [class^="icon-"],
.navbar-inverse .nav > .active > a > [class*=" icon-"],
.dropdown-menu > li > a:hover > [class^="icon-"], .dropdown-menu > li > a:hover > [class^="icon-"],
.dropdown-menu > li > a:hover > [class*=" icon-"], .dropdown-menu > li > a:hover > [class*=" icon-"],
.dropdown-menu > .active > a > [class^="icon-"], .dropdown-menu > .active > a > [class^="icon-"],
@ -2759,7 +2876,7 @@ table .span24 {
.navbar-fixed-bottom .dropdown .caret { .navbar-fixed-bottom .dropdown .caret {
border-top: 0; border-top: 0;
border-bottom: 4px solid #000000; border-bottom: 4px solid #000000;
content: "\2191"; content: "";
} }
.dropup .dropdown-menu, .dropup .dropdown-menu,
@ -2783,7 +2900,7 @@ table .span24 {
border-radius: 0 6px 6px 6px; border-radius: 0 6px 6px 6px;
} }
.dropdown-submenu:hover .dropdown-menu { .dropdown-submenu:hover > .dropdown-menu {
display: block; display: block;
} }
@ -2866,7 +2983,6 @@ table .span24 {
position: relative; position: relative;
height: 0; height: 0;
overflow: hidden; overflow: hidden;
overflow: visible \9;
-webkit-transition: height 0.35s ease; -webkit-transition: height 0.35s ease;
-moz-transition: height 0.35s ease; -moz-transition: height 0.35s ease;
-o-transition: height 0.35s ease; -o-transition: height 0.35s ease;
@ -3030,7 +3146,7 @@ button.close {
.btn-mini { .btn-mini {
padding: 2px 6px; padding: 2px 6px;
font-size: 11px; font-size: 11px;
line-height: 16px; line-height: 17px;
} }
.btn-block { .btn-block {
@ -3047,6 +3163,12 @@ button.close {
margin-top: 5px; margin-top: 5px;
} }
input[type="submit"].btn-block,
input[type="reset"].btn-block,
input[type="button"].btn-block {
width: 100%;
}
.btn-primary.active, .btn-primary.active,
.btn-warning.active, .btn-warning.active,
.btn-danger.active, .btn-danger.active,
@ -3284,7 +3406,8 @@ input[type="submit"].btn.btn-mini {
} }
.btn-link, .btn-link,
.btn-link:active { .btn-link:active,
.btn-link[disabled] {
background-color: transparent; background-color: transparent;
background-image: none; background-image: none;
-webkit-box-shadow: none; -webkit-box-shadow: none;
@ -3307,11 +3430,17 @@ input[type="submit"].btn.btn-mini {
background-color: transparent; background-color: transparent;
} }
.btn-link[disabled]:hover {
color: #333333;
text-decoration: none;
}
.btn-group { .btn-group {
position: relative; position: relative;
*margin-left: .3em; *margin-left: .3em;
font-size: 0; font-size: 0;
white-space: nowrap; white-space: nowrap;
vertical-align: middle;
} }
.btn-group:first-child { .btn-group:first-child {
@ -4018,7 +4147,7 @@ input[type="submit"].btn.btn-mini {
*z-index: 2; *z-index: 2;
margin-bottom: 20px; margin-bottom: 20px;
overflow: visible; overflow: visible;
color: #555555; color: #777777;
} }
.navbar-inner { .navbar-inner {
@ -4037,11 +4166,23 @@ input[type="submit"].btn.btn-mini {
-moz-border-radius: 4px; -moz-border-radius: 4px;
border-radius: 4px; border-radius: 4px;
filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
*zoom: 1;
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
} }
.navbar-inner:before,
.navbar-inner:after {
display: table;
line-height: 0;
content: "";
}
.navbar-inner:after {
clear: both;
}
.navbar .container { .navbar .container {
width: auto; width: auto;
} }
@ -4057,7 +4198,7 @@ input[type="submit"].btn.btn-mini {
margin-left: -20px; margin-left: -20px;
font-size: 20px; font-size: 20px;
font-weight: 200; font-weight: 200;
color: #555555; color: #777777;
text-shadow: 0 1px 0 #ffffff; text-shadow: 0 1px 0 #ffffff;
} }
@ -4071,7 +4212,7 @@ input[type="submit"].btn.btn-mini {
} }
.navbar-link { .navbar-link {
color: #555555; color: #777777;
} }
.navbar-link:hover { .navbar-link:hover {
@ -4087,11 +4228,13 @@ input[type="submit"].btn.btn-mini {
.navbar .btn, .navbar .btn,
.navbar .btn-group { .navbar .btn-group {
margin-top: 6px; margin-top: 5px;
} }
.navbar .btn-group .btn { .navbar .btn-group .btn,
margin: 0; .navbar .input-prepend .btn,
.navbar .input-append .btn {
margin-top: 0;
} }
.navbar-form { .navbar-form {
@ -4182,9 +4325,12 @@ input[type="submit"].btn.btn-mini {
} }
.navbar-fixed-top .navbar-inner, .navbar-fixed-top .navbar-inner,
.navbar-fixed-bottom .navbar-inner,
.navbar-static-top .navbar-inner { .navbar-static-top .navbar-inner {
border: 0; border-width: 0 0 1px;
}
.navbar-fixed-bottom .navbar-inner {
border-width: 1px 0 0;
} }
.navbar-fixed-top .navbar-inner, .navbar-fixed-top .navbar-inner,
@ -4233,6 +4379,7 @@ input[type="submit"].btn.btn-mini {
.navbar .nav.pull-right { .navbar .nav.pull-right {
float: right; float: right;
margin-right: 0;
} }
.navbar .nav > li { .navbar .nav > li {
@ -4242,7 +4389,7 @@ input[type="submit"].btn.btn-mini {
.navbar .nav > li > a { .navbar .nav > li > a {
float: none; float: none;
padding: 10px 15px 10px; padding: 10px 15px 10px;
color: #555555; color: #777777;
text-decoration: none; text-decoration: none;
text-shadow: 0 1px 0 #ffffff; text-shadow: 0 1px 0 #ffffff;
} }
@ -4372,8 +4519,8 @@ input[type="submit"].btn.btn-mini {
} }
.navbar .nav li.dropdown > .dropdown-toggle .caret { .navbar .nav li.dropdown > .dropdown-toggle .caret {
border-top-color: #555555; border-top-color: #777777;
border-bottom-color: #555555; border-bottom-color: #777777;
} }
.navbar .nav li.dropdown.open > .dropdown-toggle .caret, .navbar .nav li.dropdown.open > .dropdown-toggle .caret,
@ -4599,12 +4746,12 @@ input[type="submit"].btn.btn-mini {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
} }
.pagination li { .pagination ul > li {
display: inline; display: inline;
} }
.pagination a, .pagination ul > li > a,
.pagination span { .pagination ul > li > span {
float: left; float: left;
padding: 0 14px; padding: 0 14px;
line-height: 38px; line-height: 38px;
@ -4614,36 +4761,36 @@ input[type="submit"].btn.btn-mini {
border-left-width: 0; border-left-width: 0;
} }
.pagination a:hover, .pagination ul > li > a:hover,
.pagination .active a, .pagination ul > .active > a,
.pagination .active span { .pagination ul > .active > span {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.pagination .active a, .pagination ul > .active > a,
.pagination .active span { .pagination ul > .active > span {
color: #999999; color: #999999;
cursor: default; cursor: default;
} }
.pagination .disabled span, .pagination ul > .disabled > span,
.pagination .disabled a, .pagination ul > .disabled > a,
.pagination .disabled a:hover { .pagination ul > .disabled > a:hover {
color: #999999; color: #999999;
cursor: default; cursor: default;
background-color: transparent; background-color: transparent;
} }
.pagination li:first-child a, .pagination ul > li:first-child > a,
.pagination li:first-child span { .pagination ul > li:first-child > span {
border-left-width: 1px; border-left-width: 1px;
-webkit-border-radius: 3px 0 0 3px; -webkit-border-radius: 3px 0 0 3px;
-moz-border-radius: 3px 0 0 3px; -moz-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px;
} }
.pagination li:last-child a, .pagination ul > li:last-child > a,
.pagination li:last-child span { .pagination ul > li:last-child > span {
-webkit-border-radius: 0 3px 3px 0; -webkit-border-radius: 0 3px 3px 0;
-moz-border-radius: 0 3px 3px 0; -moz-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0; border-radius: 0 3px 3px 0;
@ -4679,7 +4826,8 @@ input[type="submit"].btn.btn-mini {
display: inline; display: inline;
} }
.pager a { .pager a,
.pager span {
display: inline-block; display: inline-block;
padding: 5px 14px; padding: 5px 14px;
background-color: #fff; background-color: #fff;
@ -4694,7 +4842,8 @@ input[type="submit"].btn.btn-mini {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.pager .next a { .pager .next a,
.pager .next span {
float: right; float: right;
} }
@ -4703,25 +4852,26 @@ input[type="submit"].btn.btn-mini {
} }
.pager .disabled a, .pager .disabled a,
.pager .disabled a:hover { .pager .disabled a:hover,
.pager .disabled span {
color: #999999; color: #999999;
cursor: default; cursor: default;
background-color: #fff; background-color: #fff;
} }
.modal-open .dropdown-menu { .modal-open .modal .dropdown-menu {
z-index: 2050; z-index: 2050;
} }
.modal-open .dropdown.open { .modal-open .modal .dropdown.open {
*z-index: 2050; *z-index: 2050;
} }
.modal-open .popover { .modal-open .modal .popover {
z-index: 2060; z-index: 2060;
} }
.modal-open .tooltip { .modal-open .modal .tooltip {
z-index: 2080; z-index: 2080;
} }

126
docs/static/css/drf-styles.css vendored Normal file
View File

@ -0,0 +1,126 @@
/* Set the body padding-top when above 980px to push the content down from
below the navbar, which is fixed at >980px screen widths. */
@media (min-width: 980px) {
body {
padding-top: 71px;
}
}
body {
padding-bottom: 40px;
}
pre {
font-size: 12px;
}
a.github {
float: right;
margin-top: -12px;
}
a.github:hover {
text-decoration: none;
}
.dropdown .dropdown-menu {
display: none;
}
.dropdown.open .dropdown-menu {
display: block;
}
body.index #main-content iframe {
float: right;
}
body.index #main-content iframe {
float: right;
margin-right: -15px;
}
body.index #main-content p:first-of-type {
float: right;
margin-right: 8px;
margin-top: -1px;
}
#table-of-contents {
overflow: hidden;
}
pre {
overflow: auto;
word-wrap: normal;
white-space: pre;
}
/* Preserve the spacing of the navbar across different screen sizes. */
.navbar-inner {
padding: 5px 0;
}
@media (max-width: 979px) {
.navbar .brand {
margin-left: 0;
padding-left: 0;
}
.navbar-inner .container-fluid {
padding-left: 15px;
}
}
.nav-list li.main {
font-weight: bold;
}
/* Set the table of contents to static so it flows back into the content when
viewed on tablets and smaller. */
@media (max-width: 767px) {
#table-of-contents {
position: static;
}
}
/* When the page is in two-column layout, give the main content some room
to breath on the left. */
@media (min-width: 768px) {
#main-content {
padding-left: 1em;
}
}
blockquote {
font-family: Georgia, serif;
font-size: 18px;
font-style: italic;
margin: 0.25em 0;
padding: 0.25em 40px;
line-height: 1.45;
position: relative;
color: #383838;
border-left: none;
}
blockquote:before {
display: block;
content: "\201C";
font-size: 80px;
position: absolute;
left: -10px;
top: -20px;
color: #7a7a7a;
}
blockquote p:last-child {
color: #999999;
font-size: 14px;
display: block;
margin-top: 5px;
}

30
docs/static/css/prettify.css vendored Normal file
View File

@ -0,0 +1,30 @@
.com { color: #93a1a1; }
.lit { color: #195f91; }
.pun, .opn, .clo { color: #93a1a1; }
.fun { color: #dc322f; }
.str, .atv { color: #D14; }
.kwd, .prettyprint .tag { color: #1e347b; }
.typ, .atn, .dec, .var { color: teal; }
.pln { color: #48484c; }
.prettyprint {
padding: 8px;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
}
.prettyprint.linenums {
-webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
-moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
margin: 0 0 0 33px; /* IE indents via margin-left */
}
ol.linenums li {
padding-left: 12px;
color: #bebec5;
line-height: 20px;
text-shadow: 0 1px 0 #fff;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
docs/static/img/glyphicons-halflings.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

104
docs/static/js/bootstrap-affix.js vendored Executable file
View File

@ -0,0 +1,104 @@
/* ==========================================================
* bootstrap-affix.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#affix
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* AFFIX CLASS DEFINITION
* ====================== */
var Affix = function (element, options) {
this.options = $.extend({}, $.fn.affix.defaults, options)
this.$window = $(window).on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
this.$element = $(element)
this.checkPosition()
}
Affix.prototype.checkPosition = function () {
if (!this.$element.is(':visible')) return
var scrollHeight = $(document).height()
, scrollTop = this.$window.scrollTop()
, position = this.$element.offset()
, offset = this.options.offset
, offsetBottom = offset.bottom
, offsetTop = offset.top
, reset = 'affix affix-top affix-bottom'
, affix
if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top()
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
'bottom' : offsetTop != null && scrollTop <= offsetTop ?
'top' : false
if (this.affixed === affix) return
this.affixed = affix
this.unpin = affix == 'bottom' ? position.top - scrollTop : null
this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
}
/* AFFIX PLUGIN DEFINITION
* ======================= */
$.fn.affix = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('affix')
, options = typeof option == 'object' && option
if (!data) $this.data('affix', (data = new Affix(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.affix.Constructor = Affix
$.fn.affix.defaults = {
offset: 0
}
/* AFFIX DATA-API
* ============== */
$(window).on('load', function () {
$('[data-spy="affix"]').each(function () {
var $spy = $(this)
, data = $spy.data()
data.offset = data.offset || {}
data.offsetBottom && (data.offset.bottom = data.offsetBottom)
data.offsetTop && (data.offset.top = data.offsetTop)
$spy.affix(data)
})
})
}(window.jQuery);

2
docs/static/js/bootstrap-alert.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ========================================================== /* ==========================================================
* bootstrap-alert.js v2.1.0 * bootstrap-alert.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#alerts * http://twitter.github.com/bootstrap/javascript.html#alerts
* ========================================================== * ==========================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

4
docs/static/js/bootstrap-button.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ============================================================ /* ============================================================
* bootstrap-button.js v2.1.0 * bootstrap-button.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#buttons * http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================ * ============================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.
@ -51,7 +51,7 @@
} }
Button.prototype.toggle = function () { Button.prototype.toggle = function () {
var $parent = this.$element.parent('[data-toggle="buttons-radio"]') var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
$parent && $parent $parent && $parent
.find('.active') .find('.active')

2
docs/static/js/bootstrap-carousel.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ========================================================== /* ==========================================================
* bootstrap-carousel.js v2.1.0 * bootstrap-carousel.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#carousel * http://twitter.github.com/bootstrap/javascript.html#carousel
* ========================================================== * ==========================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

2
docs/static/js/bootstrap-collapse.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ============================================================= /* =============================================================
* bootstrap-collapse.js v2.1.0 * bootstrap-collapse.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#collapse * http://twitter.github.com/bootstrap/javascript.html#collapse
* ============================================================= * =============================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

6
docs/static/js/bootstrap-dropdown.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ============================================================ /* ============================================================
* bootstrap-dropdown.js v2.1.0 * bootstrap-dropdown.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#dropdowns * http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================ * ============================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.
@ -110,7 +110,7 @@
if (!selector) { if (!selector) {
selector = $this.attr('href') selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
} }
$parent = $(selector) $parent = $(selector)
@ -142,7 +142,7 @@
$('html') $('html')
.on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus) .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
$('body') $('body')
.on('click.dropdown touchstart.dropdown.data-api', '.dropdown', function (e) { e.stopPropagation() }) .on('click.dropdown touchstart.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle) .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
.on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
}) })

2
docs/static/js/bootstrap-modal.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ========================================================= /* =========================================================
* bootstrap-modal.js v2.1.0 * bootstrap-modal.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#modals * http://twitter.github.com/bootstrap/javascript.html#modals
* ========================================================= * =========================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

2
docs/static/js/bootstrap-popover.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* =========================================================== /* ===========================================================
* bootstrap-popover.js v2.1.0 * bootstrap-popover.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#popovers * http://twitter.github.com/bootstrap/javascript.html#popovers
* =========================================================== * ===========================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

2
docs/static/js/bootstrap-scrollspy.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ============================================================= /* =============================================================
* bootstrap-scrollspy.js v2.1.0 * bootstrap-scrollspy.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#scrollspy * http://twitter.github.com/bootstrap/javascript.html#scrollspy
* ============================================================= * =============================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

2
docs/static/js/bootstrap-tab.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ======================================================== /* ========================================================
* bootstrap-tab.js v2.1.0 * bootstrap-tab.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#tabs * http://twitter.github.com/bootstrap/javascript.html#tabs
* ======================================================== * ========================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

2
docs/static/js/bootstrap-tooltip.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* =========================================================== /* ===========================================================
* bootstrap-tooltip.js v2.1.0 * bootstrap-tooltip.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#tooltips * http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame * Inspired by the original jQuery.tipsy by Jason Frame
* =========================================================== * ===========================================================

2
docs/static/js/bootstrap-transition.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* =================================================== /* ===================================================
* bootstrap-transition.js v2.1.0 * bootstrap-transition.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#transitions * http://twitter.github.com/bootstrap/javascript.html#transitions
* =================================================== * ===================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.

4
docs/static/js/bootstrap-typeahead.js vendored Normal file → Executable file
View File

@ -1,5 +1,5 @@
/* ============================================================= /* =============================================================
* bootstrap-typeahead.js v2.1.0 * bootstrap-typeahead.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#typeahead * http://twitter.github.com/bootstrap/javascript.html#typeahead
* ============================================================= * =============================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.
@ -174,7 +174,7 @@
.on('keypress', $.proxy(this.keypress, this)) .on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this)) .on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) { if ($.browser.chrome || $.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keydown, this)) this.$element.on('keydown', $.proxy(this.keydown, this))
} }

2
docs/static/js/jquery-1.8.1-min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

28
docs/static/js/prettify.js vendored Normal file
View File

@ -0,0 +1,28 @@
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();

View File

@ -7,54 +7,16 @@
<meta name="author" content=""> <meta name="author" content="">
<!-- Le styles --> <!-- Le styles -->
<link href="{{ base_url }}/css/prettify.css" rel="stylesheet">
<link href="{{ base_url }}/css/bootstrap.css" rel="stylesheet"> <link href="{{ base_url }}/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
.nav-list li.main {
font-weight: bold;
}
blockquote {
font-family: Georgia, serif;
font-size: 18px;
font-style: italic;
margin: 0.25em 0;
padding: 0.25em 40px;
line-height: 1.45;
position: relative;
color: #383838;
border-left: none;
}
blockquote:before {
display: block;
content: "\201C";
font-size: 80px;
position: absolute;
left: -10px;
top: -20px;
color: #7a7a7a;
}
blockquote p:last-child {
color: #999999;
font-size: 14px;
display: block;
margin-top: 5px;
}
</style>
<link href="{{ base_url }}/css/bootstrap-responsive.css" rel="stylesheet"> <link href="{{ base_url }}/css/bootstrap-responsive.css" rel="stylesheet">
<link href="{{ base_url }}/css/drf-styles.css" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements --> <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--> <![endif]-->
<body> <body onload="prettyPrint()" class="{{ page_id }}">
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner"> <div class="navbar-inner">
@ -108,14 +70,14 @@ margin-top: 5px;
</li> </li>
</ul> </ul>
<ul class="nav pull-right"> <ul class="nav pull-right">
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Version: 2.0.0 <b class="caret"></b></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Version: 2.0.0 <b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#">Trunk</a></li> <li><a href="#">Trunk</a></li>
<li><a href="#">2.0.0</a></li> <li><a href="#">2.0.0</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
</div> </div>
@ -123,16 +85,16 @@ margin-top: 5px;
<div class="container-fluid"> <div class="container-fluid">
<div class="row-fluid"> <div class="row-fluid">
<div class="span3"> <div class="span3">
<div class="well affix span3"> <div id="table-of-contents" class="well affix span3">
<ul class="nav nav-list side-nav"> <ul class="nav nav-list side-nav">
{{ toc }} {{ toc }}
</ul> </ul>
</div> </div>
</div> </div>
<div class="span9"> <div id="main-content" class="span9">
{{ content }} {{ content }}
</div><!--/span--> </div><!--/span-->
</div><!--/row--> </div><!--/row-->
</div><!--/.fluid-container--> </div><!--/.fluid-container-->
@ -140,13 +102,15 @@ margin-top: 5px;
<!-- Le javascript <!-- Le javascript
================================================== --> ================================================== -->
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script src="{{ base_url }}/js/jquery.js"></script> <script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
<script src="{{ base_url }}/js/prettify.js"></script>
<script src="{{ base_url }}/js/bootstrap-dropdown.js"></script> <script src="{{ base_url }}/js/bootstrap-dropdown.js"></script>
<script src="{{ base_url }}/js/bootstrap-scrollspy.js"></script> <script src="{{ base_url }}/js/bootstrap-scrollspy.js"></script>
<script src="{{ base_url }}/js/bootstrap-collapse.js"></script>
<script> <script>
//$('.side-nav').scrollspy() //$('.side-nav').scrollspy()
var shiftWindow = function() { scrollBy(0, -50) }; var shiftWindow = function() { scrollBy(0, -50) };
if (location.hash) shiftWindow(); if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow); window.addEventListener("hashchange", shiftWindow);
</script> </script>
</body></html> </body></html>

View File

@ -2,43 +2,44 @@
The following people have helped make REST framework great. The following people have helped make REST framework great.
* Tom Christie <tomchristie> * Tom Christie - [tomchristie]
* Marko Tibold <markotibold> * Marko Tibold - [markotibold]
* Paul Bagwell <pbgwl> * Paul Bagwell - [pbgwl]
* Sébastien Piquemal <sebpiq> * Sébastien Piquemal - [sebpiq]
* Carmen Wick <cwick> * Carmen Wick - [cwick]
* Alex Ehlke <aehlke> * Alex Ehlke - [aehlke]
* Alen Mujezinovic <flashingpumpkin> * Alen Mujezinovic - [flashingpumpkin]
* Carles Barrobés <txels> * Carles Barrobés - [txels]
* Michael Fötsch <mfoetsch> * Michael Fötsch - [mfoetsch]
* David Larlet <david> * David Larlet - [david]
* Andrew Straw <astraw> * Andrew Straw - [astraw]
* Zeth <zeth> * Zeth - [zeth]
* Fernando Zunino <fzunino> * Fernando Zunino - [fzunino]
* Jens Alm <ulmus> * Jens Alm - [ulmus]
* Craig Blaszczyk <jakul> * Craig Blaszczyk - [jakul]
* Garcia Solero <garciasolero> * Garcia Solero - [garciasolero]
* Tom Drummond <devioustree> * Tom Drummond - [devioustree]
* Danilo Bargen <gwrtheyrn> * Danilo Bargen - [gwrtheyrn]
* Andrew McCloud <amccloud> * Andrew McCloud - [amccloud]
* Thomas Steinacher <thomasst> * Thomas Steinacher - [thomasst]
* Meurig Freeman <meurig> * Meurig Freeman - [meurig]
* Anthony Nemitz <anemitz> * Anthony Nemitz - [anemitz]
* Ewoud Kohl van Wijngaarden <ekohl> * Ewoud Kohl van Wijngaarden - [ekohl]
* Michael Ding <yandy> * Michael Ding - [yandy]
* Mjumbe Poe <mjumbewu> * Mjumbe Poe - [mjumbewu]
* Natim <natim> * Natim - [natim]
* Sebastian Żurek <sebzur> * Sebastian Żurek - [sebzur]
* Benoit C <dzen> * Benoit C - [dzen]
* Chris Pickett <bunchesofdonald> * Chris Pickett - [bunchesofdonald]
* Ben Timby <btimby> * Ben Timby - [btimby]
* Michele Lazzeri <michelelazzeri-nextage> * Michele Lazzeri - [michelelazzeri-nextage]
* Camille Harang <mammique> * Camille Harang - [mammique]
* Paul Oswald <poswald> * Paul Oswald - [poswald]
* Sean C. Farley <scfarley> * Sean C. Farley - [scfarley]
* Daniel Izquierdo <izquierdo> * Daniel Izquierdo - [izquierdo]
* Can Yavuz <tschan> * Can Yavuz - [tschan]
* Shawn Lewis <shawnlewis> * Shawn Lewis - [shawnlewis]
* Alec Perkins - [alecperkins]
Many thanks to everyone who's contributed to the project. Many thanks to everyone who's contributed to the project.
@ -55,11 +56,49 @@ Continuous integration testing is managed with [Travis CI][travis-ci].
To contact the author directly: To contact the author directly:
* twitter: [@_tomchristie][twitter] * twitter: [@_tomchristie][twitter]
* mail: [tom@tomchristie.com][email] * email: [tom@tomchristie.com][email]
[email]: mailto:tom@tomchristie.com [email]: mailto:tom@tomchristie.com
[twitter]: http://twitter.com/_tomchristie [twitter]: http://twitter.com/_tomchristie
[bootstrap]: http://twitter.github.com/bootstrap/ [bootstrap]: http://twitter.github.com/bootstrap/
[markdown]: http://daringfireball.net/projects/markdown/ [markdown]: http://daringfireball.net/projects/markdown/
[github]: github.com/tomchristie/django-rest-framework [github]: github.com/tomchristie/django-rest-framework
[travis-ci]: https://secure.travis-ci.org/tomchristie/django-rest-framework [travis-ci]: https://secure.travis-ci.org/tomchristie/django-rest-framework
[tomchristie]: https://github.com/tomchristie
[markotibold]: https://github.com/markotibold
[pbgwl]: https://github.com/pbgwl
[sebpiq]: https://github.com/sebpiq
[cwick]: https://github.com/cwick
[aehlke]: https://github.com/aehlke
[flashingpumpkin]: https://github.com/flashingpumpkin
[txels]: https://github.com/txels
[mfoetsch]: https://github.com/mfoetsch
[david]: https://github.com/david
[astraw]: https://github.com/astraw
[zeth]: https://github.com/zeth
[fzunino]: https://github.com/fzunino
[ulmus]: https://github.com/ulmus
[jakul]: https://github.com/jakul
[garciasolero]: https://github.com/garciasolero
[devioustree]: https://github.com/devioustree
[gwrtheyrn]: https://github.com/gwrtheyrn
[amccloud]: https://github.com/amccloud
[thomasst]: https://github.com/thomasst
[meurig]: https://github.com/meurig
[anemitz]: https://github.com/anemitz
[ekohl]: https://github.com/ekohl
[yandy]: https://github.com/yandy
[mjumbewu]: https://github.com/mjumbewu
[natim]: https://github.com/natim
[sebzur]: https://github.com/sebzur
[dzen]: https://github.com/dzen
[bunchesofdonald]: https://github.com/bunchesofdonald
[btimby]: https://github.com/btimby
[michelelazzeri-nextage]: https://github.com/michelelazzeri-nextage
[mammique]: https://github.com/mammique
[poswald]: https://github.com/poswald
[scfarley]: https://github.com/scfarley
[izquierdo]: https://github.com/izquierdo
[tschan]: https://github.com/tschan
[shawnlewis]: https://github.com/shawnlewis
[alecperkins]: https://github.com/alecperkins

View File

@ -8,6 +8,7 @@ This tutorial will walk you through the building blocks that make up REST framew
Before we do anything else we'll create a new virtual environment, using [virtualenv]. This will make sure our package configuration is keep nicely isolated from any other projects we're working on. Before we do anything else we'll create a new virtual environment, using [virtualenv]. This will make sure our package configuration is keep nicely isolated from any other projects we're working on.
:::bash
mkdir ~/env mkdir ~/env
virtualenv --no-site-packages ~/env/tutorial virtualenv --no-site-packages ~/env/tutorial
source ~/env/tutorial/bin/activate source ~/env/tutorial/bin/activate

View File

@ -7,30 +7,31 @@ We can also write our API views using class based views, rather than function ba
We'll start by rewriting the root view as a class based view. All this involves is a little bit of refactoring. We'll start by rewriting the root view as a class based view. All this involves is a little bit of refactoring.
from blog.models import Comment from blog.models import Comment
from blog.serializers import ComentSerializer from blog.serializers import CommentSerializer
from django.http import Http404 from django.http import Http404
from djangorestframework.views import APIView from djangorestframework.views import APIView
from djangorestframework.response import Response from djangorestframework.response import Response
from djangorestframework.status import status from djangorestframework import status
class CommentRoot(APIView): class CommentRoot(APIView):
""" """
List all comments, or create a new comment. List all comments, or create a new comment.
""" """
def get(self, request, format=None): def get(self, request, format=None):
comments = Comment.objects.all() comments = Comment.objects.all()
serializer = ComentSerializer(instance=comments) serializer = CommentSerializer(instance=comments)
return Response(serializer.data) return Response(serializer.data)
def post(self, request, format=None) def post(self, request, format=None):
serializer = ComentSerializer(request.DATA) serializer = CommentSerializer(request.DATA)
if serializer.is_valid(): if serializer.is_valid():
comment = serializer.object comment = serializer.object
comment.save() comment.save()
return Response(serializer.serialized, status=HTTP_201_CREATED) return Response(serializer.serialized, status=status.HTTP_201_CREATED)
return Response(serializer.serialized_errors, status=HTTP_400_BAD_REQUEST) return Response(serializer.serialized_errors, status=status.HTTP_400_BAD_REQUEST)
comment_root = CommentRoot.as_view() comment_root = CommentRoot.as_view()
So far, so good. It looks pretty similar to the previous case, but we've got better seperation between the different HTTP methods. We'll also need to update the instance view. So far, so good. It looks pretty similar to the previous case, but we've got better seperation between the different HTTP methods. We'll also need to update the instance view.
@ -38,18 +39,18 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
""" """
Retrieve, update or delete a comment instance. Retrieve, update or delete a comment instance.
""" """
def get_object(self, pk): def get_object(self, pk):
try: try:
return Comment.objects.get(pk=pk) return Comment.objects.get(pk=pk)
except Comment.DoesNotExist: except Comment.DoesNotExist:
raise Http404 raise Http404
def get(self, request, pk, format=None): def get(self, request, pk, format=None):
comment = self.get_object(pk) comment = self.get_object(pk)
serializer = CommentSerializer(instance=comment) serializer = CommentSerializer(instance=comment)
return Response(serializer.data) return Response(serializer.data)
def put(self, request, pk, format=None): def put(self, request, pk, format=None):
comment = self.get_object(pk) comment = self.get_object(pk)
serializer = CommentSerializer(request.DATA, instance=comment) serializer = CommentSerializer(request.DATA, instance=comment)
@ -64,7 +65,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
comment.delete() comment.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
comment_instance = CommentInstance.as_view() comment_instance = CommentInstance.as_view()
That's looking good. Again, it's still pretty similar to the function based view right now. That's looking good. Again, it's still pretty similar to the function based view right now.
Okay, we're done. If you run the development server everything should be working just as before. Okay, we're done. If you run the development server everything should be working just as before.

View File

@ -44,6 +44,6 @@ We've reached the end of our tutorial. If you want to get more involved in the
* Contribute on GitHub by reviewing issues, and submitting issues or pull requests. * Contribute on GitHub by reviewing issues, and submitting issues or pull requests.
* Join the REST framework group, and help build the community. * Join the REST framework group, and help build the community.
* Follow me on Twitter and say hi. * Follow me [on Twitter](https://twitter.com/_tomchristie) and say hi.
Now go build something great. Now go build something great.

View File

@ -24,11 +24,12 @@ else:
main_header = '<li class="main"><a href="#{{ anchor }}">{{ title }}</a></li>' main_header = '<li class="main"><a href="#{{ anchor }}">{{ title }}</a></li>'
sub_header = '<li><a href="#{{ anchor }}">{{ title }}</a></li>' sub_header = '<li><a href="#{{ anchor }}">{{ title }}</a></li>'
code_label = r'<a class="github" href="https://github.com/tomchristie/django-rest-framework/blob/restframework2/djangorestframework/\1"><span class="label label-info">\1</span></a>'
page = open(os.path.join(docs_dir, 'template.html'), 'r').read() page = open(os.path.join(docs_dir, 'template.html'), 'r').read()
# Copy static files # Copy static files
for static in ['css', 'js']: for static in ['css', 'js', 'img']:
source = os.path.join(docs_dir, 'static', static) source = os.path.join(docs_dir, 'static', static)
target = os.path.join(html_dir, static) target = os.path.join(html_dir, static)
if os.path.exists(target): if os.path.exists(target):
@ -65,5 +66,9 @@ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
if not os.path.exists(build_dir): if not os.path.exists(build_dir):
os.makedirs(build_dir) os.makedirs(build_dir)
output = page.replace('{{ content }}', content).replace('{{ toc }}', toc).replace('{{ base_url }}', base_url).replace('{{ suffix }}', suffix).replace('{{ index }}', index) output = page.replace('{{ content }}', content).replace('{{ toc }}', toc).replace('{{ base_url }}', base_url).replace('{{ suffix }}', suffix).replace('{{ index }}', index)
output = output.replace('{{ page_id }}', filename[:-3])
output = re.sub(r'a href="([^"]*)\.md"', r'a href="\1%s"' % suffix, output) output = re.sub(r'a href="([^"]*)\.md"', r'a href="\1%s"' % suffix, output)
output = re.sub(r'<pre><code>:::bash', r'<pre class="prettyprint lang-bsh">', output)
output = re.sub(r'<pre>', r'<pre class="prettyprint lang-py">', output)
output = re.sub(r'<a class="github" href="([^"]*)"></a>', code_label, output)
open(build_file, 'w').write(output.encode('utf-8')) open(build_file, 'w').write(output.encode('utf-8'))