mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-10 19:56:59 +03:00
Merge master
This commit is contained in:
commit
f13c0ebe69
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,7 +3,7 @@
|
|||
*~
|
||||
.*
|
||||
|
||||
html/
|
||||
site/
|
||||
htmlcov/
|
||||
coverage/
|
||||
build/
|
||||
|
|
|
@ -4,6 +4,7 @@ sudo: false
|
|||
|
||||
env:
|
||||
- TOX_ENV=py27-flake8
|
||||
- TOX_ENV=py27-docs
|
||||
- TOX_ENV=py34-django17
|
||||
- TOX_ENV=py33-django17
|
||||
- TOX_ENV=py32-django17
|
||||
|
|
|
@ -101,15 +101,15 @@ There are many great markdown editors that make working with the documentation r
|
|||
|
||||
## Building the documentation
|
||||
|
||||
To build the documentation, simply run the `mkdocs.py` script.
|
||||
To build the documentation, install MkDocs with `pip install mkdocs` and then run the following command.
|
||||
|
||||
./mkdocs.py
|
||||
mkdocs build
|
||||
|
||||
This will build the html output into the `html` directory.
|
||||
|
||||
You can build the documentation and open a preview in a browser window by using the `-p` flag.
|
||||
You can build the documentation and open a preview in a browser window by using the `serve` command.
|
||||
|
||||
./mkdocs.py -p
|
||||
mkdocs serve
|
||||
|
||||
## Language style
|
||||
|
||||
|
|
1
docs/CNAME
Normal file
1
docs/CNAME
Normal file
|
@ -0,0 +1 @@
|
|||
www.django-rest-framework.org
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="authentication.py"></a>
|
||||
source: authentication.py
|
||||
|
||||
# Authentication
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="negotiation.py"></a>
|
||||
source: negotiation.py
|
||||
|
||||
# Content negotiation
|
||||
|
||||
|
@ -29,7 +29,7 @@ The priorities for each of the given media types would be:
|
|||
|
||||
If the requested view was only configured with renderers for `YAML` and `HTML`, then REST framework would select whichever renderer was listed first in the `renderer_classes` list or `DEFAULT_RENDERER_CLASSES` setting.
|
||||
|
||||
For more information on the `HTTP Accept` header, see [RFC 2616][accept-header]
|
||||
For more information on the `HTTP Accept` header, see [RFC 2616][accept-header]
|
||||
|
||||
---
|
||||
|
||||
|
@ -62,7 +62,7 @@ request when selecting the appropriate parser or renderer.
|
|||
Select the first parser in the `.parser_classes` list.
|
||||
"""
|
||||
return parsers[0]
|
||||
|
||||
|
||||
def select_renderer(self, request, renderers, format_suffix):
|
||||
"""
|
||||
Select the first renderer in the `.renderer_classes` list.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="exceptions.py"></a>
|
||||
source: exceptions.py
|
||||
|
||||
# Exceptions
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="fields.py"></a>
|
||||
source: fields.py
|
||||
|
||||
# Serializer fields
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="filters.py"></a>
|
||||
source: filters.py
|
||||
|
||||
# Filtering
|
||||
|
||||
|
@ -26,7 +26,7 @@ For example:
|
|||
|
||||
class PurchaseList(generics.ListAPIView):
|
||||
serializer_class = PurchaseSerializer
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
This view should return a list of all the purchases
|
||||
|
@ -38,7 +38,7 @@ For example:
|
|||
|
||||
## Filtering against the URL
|
||||
|
||||
Another style of filtering might involve restricting the queryset based on some part of the URL.
|
||||
Another style of filtering might involve restricting the queryset based on some part of the URL.
|
||||
|
||||
For example if your URL config contained an entry like this:
|
||||
|
||||
|
@ -48,7 +48,7 @@ You could then write a view that returned a purchase queryset filtered by the us
|
|||
|
||||
class PurchaseList(generics.ListAPIView):
|
||||
serializer_class = PurchaseSerializer
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
This view should return a list of all the purchases for
|
||||
|
@ -57,7 +57,7 @@ You could then write a view that returned a purchase queryset filtered by the us
|
|||
username = self.kwargs['username']
|
||||
return Purchase.objects.filter(purchaser__username=username)
|
||||
|
||||
## Filtering against query parameters
|
||||
## Filtering against query parameters
|
||||
|
||||
A final example of filtering the initial queryset would be to determine the initial queryset based on query parameters in the url.
|
||||
|
||||
|
@ -65,7 +65,7 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/
|
|||
|
||||
class PurchaseList(generics.ListAPIView):
|
||||
serializer_class = PurchaseSerializer
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Optionally restricts the returned purchases to a given user,
|
||||
|
@ -113,7 +113,7 @@ For instance, given the previous example, and a product with an id of `4675`, th
|
|||
http://example.com/api/products/4675/?category=clothing&max_price=10.00
|
||||
|
||||
## Overriding the initial queryset
|
||||
|
||||
|
||||
Note that you can use both an overridden `.get_queryset()` and generic filtering together, and everything will work as expected. For example, if `Product` had a many-to-many relationship with `User`, named `purchase`, you might want to write a view like this:
|
||||
|
||||
class PurchasedProductsList(generics.ListAPIView):
|
||||
|
@ -124,7 +124,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
|
|||
model = Product
|
||||
serializer_class = ProductSerializer
|
||||
filter_class = ProductFilter
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return user.purchase_set.all()
|
||||
|
@ -135,7 +135,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
|
|||
|
||||
## DjangoFilterBackend
|
||||
|
||||
The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter].
|
||||
The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter].
|
||||
|
||||
To use REST framework's `DjangoFilterBackend`, first install `django-filter`.
|
||||
|
||||
|
@ -216,7 +216,7 @@ This is nice, but it exposes the Django's double underscore convention as part o
|
|||
And now you can execute:
|
||||
|
||||
http://example.com/api/products?manufacturer=foo
|
||||
|
||||
|
||||
For more details on using filter sets see the [django-filter documentation][django-filter-docs].
|
||||
|
||||
---
|
||||
|
@ -224,7 +224,7 @@ For more details on using filter sets see the [django-filter documentation][djan
|
|||
**Hints & Tips**
|
||||
|
||||
* By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'DEFAULT_FILTER_BACKENDS'` setting.
|
||||
* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].)
|
||||
* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].)
|
||||
* `django-filter` supports filtering across relationships, using Django's double-underscore syntax.
|
||||
* For Django 1.3 support, make sure to install `django-filter` version 0.5.4, as later versions drop support for 1.3.
|
||||
|
||||
|
@ -316,7 +316,7 @@ Typically you'd instead control this by setting `order_by` on the initial querys
|
|||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
filter_backends = (filters.OrderingFilter,)
|
||||
ordering = ('username',)
|
||||
ordering = ('username',)
|
||||
|
||||
The `ordering` attribute may be either a string or a list/tuple of strings.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="urlpatterns.py"></a>
|
||||
source: urlpatterns.py
|
||||
|
||||
# Format suffixes
|
||||
|
||||
|
@ -7,7 +7,7 @@ used all the time.
|
|||
>
|
||||
> — Roy Fielding, [REST discuss mailing list][cite]
|
||||
|
||||
A common pattern for Web APIs is to use filename extensions on URLs to provide an endpoint for a given media type. For example, 'http://example.com/api/users.json' to serve a JSON representation.
|
||||
A common pattern for Web APIs is to use filename extensions on URLs to provide an endpoint for a given media type. For example, 'http://example.com/api/users.json' to serve a JSON representation.
|
||||
|
||||
Adding format-suffix patterns to each individual entry in the URLconf for your API is error-prone and non-DRY, so REST framework provides a shortcut to adding these patterns to your URLConf.
|
||||
|
||||
|
@ -21,7 +21,7 @@ Arguments:
|
|||
|
||||
* **urlpatterns**: Required. A URL pattern list.
|
||||
* **suffix_required**: Optional. A boolean indicating if suffixes in the URLs should be optional or mandatory. Defaults to `False`, meaning that suffixes are optional by default.
|
||||
* **allowed**: Optional. A list or tuple of valid format suffixes. If not provided, a wildcard format suffix pattern will be used.
|
||||
* **allowed**: Optional. A list or tuple of valid format suffixes. If not provided, a wildcard format suffix pattern will be used.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -33,7 +33,7 @@ Example:
|
|||
url(r'^comments/$', views.comment_list),
|
||||
url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)
|
||||
]
|
||||
|
||||
|
||||
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
|
||||
|
||||
When using `format_suffix_patterns`, you must make sure to add the `'format'` keyword argument to the corresponding views. For example:
|
||||
|
@ -56,12 +56,12 @@ The name of the kwarg used may be modified by using the `FORMAT_SUFFIX_KWARG` se
|
|||
Also note that `format_suffix_patterns` does not support descending into `include` URL patterns.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Accept headers vs. format suffixes
|
||||
|
||||
There seems to be a view among some of the Web community that filename extensions are not a RESTful pattern, and that `HTTP Accept` headers should always be used instead.
|
||||
|
||||
It is actually a misconception. For example, take the following quote from Roy Fielding discussing the relative merits of query parameter media-type indicators vs. file extension media-type indicators:
|
||||
It is actually a misconception. For example, take the following quote from Roy Fielding discussing the relative merits of query parameter media-type indicators vs. file extension media-type indicators:
|
||||
|
||||
“That's why I always prefer extensions. Neither choice has anything to do with REST.” — Roy Fielding, [REST discuss mailing list][cite2]
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<a class="github" href="mixins.py"></a>
|
||||
<a class="github" href="generics.py"></a>
|
||||
source: mixins.py
|
||||
generics.py
|
||||
|
||||
# Generic views
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="pagination.py"></a>
|
||||
source: pagination.py
|
||||
|
||||
# Pagination
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
|||
>
|
||||
> — [Django documentation][cite]
|
||||
|
||||
REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.
|
||||
REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.
|
||||
|
||||
## Paginating basic data
|
||||
|
||||
|
@ -33,7 +33,7 @@ The `context` argument of the `PaginationSerializer` class may optionally includ
|
|||
request = RequestFactory().get('/foobar')
|
||||
serializer = PaginationSerializer(instance=page, context={'request': request})
|
||||
serializer.data
|
||||
# {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}
|
||||
# {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}
|
||||
|
||||
We could now return that data in a `Response` object, and it would be rendered into the correct media type.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="parsers.py"></a>
|
||||
source: parsers.py
|
||||
|
||||
# Parsers
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="permissions.py"></a>
|
||||
source: permissions.py
|
||||
|
||||
# Permissions
|
||||
|
||||
|
@ -12,7 +12,7 @@ Permission checks are always run at the very start of the view, before any other
|
|||
|
||||
## How permissions are determined
|
||||
|
||||
Permissions in REST framework are always defined as a list of permission classes.
|
||||
Permissions in REST framework are always defined as a list of permission classes.
|
||||
|
||||
Before running the main body of the view each permission in the list is checked.
|
||||
If any permission check fails an `exceptions.PermissionDenied` exception will be raised, and the main body of the view will not run.
|
||||
|
@ -220,9 +220,9 @@ As well as global permissions, that are run against all incoming requests, you c
|
|||
def has_object_permission(self, request, view, obj):
|
||||
# Read permissions are allowed to any request,
|
||||
# so we'll always allow GET, HEAD or OPTIONS requests.
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
|
||||
|
||||
# Instance must have an attribute named `owner`.
|
||||
return obj.owner == request.user
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="relations.py"></a>
|
||||
source: relations.py
|
||||
|
||||
# Serializer relations
|
||||
|
||||
|
@ -33,7 +33,7 @@ In order to explain the various types of relational fields, we'll use a couple o
|
|||
class Meta:
|
||||
unique_together = ('album', 'order')
|
||||
order_by = 'order'
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return '%d: %s' % (self.order, self.title)
|
||||
|
||||
|
@ -42,10 +42,10 @@ In order to explain the various types of relational fields, we'll use a couple o
|
|||
`RelatedField` may be used to represent the target of the relationship using its `__unicode__` method.
|
||||
|
||||
For example, the following serializer.
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = serializers.RelatedField(many=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -74,10 +74,10 @@ This field is read only.
|
|||
`PrimaryKeyRelatedField` may be used to represent the target of the relationship using its primary key.
|
||||
|
||||
For example, the following serializer:
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -108,11 +108,11 @@ By default this field is read-write, although you can change this behavior using
|
|||
`HyperlinkedRelatedField` may be used to represent the target of the relationship using a hyperlink.
|
||||
|
||||
For example, the following serializer:
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True,
|
||||
view_name='track-detail')
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -146,11 +146,11 @@ By default this field is read-write, although you can change this behavior using
|
|||
`SlugRelatedField` may be used to represent the target of the relationship using a field on the target.
|
||||
|
||||
For example, the following serializer:
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = serializers.SlugRelatedField(many=True, read_only=True,
|
||||
slug_field='title')
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -222,10 +222,10 @@ For example, the following serializer:
|
|||
class Meta:
|
||||
model = Track
|
||||
fields = ('order', 'title')
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = TrackSerializer(many=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -262,7 +262,7 @@ For, example, we could define a relational field, to serialize a track to a cust
|
|||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = TrackListingField(many=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -302,7 +302,7 @@ If you have not set a related name for the reverse relationship, you'll need to
|
|||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
fields = ('track_set', ...)
|
||||
fields = ('track_set', ...)
|
||||
|
||||
See the Django documentation on [reverse relationships][reverse-relationships] for more details.
|
||||
|
||||
|
@ -315,14 +315,14 @@ For example, given the following model for a tag, which has a generic relationsh
|
|||
class TaggedItem(models.Model):
|
||||
"""
|
||||
Tags arbitrary model instances using a generic relation.
|
||||
|
||||
|
||||
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
|
||||
"""
|
||||
tag_name = models.SlugField()
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
tagged_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.tag
|
||||
|
||||
|
@ -353,7 +353,7 @@ We could define a custom field that could be used to serialize tagged instances,
|
|||
def to_native(self, value):
|
||||
"""
|
||||
Serialize tagged objects to a simple textual representation.
|
||||
"""
|
||||
"""
|
||||
if isinstance(value, Bookmark):
|
||||
return 'Bookmark: ' + value.url
|
||||
elif isinstance(value, Note):
|
||||
|
@ -366,7 +366,7 @@ If you need the target of the relationship to have a nested representation, you
|
|||
"""
|
||||
Serialize bookmark instances using a bookmark serializer,
|
||||
and note instances using a note serializer.
|
||||
"""
|
||||
"""
|
||||
if isinstance(value, Bookmark):
|
||||
serializer = BookmarkSerializer(value)
|
||||
elif isinstance(value, Note):
|
||||
|
@ -391,7 +391,7 @@ to ``True``.
|
|||
|
||||
## Advanced Hyperlinked fields
|
||||
|
||||
If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.
|
||||
If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.
|
||||
|
||||
There are two methods you'll need to override.
|
||||
|
||||
|
@ -411,7 +411,7 @@ May raise an `ObjectDoesNotExist` exception.
|
|||
|
||||
### Example
|
||||
|
||||
For example, if all your object URLs used both a account and a slug in the the URL to reference the object, you might create a custom field like this:
|
||||
For example, if all your object URLs used both a account and a slug in the the URL to reference the object, you might create a custom field like this:
|
||||
|
||||
class CustomHyperlinkedField(serializers.HyperlinkedRelatedField):
|
||||
def get_url(self, obj, view_name, request, format):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="renderers.py"></a>
|
||||
source: renderers.py
|
||||
|
||||
# Renderers
|
||||
|
||||
|
@ -115,7 +115,7 @@ The `jsonp` approach is essentially a browser hack, and is [only appropriate for
|
|||
|
||||
## YAMLRenderer
|
||||
|
||||
Renders the request data into `YAML`.
|
||||
Renders the request data into `YAML`.
|
||||
|
||||
Requires the `pyyaml` package to be installed.
|
||||
|
||||
|
@ -131,7 +131,7 @@ Note that non-ascii characters will be rendered using `\uXXXX` character escape.
|
|||
|
||||
## UnicodeYAMLRenderer
|
||||
|
||||
Renders the request data into `YAML`.
|
||||
Renders the request data into `YAML`.
|
||||
|
||||
Requires the `pyyaml` package to be installed.
|
||||
|
||||
|
@ -184,7 +184,7 @@ An example of a view that uses `TemplateHTMLRenderer`:
|
|||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return Response({'user': self.object}, template_name='user_detail.html')
|
||||
|
||||
|
||||
You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
|
||||
|
||||
If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers.
|
||||
|
@ -205,7 +205,7 @@ An example of a view that uses `TemplateHTMLRenderer`:
|
|||
|
||||
@api_view(('GET',))
|
||||
@renderer_classes((StaticHTMLRenderer,))
|
||||
def simple_html_view(request):
|
||||
def simple_html_view(request):
|
||||
data = '<html><body><h1>Hello, world</h1></body></html>'
|
||||
return Response(data)
|
||||
|
||||
|
@ -300,7 +300,7 @@ The following is an example plaintext renderer that will return a response with
|
|||
class PlainTextRenderer(renderers.BaseRenderer):
|
||||
media_type = 'text/plain'
|
||||
format = 'txt'
|
||||
|
||||
|
||||
def render(self, data, media_type=None, renderer_context=None):
|
||||
return data.encode(self.charset)
|
||||
|
||||
|
@ -340,7 +340,7 @@ You can do some pretty flexible things using REST framework's renderers. Some e
|
|||
* Provide either flat or nested representations from the same endpoint, depending on the requested media type.
|
||||
* Serve both regular HTML webpages, and JSON based API responses from the same endpoints.
|
||||
* Specify multiple types of HTML representation for API clients to use.
|
||||
* Underspecify a renderer's media type, such as using `media_type = 'image/*'`, and use the `Accept` header to vary the encoding of the response.
|
||||
* Underspecify a renderer's media type, such as using `media_type = 'image/*'`, and use the `Accept` header to vary the encoding of the response.
|
||||
|
||||
## Varying behaviour by media type
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="request.py"></a>
|
||||
source: request.py
|
||||
|
||||
# Requests
|
||||
|
||||
|
@ -108,7 +108,7 @@ REST framework supports a few browser enhancements such as browser-based `PUT`,
|
|||
|
||||
Browser-based `PUT`, `PATCH` and `DELETE` forms are transparently supported.
|
||||
|
||||
For more information see the [browser enhancements documentation].
|
||||
For more information see the [browser enhancements documentation].
|
||||
|
||||
## .content_type
|
||||
|
||||
|
@ -118,7 +118,7 @@ You won't typically need to directly access the request's content type, as you'l
|
|||
|
||||
If you do need to access the content type of the request you should use the `.content_type` property in preference to using `request.META.get('HTTP_CONTENT_TYPE')`, as it provides transparent support for browser-based non-form content.
|
||||
|
||||
For more information see the [browser enhancements documentation].
|
||||
For more information see the [browser enhancements documentation].
|
||||
|
||||
## .stream
|
||||
|
||||
|
@ -128,7 +128,7 @@ You won't typically need to directly access the request's content, as you'll nor
|
|||
|
||||
If you do need to access the raw content directly, you should use the `.stream` property in preference to using `request.content`, as it provides transparent support for browser-based non-form content.
|
||||
|
||||
For more information see the [browser enhancements documentation].
|
||||
For more information see the [browser enhancements documentation].
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="response.py"></a>
|
||||
source: response.py
|
||||
|
||||
# Responses
|
||||
|
||||
|
@ -90,6 +90,6 @@ The `Response` class extends `SimpleTemplateResponse`, and all the usual attribu
|
|||
As with any other `TemplateResponse`, this method is called to render the serialized data of the response into the final response content. When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance.
|
||||
|
||||
You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle.
|
||||
|
||||
|
||||
[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/
|
||||
[statuscodes]: status-codes.md
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="reverse.py"></a>
|
||||
source: reverse.py
|
||||
|
||||
# Returning URLs
|
||||
|
||||
|
@ -30,7 +30,7 @@ You should **include the request as a keyword argument** to the function, for ex
|
|||
from rest_framework.reverse import reverse
|
||||
from rest_framework.views import APIView
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
class APIRootView(APIView):
|
||||
def get(self, request):
|
||||
year = now().year
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="routers.py"></a>
|
||||
source: routers.py
|
||||
|
||||
# Routers
|
||||
|
||||
|
@ -56,10 +56,10 @@ For example, given a method like this on the `UserViewSet` class:
|
|||
|
||||
from myapp.permissions import IsAdminOrIsSelf
|
||||
from rest_framework.decorators import detail_route
|
||||
|
||||
|
||||
class UserViewSet(ModelViewSet):
|
||||
...
|
||||
|
||||
|
||||
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
|
||||
def set_password(self, request, pk=None):
|
||||
...
|
||||
|
@ -228,7 +228,7 @@ For another example of setting the `.routes` attribute, see the source code for
|
|||
|
||||
## Advanced custom routers
|
||||
|
||||
If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.
|
||||
If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.
|
||||
|
||||
You may also want to override the `get_default_base_name(self, viewset)` method, or else always explicitly set the `base_name` argument when registering your viewsets with the router.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="serializers.py"></a>
|
||||
source: serializers.py
|
||||
|
||||
# Serializers
|
||||
|
||||
|
@ -21,7 +21,7 @@ Let's start by creating a simple object we can use for example purposes:
|
|||
self.email = email
|
||||
self.content = content
|
||||
self.created = created or datetime.datetime.now()
|
||||
|
||||
|
||||
comment = Comment(email='leila@example.com', content='foo bar')
|
||||
|
||||
We'll declare a serializer that we can use to serialize and deserialize `Comment` objects.
|
||||
|
@ -45,7 +45,7 @@ Declaring a serializer looks very similar to declaring a form:
|
|||
instance.content = attrs.get('content', instance.content)
|
||||
instance.created = attrs.get('created', instance.created)
|
||||
return instance
|
||||
return Comment(**attrs)
|
||||
return Comment(**attrs)
|
||||
|
||||
The first part of serializer class defines the fields that get serialized/deserialized. The `restore_object` method defines how fully fledged instances get created when deserializing data.
|
||||
|
||||
|
@ -83,8 +83,8 @@ If you need to customize the serialized value of a particular field, you can do
|
|||
These methods are essentially the reverse of `validate_<fieldname>` (see *Validation* below.)
|
||||
|
||||
## Deserializing objects
|
||||
|
||||
Deserialization is similar. First we parse a stream into Python native datatypes...
|
||||
|
||||
Deserialization is similar. First we parse a stream into Python native datatypes...
|
||||
|
||||
from StringIO import StringIO
|
||||
from rest_framework.parsers import JSONParser
|
||||
|
@ -174,7 +174,7 @@ To save the deserialized objects created by a serializer, call the `.save()` met
|
|||
|
||||
The default behavior of the method is to simply call `.save()` on the deserialized object instance. You can override the default save behaviour by overriding the `.save_object(obj)` method on the serializer class.
|
||||
|
||||
The generic views provided by REST framework call the `.save()` method when updating or creating entities.
|
||||
The generic views provided by REST framework call the `.save()` method when updating or creating entities.
|
||||
|
||||
## Dealing with nested objects
|
||||
|
||||
|
@ -288,12 +288,12 @@ By default the serializer class will use the `id` key on the incoming data to de
|
|||
slug = serializers.CharField(max_length=100)
|
||||
created = serializers.DateTimeField()
|
||||
... # Various other fields
|
||||
|
||||
|
||||
def get_identity(self, data):
|
||||
"""
|
||||
This hook is required for bulk update.
|
||||
We need to override the default, to use the slug as the identity.
|
||||
|
||||
|
||||
Note that the data has not yet been validated at this point,
|
||||
so we need to deal gracefully with incorrect datatypes.
|
||||
"""
|
||||
|
@ -361,7 +361,7 @@ The `depth` option should be set to an integer value that indicates the depth of
|
|||
|
||||
If you want to customize the way the serialization is done (e.g. using `allow_add_remove`) you'll need to define the field yourself.
|
||||
|
||||
## Specifying which fields should be read-only
|
||||
## Specifying which fields should be read-only
|
||||
|
||||
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the `read_only_fields` Meta option, like so:
|
||||
|
||||
|
@ -371,9 +371,9 @@ You may wish to specify multiple fields as read-only. Instead of adding each fi
|
|||
fields = ('id', 'account_name', 'users', 'created')
|
||||
read_only_fields = ('account_name',)
|
||||
|
||||
Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.
|
||||
Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.
|
||||
|
||||
## Specifying which fields should be write-only
|
||||
## Specifying which fields should be write-only
|
||||
|
||||
You may wish to specify multiple fields as write-only. Instead of adding each field explicitly with the `write_only=True` attribute, you may use the `write_only_fields` Meta option, like so:
|
||||
|
||||
|
@ -387,12 +387,12 @@ You may wish to specify multiple fields as write-only. Instead of adding each f
|
|||
"""
|
||||
Instantiate a new User instance.
|
||||
"""
|
||||
assert instance is None, 'Cannot update users with CreateUserSerializer'
|
||||
assert instance is None, 'Cannot update users with CreateUserSerializer'
|
||||
user = User(email=attrs['email'], username=attrs['username'])
|
||||
user.set_password(attrs['password'])
|
||||
return user
|
||||
|
||||
## Specifying fields explicitly
|
||||
|
||||
## Specifying fields explicitly
|
||||
|
||||
You can add extra fields to a `ModelSerializer` or override the default fields by declaring fields on the class, just as you would for a `Serializer` class.
|
||||
|
||||
|
@ -524,10 +524,10 @@ For example, if you wanted to be able to set which fields should be used by a se
|
|||
def __init__(self, *args, **kwargs):
|
||||
# Don't pass the 'fields' arg up to the superclass
|
||||
fields = kwargs.pop('fields', None)
|
||||
|
||||
|
||||
# Instantiate the superclass normally
|
||||
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
if fields:
|
||||
# Drop any fields that are not specified in the `fields` argument.
|
||||
allowed = set(fields)
|
||||
|
@ -550,7 +550,7 @@ This would then allow you to do the following:
|
|||
|
||||
## Customising the default fields
|
||||
|
||||
The `field_mapping` attribute is a dictionary that maps model classes to serializer classes. Overriding the attribute will let you set a different set of default serializer classes.
|
||||
The `field_mapping` attribute is a dictionary that maps model classes to serializer classes. Overriding the attribute will let you set a different set of default serializer classes.
|
||||
|
||||
For more advanced customization than simply changing the default serializer class you can override various `get_<field_type>_field` methods. Doing so will allow you to customize the arguments that each serializer field is initialized with. Each of these methods may either return a field or serializer instance, or `None`.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="settings.py"></a>
|
||||
source: settings.py
|
||||
|
||||
# Settings
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="status.py"></a>
|
||||
source: status.py
|
||||
|
||||
# Status Codes
|
||||
|
||||
|
@ -27,7 +27,7 @@ The module also includes a set of helper functions for testing if a status code
|
|||
url = reverse('index')
|
||||
response = self.client.get(url)
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
|
||||
|
||||
For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616]
|
||||
and [RFC 6585][rfc6585].
|
||||
|
@ -51,7 +51,7 @@ This class of status code indicates that the client's request was successfully r
|
|||
HTTP_205_RESET_CONTENT
|
||||
HTTP_206_PARTIAL_CONTENT
|
||||
|
||||
## Redirection - 3xx
|
||||
## Redirection - 3xx
|
||||
|
||||
This class of status code indicates that further action needs to be taken by the user agent in order to fulfill the request.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="test.py"></a>
|
||||
source: test.py
|
||||
|
||||
# Testing
|
||||
|
||||
|
@ -170,7 +170,7 @@ This can be a useful shortcut if you're testing the API but don't want to have t
|
|||
|
||||
To unauthenticate subsequent requests, call `force_authenticate` setting the user and/or token to `None`.
|
||||
|
||||
client.force_authenticate(user=None)
|
||||
client.force_authenticate(user=None)
|
||||
|
||||
## CSRF validation
|
||||
|
||||
|
@ -197,7 +197,7 @@ You can use any of REST framework's test case classes as you would for the regul
|
|||
|
||||
from django.core.urlresolvers import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
class AccountTests(APITestCase):
|
||||
def test_create_account(self):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="throttling.py"></a>
|
||||
source: throttling.py
|
||||
|
||||
# Throttling
|
||||
|
||||
|
@ -83,7 +83,7 @@ The throttle classes provided by REST framework use Django's cache backend. You
|
|||
If you need to use a cache other than `'default'`, you can do so by creating a custom throttle class and setting the `cache` attribute. For example:
|
||||
|
||||
class CustomAnonRateThrottle(AnonRateThrottle):
|
||||
cache = get_cache('alternate')
|
||||
cache = get_cache('alternate')
|
||||
|
||||
You'll need to remember to also set your custom throttle class in the `'DEFAULT_THROTTLE_CLASSES'` settings key, or using the `throttle_classes` view attribute.
|
||||
|
||||
|
@ -147,15 +147,15 @@ For example, given the following views...
|
|||
class ContactListView(APIView):
|
||||
throttle_scope = 'contacts'
|
||||
...
|
||||
|
||||
|
||||
class ContactDetailView(ApiView):
|
||||
throttle_scope = 'contacts'
|
||||
...
|
||||
|
||||
class UploadView(APIView):
|
||||
class UploadView(APIView):
|
||||
throttle_scope = 'uploads'
|
||||
...
|
||||
|
||||
|
||||
...and the following settings.
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<a class="github" href="decorators.py"></a> <a class="github" href="views.py"></a>
|
||||
source: decorators.py
|
||||
views.py
|
||||
|
||||
# Class Based Views
|
||||
|
||||
|
@ -26,7 +27,7 @@ For example:
|
|||
class ListUsers(APIView):
|
||||
"""
|
||||
View to list all users in the system.
|
||||
|
||||
|
||||
* Requires token authentication.
|
||||
* Only admin users are able to access this view.
|
||||
"""
|
||||
|
@ -54,7 +55,7 @@ The following attributes control the pluggable aspects of API views.
|
|||
|
||||
### .permission_classes
|
||||
|
||||
### .content_negotiation_class
|
||||
### .content_negotiation_class
|
||||
|
||||
## API policy instantiation methods
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="github" href="viewsets.py"></a>
|
||||
source: viewsets.py
|
||||
|
||||
# ViewSets
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ body{
|
|||
}
|
||||
|
||||
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand{
|
||||
color: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-list > .active > a, .navbar .navbar-inner .nav li a:hover {
|
||||
|
@ -190,8 +190,20 @@ body{
|
|||
}
|
||||
|
||||
.navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li{
|
||||
color: #A30000;
|
||||
color: #A30000;
|
||||
}
|
||||
|
||||
.dropdown-menu .active > a,
|
||||
.dropdown-menu .active > a:hover {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.navbar-inverse .nav .dropdown .active > a,
|
||||
.navbar-inverse .nav .dropdown .active > a:hover,
|
||||
.navbar-inverse .nav .dropdown .active > a:focus {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.navbar .navbar-inner .dropdown-menu li a:hover{
|
||||
background: #eeeeee;
|
||||
color: #c20000;
|
||||
|
|
|
@ -26,9 +26,6 @@
|
|||
<img alt="Django REST Framework" title="Logo by Jake 'Sid' Smith" src="img/logo.png" width="600px" style="display: block; margin: 0 auto 0 auto">
|
||||
</p>
|
||||
|
||||
<!--
|
||||
# Django REST framework
|
||||
-->
|
||||
|
||||
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.
|
||||
|
||||
|
@ -175,7 +172,7 @@ The API guide is your complete reference manual to all the functionality provide
|
|||
* [Serializers][serializers]
|
||||
* [Serializer fields][fields]
|
||||
* [Serializer relations][relations]
|
||||
* [Validators][validators]
|
||||
<!--* [Validators][validators]-->
|
||||
* [Authentication][authentication]
|
||||
* [Permissions][permissions]
|
||||
* [Throttling][throttling]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
markdown>=2.1.0
|
|
@ -1,239 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<title>{{ title }}</title>
|
||||
<link href="{{ base_url }}/img/favicon.ico" rel="icon" type="image/x-icon">
|
||||
<link rel="canonical" href="{{ canonical_url }}"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="author" content="Tom Christie">
|
||||
|
||||
<!-- 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-responsive.css" rel="stylesheet">
|
||||
<link href="{{ base_url }}/css/default.css" rel="stylesheet">
|
||||
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
span.fusion-wrap a {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
a.fusion-poweredby {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
div.promo {display: none;}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="prettyPrint()" class="{{ page_id }}-page">
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
|
||||
<a class="repo-link btn btn-inverse btn-small {{ next_url_disabled }}" href="{{ next_url }}">Next <i class="icon-arrow-right icon-white"></i></a>
|
||||
<a class="repo-link btn btn-inverse btn-small {{ prev_url_disabled }}" href="{{ prev_url }}"><i class="icon-arrow-left icon-white"></i> Previous</a>
|
||||
<a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
|
||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</a>
|
||||
<a class="brand" href="{{ base_url }}{{ index }}">Django REST framework</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a href="{{ base_url }}{{ index }}">Home</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ base_url }}/tutorial/quickstart{{ suffix }}">Quickstart</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/1-serialization{{ suffix }}">1 - Serialization</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/2-requests-and-responses{{ suffix }}">2 - Requests and responses</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/3-class-based-views{{ suffix }}">3 - Class based views</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/4-authentication-and-permissions{{ suffix }}">4 - Authentication and permissions</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/5-relationships-and-hyperlinked-apis{{ suffix }}">5 - Relationships and hyperlinked APIs</a></li>
|
||||
<li><a href="{{ base_url }}/tutorial/6-viewsets-and-routers{{ suffix }}">6 - Viewsets and routers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ base_url }}/api-guide/requests{{ suffix }}">Requests</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/responses{{ suffix }}">Responses</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/views{{ suffix }}">Views</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/generic-views{{ suffix }}">Generic views</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/viewsets{{ suffix }}">Viewsets</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/routers{{ suffix }}">Routers</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/parsers{{ suffix }}">Parsers</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/renderers{{ suffix }}">Renderers</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/serializers{{ suffix }}">Serializers</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/fields{{ suffix }}">Serializer fields</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/relations{{ suffix }}">Serializer relations</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/validators{{ suffix }}">Validators</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/authentication{{ suffix }}">Authentication</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/permissions{{ suffix }}">Permissions</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/throttling{{ suffix }}">Throttling</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/filtering{{ suffix }}">Filtering</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/pagination{{ suffix }}">Pagination</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/content-negotiation{{ suffix }}">Content negotiation</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/format-suffixes{{ suffix }}">Format suffixes</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/reverse{{ suffix }}">Returning URLs</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/exceptions{{ suffix }}">Exceptions</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/status-codes{{ suffix }}">Status codes</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/testing{{ suffix }}">Testing</a></li>
|
||||
<li><a href="{{ base_url }}/api-guide/settings{{ suffix }}">Settings</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ base_url }}/topics/documenting-your-api{{ suffix }}">Documenting your API</a></li>
|
||||
<li><a href="{{ base_url }}/topics/ajax-csrf-cors{{ suffix }}">AJAX, CSRF & CORS</a></li>
|
||||
<li><a href="{{ base_url }}/topics/browser-enhancements{{ suffix }}">Browser enhancements</a></li>
|
||||
<li><a href="{{ base_url }}/topics/browsable-api{{ suffix }}">The Browsable API</a></li>
|
||||
<li><a href="{{ base_url }}/topics/rest-hypermedia-hateoas{{ suffix }}">REST, Hypermedia & HATEOAS</a></li>
|
||||
<li><a href="{{ base_url }}/topics/third-party-resources{{ suffix }}">Third Party Resources</a></li>
|
||||
<li><a href="{{ base_url }}/topics/contributing{{ suffix }}">Contributing to REST framework</a></li>
|
||||
<li><a href="{{ base_url }}/topics/rest-framework-2-announcement{{ suffix }}">2.0 Announcement</a></li>
|
||||
<li><a href="{{ base_url }}/topics/2.2-announcement{{ suffix }}">2.2 Announcement</a></li>
|
||||
<li><a href="{{ base_url }}/topics/2.3-announcement{{ suffix }}">2.3 Announcement</a></li>
|
||||
<li><a href="{{ base_url }}/topics/2.4-announcement{{ suffix }}">2.4 Announcement</a></li>
|
||||
<li><a href="{{ base_url }}/topics/kickstarter-announcement{{ suffix }}">Kickstarter Announcement</a></li>
|
||||
<li><a href="{{ base_url }}/topics/release-notes{{ suffix }}">Release Notes</a></li>
|
||||
<li><a href="{{ base_url }}/topics/credits{{ suffix }}">Credits</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav pull-right">
|
||||
<!-- TODO
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Version: 2.0.0 <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Trunk</a></li>
|
||||
<li><a href="#">2.0.0</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Search Modal -->
|
||||
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Documentation search</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Custom google search -->
|
||||
<script>
|
||||
(function() {
|
||||
var cx = '015016005043623903336:rxraeohqk6w';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
</script>
|
||||
<gcse:search></gcse:search>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span3">
|
||||
<!-- TODO
|
||||
<p style="margin-top: -12px">
|
||||
<a class="btn btn-mini btn-primary" style="width: 60px">« previous</a>
|
||||
<a class="btn btn-mini btn-primary" style="float: right; margin-right: 8px; width: 60px;">next »</a>
|
||||
</p>
|
||||
-->
|
||||
<div id="table-of-contents">
|
||||
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
|
||||
{{ toc }}
|
||||
<div class="promo">
|
||||
{{ ad_block }}
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="span9">
|
||||
{{ content }}
|
||||
</div><!--/span-->
|
||||
</div><!--/row-->
|
||||
</div><!--/.fluid-container-->
|
||||
</div><!--/.body content-->
|
||||
|
||||
<div id="push"></div>
|
||||
</div><!--/.wrapper -->
|
||||
|
||||
<footer class="span12">
|
||||
<p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- Le javascript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
|
||||
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
|
||||
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
|
||||
|
||||
<script>
|
||||
//$('.side-nav').scrollspy()
|
||||
var shiftWindow = function() { scrollBy(0, -50) };
|
||||
if (location.hash) shiftWindow();
|
||||
window.addEventListener("hashchange", shiftWindow);
|
||||
|
||||
$('.dropdown-menu').on('click touchstart', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Dynamically force sidenav to no higher than browser window
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
|
||||
$(function(){
|
||||
$(window).resize(function(){
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
|
@ -42,7 +42,7 @@ The 2.2 release makes a few changes to the API, in order to make it more consist
|
|||
|
||||
The `ManyRelatedField()` style is being deprecated in favor of a new `RelatedField(many=True)` syntax.
|
||||
|
||||
For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:
|
||||
For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:
|
||||
|
||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
questions = serializers.PrimaryKeyRelatedField(many=True)
|
||||
|
@ -58,10 +58,10 @@ The change also applies to serializers. If you have a nested serializer, you sh
|
|||
class Meta:
|
||||
model = Track
|
||||
fields = ('name', 'duration')
|
||||
|
||||
|
||||
class AlbumSerializer(serializer.ModelSerializer):
|
||||
tracks = TrackSerializer(many=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ('album_name', 'artist', 'tracks')
|
||||
|
@ -87,7 +87,7 @@ For example, is a user account has an optional foreign key to a company, that yo
|
|||
|
||||
This is in line both with the rest of the serializer fields API, and with Django's `Form` and `ModelForm` API.
|
||||
|
||||
Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.
|
||||
Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.
|
||||
|
||||
The `null=True` argument will continue to function, and will imply `required=False`, but will raise a `PendingDeprecationWarning`.
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ As an example of just how simple REST framework APIs can now be, here's an API w
|
|||
class GroupViewSet(viewsets.ModelViewSet):
|
||||
model = Group
|
||||
|
||||
|
||||
|
||||
# Routers provide an easy way of automatically determining the URL conf
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'users', UserViewSet)
|
||||
|
@ -197,13 +197,13 @@ Usage of the old-style attributes continues to be supported, but will raise a `P
|
|||
|
||||
For most cases APIs using model fields will behave as previously, however if you are using a custom renderer, not provided by REST framework, then you may now need to add support for rendering `Decimal` instances to your renderer implementation.
|
||||
|
||||
## ModelSerializers and reverse relationships
|
||||
## ModelSerializers and reverse relationships
|
||||
|
||||
The support for adding reverse relationships to the `fields` option on a `ModelSerializer` class means that the `get_related_field` and `get_nested_field` method signatures have now changed.
|
||||
|
||||
In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now `(self, model_field, related_model, to_many)`. For reverse relationships `model_field` will be `None`.
|
||||
|
||||
The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.
|
||||
The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.
|
||||
|
||||
## View names and descriptions
|
||||
|
||||
|
@ -211,7 +211,7 @@ The mechanics of how the names and descriptions used in the browseable API are g
|
|||
|
||||
If you've been customizing this behavior, for example perhaps to use `rst` markup for the browseable API, then you'll need to take a look at the implementation to see what updates you need to make.
|
||||
|
||||
Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.
|
||||
Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ We strongly recommend that you use the namespaced import style of `import serial
|
|||
The `validate_<field_name>` method hooks that can be attached to serializer classes change their signature slightly and return type. Previously these would take a dictionary of all incoming data, and a key representing the field name, and would return a dictionary including the validated data for that field:
|
||||
|
||||
def validate_score(self, attrs, source):
|
||||
if attrs[score] % 10 != 0:
|
||||
if attrs['score'] % 10 != 0:
|
||||
raise serializers.ValidationError('This field should be a multiple of ten.')
|
||||
return attrs
|
||||
|
||||
|
|
|
@ -135,15 +135,15 @@ There are many great Markdown editors that make working with the documentation r
|
|||
|
||||
## Building the documentation
|
||||
|
||||
To build the documentation, simply run the `mkdocs.py` script.
|
||||
To build the documentation, install MkDocs with `pip install mkdocs` and then run the following command.
|
||||
|
||||
./mkdocs.py
|
||||
mkdocs build
|
||||
|
||||
This will build the html output into the `html` directory.
|
||||
This will build the documentation into the `site` directory.
|
||||
|
||||
You can build the documentation and open a preview in a browser window by using the `-p` flag.
|
||||
You can build the documentation and open a preview in a browser window by using the `serve` command.
|
||||
|
||||
./mkdocs.py -p
|
||||
mkdocs serve
|
||||
|
||||
## Language style
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ The title that is used in the browsable API is generated from the view class nam
|
|||
|
||||
For example, the view `UserListView`, will be named `User List` when presented in the browsable API.
|
||||
|
||||
When working with viewsets, an appropriate suffix is appended to each generated view. For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.
|
||||
When working with viewsets, an appropriate suffix is appended to each generated view. For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.
|
||||
|
||||
#### Setting the description
|
||||
|
||||
|
@ -65,9 +65,9 @@ If the python `markdown` library is installed, then [markdown syntax][markdown]
|
|||
class AccountListView(views.APIView):
|
||||
"""
|
||||
Returns a list of all **active** accounts in the system.
|
||||
|
||||
|
||||
For more details on how accounts are activated please [see here][ref].
|
||||
|
||||
|
||||
[ref]: http://example.com/activating-accounts
|
||||
"""
|
||||
|
||||
|
@ -84,7 +84,7 @@ You can modify the response behavior to `OPTIONS` requests by overriding the `me
|
|||
def metadata(self, request):
|
||||
"""
|
||||
Don't include the view description in OPTIONS responses.
|
||||
"""
|
||||
"""
|
||||
data = super(ExampleView, self).metadata(request)
|
||||
data.pop('description')
|
||||
return data
|
||||
|
|
|
@ -160,4 +160,4 @@ The following individuals made a significant financial contribution to the devel
|
|||
|
||||
### Supporters
|
||||
|
||||
There were also almost 300 further individuals choosing to help fund the project at other levels or choosing to give anonymously. Again, thank you, thank you, thank you!
|
||||
There were also almost 300 further individuals choosing to help fund the project at other levels or choosing to give anonymously. Again, thank you, thank you, thank you!
|
||||
|
|
|
@ -63,7 +63,7 @@ You can determine your currently installed version using `pip freeze`:
|
|||
* Bugfix: Fix migration in `authtoken` application.
|
||||
* Bugfix: Allow selection of integer keys in nested choices.
|
||||
* Bugfix: Return `None` instead of `'None'` in `CharField` with `allow_none=True`.
|
||||
* Bugfix: Ensure custom model fields map to equivelent serializer fields more reliably.
|
||||
* Bugfix: Ensure custom model fields map to equivelent serializer fields more reliably.
|
||||
* Bugfix: `DjangoFilterBackend` no longer quietly changes queryset ordering.
|
||||
|
||||
### 2.4.2
|
||||
|
|
|
@ -8,7 +8,7 @@ What it is, and why you should care.
|
|||
|
||||
---
|
||||
|
||||
**Announcement:** REST framework 2 released - Tue 30th Oct 2012
|
||||
**Announcement:** REST framework 2 released - Tue 30th Oct 2012
|
||||
|
||||
---
|
||||
|
||||
|
@ -37,7 +37,7 @@ REST framework 2 includes a totally re-worked serialization engine, that was ini
|
|||
* A declarative serialization API, that mirrors Django's `Forms`/`ModelForms` API.
|
||||
* Structural concerns are decoupled from encoding concerns.
|
||||
* Able to support rendering and parsing to many formats, including both machine-readable representations and HTML forms.
|
||||
* Validation that can be mapped to obvious and comprehensive error responses.
|
||||
* Validation that can be mapped to obvious and comprehensive error responses.
|
||||
* Serializers that support both nested, flat, and partially-nested representations.
|
||||
* Relationships that can be expressed as primary keys, hyperlinks, slug fields, and other custom representations.
|
||||
|
||||
|
|
|
@ -1,92 +1,322 @@
|
|||
# Third Party Resources
|
||||
|
||||
## About Third Party Packages
|
||||
|
||||
Third Party Packages allow developers to share code that extends the functionality of Django REST framework, in order to support additional use-cases.
|
||||
|
||||
We **support**, **encourage** and **strongly favour** the creation of Third Party Packages to encapsulate new behaviour rather than adding additional functionality directly to Django REST Framework.
|
||||
|
||||
We aim to make creating Third Party Packages as easy as possible, whilst keeping the **simplicity** of the core API and ensuring that **maintenance** of the main project remains under control. If a Third Party Package proves popular it is relatively easy to move it into the main project; removing features is much more problematic.
|
||||
|
||||
If you have an idea for a new feature please consider how it may be packaged as a Third Party Package. We're always happy to dicuss ideas on the [Mailing List][discussion-group].
|
||||
|
||||
## How to create a Third Party Package
|
||||
|
||||
### Creating your package
|
||||
|
||||
You can use [this cookiecutter template][cookiecutter] for creating reusable Django REST Framework packages quickly. Cookiecutter creates projects from project templates. While optional, this cookiecutter template includes best practices from Django REST framework and other packages, as well as a Travis CI configuration, Tox configuration, and a sane setup.py for easy PyPI registration/distribution.
|
||||
|
||||
Note: Let us know if you have an alternate cookiecuter package so we can also link to it.
|
||||
|
||||
#### Running the initial cookiecutter command
|
||||
|
||||
To run the initial cookiecutter command, you'll first need to install the Python `cookiecutter` package.
|
||||
|
||||
$ pip install cookiecutter
|
||||
|
||||
Once `cookiecutter` is installed just run the following to create a new project.
|
||||
|
||||
$ cookiecutter gh:jpadilla/cookiecutter-django-rest-framework
|
||||
|
||||
You'll be prompted for some questions, answer them, then it'll create your Python package in the current working directory based on those values.
|
||||
|
||||
full_name (default is "Your full name here")? Johnny Appleseed
|
||||
email (default is "you@example.com")? jappleseed@example.com
|
||||
github_username (default is "yourname")? jappleseed
|
||||
pypi_project_name (default is "dj-package")? djangorestframework-custom-auth
|
||||
repo_name (default is "dj-package")? django-rest-framework-custom-auth
|
||||
app_name (default is "djpackage")? custom_auth
|
||||
project_short_description (default is "Your project description goes here")?
|
||||
year (default is "2014")?
|
||||
version (default is "0.1.0")?
|
||||
|
||||
#### Getting it onto GitHub
|
||||
|
||||
To put your project up on GitHub, you'll need a repository for it to live in. You can create a new repository [here][new-repo]. If you need help, check out the [Create A Repo][create-a-repo] article on GitHub.
|
||||
|
||||
|
||||
#### Adding to Travis CI
|
||||
|
||||
We recommend using [Travis CI][travis-ci], a hosted continuous integration service which integrates well with GitHub and is free for public repositories.
|
||||
|
||||
To get started with Travis CI, [sign in][travis-ci] with your GitHub account. Once you're signed in, go to your [profile page][travis-profile] and enable the service hook for the repository you want.
|
||||
|
||||
If you use the cookiecutter template, your project will already contain a `.travis.yml` file which Travis CI will use to build your project and run tests. By default, builds are triggered everytime you push to your repository or create Pull Request.
|
||||
|
||||
#### Uploading to PyPI
|
||||
|
||||
Once you've got at least a prototype working and tests running, you should publish it on PyPI to allow others to install it via `pip`.
|
||||
|
||||
You must [register][pypi-register] an account before publishing to PyPI.
|
||||
|
||||
To register your package on PyPI run the following command.
|
||||
|
||||
$ python setup.py register
|
||||
|
||||
If this is the first time publishing to PyPI, you'll be prompted to login.
|
||||
|
||||
Note: Before publishing you'll need to make sure you have the latest pip that supports `wheel` as well as install the `wheel` package.
|
||||
|
||||
$ pip install --upgrade pip
|
||||
$ pip install wheel
|
||||
|
||||
After this, every time you want to release a new version on PyPI just run the following command.
|
||||
|
||||
$ python setup.py publish
|
||||
You probably want to also tag the version now:
|
||||
git tag -a {0} -m 'version 0.1.0'
|
||||
git push --tags
|
||||
|
||||
After releasing a new version to PyPI, it's always a good idea to tag the version and make available as a GitHub Release.
|
||||
|
||||
We recommend to follow [Semantic Versioning][semver] for your package's versions.
|
||||
|
||||
### Development
|
||||
|
||||
#### Version requirements
|
||||
|
||||
The cookiecutter template assumes a set of supported versions will be provided for Python and Django. Make sure you correctly update your requirements, docs, `tox.ini`, `.travis.yml`, and `setup.py` to match the set of versions you wish to support.
|
||||
|
||||
#### Tests
|
||||
|
||||
The cookiecutter template includes a `runtests.py` which uses the `pytest` package as a test runner.
|
||||
|
||||
Before running, you'll need to install a couple test requirements.
|
||||
|
||||
$ pip install -r requirements-test.txt
|
||||
|
||||
Once requirements installed, you can run `runtests.py`.
|
||||
|
||||
$ ./runtests.py
|
||||
|
||||
Run using a more concise output style.
|
||||
|
||||
$ ./runtests.py -q
|
||||
|
||||
Run the tests using a more concise output style, no coverage, no flake8.
|
||||
|
||||
$ ./runtests.py --fast
|
||||
|
||||
Don't run the flake8 code linting.
|
||||
|
||||
$ ./runtests.py --nolint
|
||||
|
||||
Only run the flake8 code linting, don't run the tests.
|
||||
|
||||
$ ./runtests.py --lintonly
|
||||
|
||||
Run the tests for a given test case.
|
||||
|
||||
$ ./runtests.py MyTestCase
|
||||
|
||||
Run the tests for a given test method.
|
||||
|
||||
$ ./runtests.py MyTestCase.test_this_method
|
||||
|
||||
Shorter form to run the tests for a given test method.
|
||||
|
||||
$ ./runtests.py test_this_method
|
||||
|
||||
To run your tests against multiple versions of Python as different versions of requirements such as Django we recommend using `tox`. [Tox][tox-docs] is a generic virtualenv management and test command line tool.
|
||||
|
||||
First, install `tox` globally.
|
||||
|
||||
$ pip install tox
|
||||
|
||||
To run `tox`, just simply run:
|
||||
|
||||
$ tox
|
||||
|
||||
To run a particular `tox` environment:
|
||||
|
||||
$ tox -e envlist
|
||||
|
||||
`envlist` is a comma-separated value to that specifies the environments to run tests against. To view a list of all possible test environments, run:
|
||||
|
||||
$ tox -l
|
||||
|
||||
#### Version compatibility
|
||||
|
||||
Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment. Any code that branches in this way should be isolated into a `compat.py` module, and should provide a single common interface that the rest of the codebase can use.
|
||||
|
||||
Check out Django REST framework's [compat.py][drf-compat] for an example.
|
||||
|
||||
### Once your package is available
|
||||
|
||||
Once your package is decently documented and available on PyPI, you might want share it with others that might find it useful.
|
||||
|
||||
#### Adding to the Django REST framework grid
|
||||
|
||||
We suggest adding your package to the [REST Framework][rest-framework-grid] grid on Django Packages.
|
||||
|
||||
#### Adding to the Django REST framework docs
|
||||
|
||||
Create a [Pull Request][drf-create-pr] or [Issue][drf-create-issue] on GitHub, and we'll add a link to it from the main REST framework documentation. You can add your package under **Third party packages** of the API Guide section that best applies, like [Authentication][authentication] or [Permissions][permissions]. You can also link your package under the [Third Party Resources][third-party-resources] section.
|
||||
|
||||
#### Announce on the discussion group.
|
||||
|
||||
You can also let others know about your package through the [discussion group][discussion-group].
|
||||
|
||||
## Existing Third Party Packages
|
||||
|
||||
Django REST Framework has a growing community of developers, packages, and resources.
|
||||
|
||||
Check out a grid detailing all the packages and ecosystem around Django REST Framework at [Django Packages](https://www.djangopackages.com/grids/g/django-rest-framework/).
|
||||
Check out a grid detailing all the packages and ecosystem around Django REST Framework at [Django Packages][rest-framework-grid].
|
||||
|
||||
To submit new content, [open an issue](https://github.com/tomchristie/django-rest-framework/issues/new) or [create a pull request](https://github.com/tomchristie/django-rest-framework/).
|
||||
|
||||
## Libraries and Extensions
|
||||
To submit new content, [open an issue][drf-create-issue] or [create a pull request][drf-create-pr].
|
||||
|
||||
### Authentication
|
||||
|
||||
* [djangorestframework-digestauth](https://github.com/juanriaza/django-rest-framework-digestauth) - Provides Digest Access Authentication support.
|
||||
* [django-oauth-toolkit](https://github.com/evonove/django-oauth-toolkit) - Provides OAuth 2.0 support.
|
||||
* [doac](https://github.com/Rediker-Software/doac) - Provides OAuth 2.0 support.
|
||||
* [djangorestframework-jwt](https://github.com/GetBlimp/django-rest-framework-jwt) - Provides JSON Web Token Authentication support.
|
||||
* [hawkrest](https://github.com/kumar303/hawkrest) - Provides Hawk HTTP Authorization.
|
||||
* [djangorestframework-httpsignature](https://github.com/etoccalino/django-rest-framework-httpsignature) - Provides an easy to use HTTP Signature Authentication mechanism.
|
||||
* [djoser](https://github.com/sunscrapers/djoser) - Provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation.
|
||||
* [djangorestframework-digestauth][djangorestframework-digestauth] - Provides Digest Access Authentication support.
|
||||
* [django-oauth-toolkit][django-oauth-toolkit] - Provides OAuth 2.0 support.
|
||||
* [doac][doac] - Provides OAuth 2.0 support.
|
||||
* [djangorestframework-jwt][djangorestframework-jwt] - Provides JSON Web Token Authentication support.
|
||||
* [hawkrest][hawkrest] - Provides Hawk HTTP Authorization.
|
||||
* [djangorestframework-httpsignature][djangorestframework-httpsignature] - Provides an easy to use HTTP Signature Authentication mechanism.
|
||||
* [djoser][djoser] - Provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation.
|
||||
|
||||
### Permissions
|
||||
|
||||
* [drf-any-permissions](https://github.com/kevin-brown/drf-any-permissions) - Provides alternative permission handling.
|
||||
* [djangorestframework-composed-permissions](https://github.com/niwibe/djangorestframework-composed-permissions) - Provides a simple way to define complex permissions.
|
||||
* [rest_condition](https://github.com/caxap/rest_condition) - Another extension for building complex permissions in a simple and convenient way.
|
||||
* [drf-any-permissions][drf-any-permissions] - Provides alternative permission handling.
|
||||
* [djangorestframework-composed-permissions][djangorestframework-composed-permissions] - Provides a simple way to define complex permissions.
|
||||
* [rest_condition][rest-condition] - Another extension for building complex permissions in a simple and convenient way.
|
||||
|
||||
### Serializers
|
||||
|
||||
* [django-rest-framework-mongoengine](https://github.com/umutbozkurt/django-rest-framework-mongoengine) - Serializer class that supports using MongoDB as the storage layer for Django REST framework.
|
||||
* [djangorestframework-gis](https://github.com/djangonauts/django-rest-framework-gis) - Geographic add-ons
|
||||
* [djangorestframework-hstore](https://github.com/djangonauts/django-rest-framework-hstore) - Serializer class to support django-hstore DictionaryField model field and its schema-mode feature.
|
||||
* [django-rest-framework-mongoengine][django-rest-framework-mongoengine] - Serializer class that supports using MongoDB as the storage layer for Django REST framework.
|
||||
* [djangorestframework-gis][djangorestframework-gis] - Geographic add-ons
|
||||
* [djangorestframework-hstore][djangorestframework-hstore] - Serializer class to support django-hstore DictionaryField model field and its schema-mode feature.
|
||||
|
||||
### Serializer fields
|
||||
|
||||
* [drf-compound-fields](https://github.com/estebistec/drf-compound-fields) - Provides "compound" serializer fields, such as lists of simple values.
|
||||
* [django-extra-fields](https://github.com/Hipo/drf-extra-fields) - Provides extra serializer fields.
|
||||
* [drf-compound-fields][drf-compound-fields] - Provides "compound" serializer fields, such as lists of simple values.
|
||||
* [django-extra-fields][django-extra-fields] - Provides extra serializer fields.
|
||||
|
||||
### Views
|
||||
|
||||
* [djangorestframework-bulk](https://github.com/miki725/django-rest-framework-bulk) - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
|
||||
* [djangorestframework-bulk][djangorestframework-bulk] - Implements generic view mixins as well as some common concrete generic views to allow to apply bulk operations via API requests.
|
||||
|
||||
### Routers
|
||||
|
||||
* [drf-nested-routers](https://github.com/alanjds/drf-nested-routers) - Provides routers and relationship fields for working with nested resources.
|
||||
* [wq.db.rest](http://wq.io/docs/about-rest) - Provides an admin-style model registration API with reasonable default URLs and viewsets.
|
||||
* [drf-nested-routers][drf-nested-routers] - Provides routers and relationship fields for working with nested resources.
|
||||
* [wq.db.rest][wq.db.rest] - Provides an admin-style model registration API with reasonable default URLs and viewsets.
|
||||
|
||||
### Parsers
|
||||
|
||||
* [djangorestframework-msgpack](https://github.com/juanriaza/django-rest-framework-msgpack) - Provides MessagePack renderer and parser support.
|
||||
* [djangorestframework-camel-case](https://github.com/vbabiy/djangorestframework-camel-case) - Provides camel case JSON renderers and parsers.
|
||||
* [djangorestframework-msgpack][djangorestframework-msgpack] - Provides MessagePack renderer and parser support.
|
||||
* [djangorestframework-camel-case][djangorestframework-camel-case] - Provides camel case JSON renderers and parsers.
|
||||
|
||||
### Renderers
|
||||
|
||||
* [djangorestframework-csv](https://github.com/mjumbewu/django-rest-framework-csv) - Provides CSV renderer support.
|
||||
* [drf_ujson](https://github.com/gizmag/drf-ujson-renderer) - Implements JSON rendering using the UJSON package.
|
||||
* [Django REST Pandas](https://github.com/wq/django-rest-pandas) - Pandas DataFrame-powered renderers including Excel, CSV, and SVG formats.
|
||||
* [djangorestframework-csv][djangorestframework-csv] - Provides CSV renderer support.
|
||||
* [drf_ujson][drf_ujson] - Implements JSON rendering using the UJSON package.
|
||||
* [rest-pandas][rest-pandas] - Pandas DataFrame-powered renderers including Excel, CSV, and SVG formats.
|
||||
|
||||
### Filtering
|
||||
|
||||
* [djangorestframework-chain](https://github.com/philipn/django-rest-framework-chain) - Allows arbitrary chaining of both relations and lookup filters.
|
||||
* [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters.
|
||||
|
||||
### Misc
|
||||
|
||||
* [djangorestrelationalhyperlink](https://github.com/fredkingham/django_rest_model_hyperlink_serializers_project) - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
|
||||
* [django-rest-swagger](https://github.com/marcgibbons/django-rest-swagger) - An API documentation generator for Swagger UI.
|
||||
* [django-rest-framework-proxy ](https://github.com/eofs/django-rest-framework-proxy) - Proxy to redirect incoming request to another API server.
|
||||
* [gaiarestframework](https://github.com/AppsFuel/gaiarestframework) - Utils for django-rest-framewok
|
||||
* [drf-extensions](https://github.com/chibisov/drf-extensions) - A collection of custom extensions
|
||||
* [ember-data-django-rest-adapter](https://github.com/toranb/ember-data-django-rest-adapter) - An ember-data adapter
|
||||
* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
|
||||
* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI.
|
||||
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
|
||||
* [gaiarestframework][gaiarestframework] - Utils for django-rest-framewok
|
||||
* [drf-extensions][drf-extensions] - A collection of custom extensions
|
||||
* [ember-data-django-rest-adapter][ember-data-django-rest-adapter] - An ember-data adapter
|
||||
|
||||
## Tutorials
|
||||
## Other Resources
|
||||
|
||||
* [Beginner's Guide to the Django Rest Framework](http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786)
|
||||
* [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
|
||||
* [End to end web app with Django-Rest-Framework & AngularJS](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework)
|
||||
* [Start Your API - django-rest-framework part 1](https://godjango.com/41-start-your-api-django-rest-framework-part-1/)
|
||||
* [Permissions & Authentication - django-rest-framework part 2](https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/)
|
||||
* [ViewSets and Routers - django-rest-framework part 3](https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/)
|
||||
* [Django Rest Framework User Endpoint](http://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/)
|
||||
* [Check credentials using Django Rest Framework](http://richardtier.com/2014/03/06/110/)
|
||||
### Tutorials
|
||||
|
||||
## Videos
|
||||
* [Beginner's Guide to the Django Rest Framework][beginners-guide-to-the-django-rest-framework]
|
||||
* [Getting Started with Django Rest Framework and AngularJS][getting-started-with-django-rest-framework-and-angularjs]
|
||||
* [End to end web app with Django-Rest-Framework & AngularJS][end-to-end-web-app-with-django-rest-framework-angularjs]
|
||||
* [Start Your API - django-rest-framework part 1][start-your-api-django-rest-framework-part-1]
|
||||
* [Permissions & Authentication - django-rest-framework part 2][permissions-authentication-django-rest-framework-part-2]
|
||||
* [ViewSets and Routers - django-rest-framework part 3][viewsets-and-routers-django-rest-framework-part-3]
|
||||
* [Django Rest Framework User Endpoint][django-rest-framework-user-endpoint]
|
||||
* [Check credentials using Django Rest Framework][check-credentials-using-django-rest-framework]
|
||||
|
||||
* [Ember and Django Part 1 (Video)](http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1)
|
||||
* [Django Rest Framework Part 1 (Video)](http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1)
|
||||
* [Pyowa July 2013 - Django Rest Framework (Video)](http://www.youtube.com/watch?v=E1ZrehVxpBo)
|
||||
* [django-rest-framework and angularjs (Video)](http://www.youtube.com/watch?v=Q8FRBGTJ020)
|
||||
### Videos
|
||||
|
||||
## Articles
|
||||
* [Ember and Django Part 1 (Video)][ember-and-django-part 1-video]
|
||||
* [Django Rest Framework Part 1 (Video)][django-rest-framework-part-1-video]
|
||||
* [Pyowa July 2013 - Django Rest Framework (Video)][pyowa-july-2013-django-rest-framework-video]
|
||||
* [django-rest-framework and angularjs (Video)][django-rest-framework-and-angularjs-video]
|
||||
|
||||
* [Web API performance: profiling Django REST framework](http://dabapps.com/blog/api-performance-profiling-django-rest-framework/)
|
||||
* [API Development with Django and Django REST Framework](https://bnotions.com/api-development-with-django-and-django-rest-framework/)
|
||||
### Articles
|
||||
|
||||
* [Web API performance: profiling Django REST framework][web-api-performance-profiling-django-rest-framework]
|
||||
* [API Development with Django and Django REST Framework][api-development-with-django-and-django-rest-framework]
|
||||
|
||||
|
||||
[cookiecutter]: https://github.com/jpadilla/cookiecutter-django-rest-framework
|
||||
[new-repo]: https://github.com/new
|
||||
[create-a-repo]: https://help.github.com/articles/create-a-repo/
|
||||
[travis-ci]: https://travis-ci.org
|
||||
[travis-profile]: https://travis-ci.org/profile
|
||||
[pypi-register]: https://pypi.python.org/pypi?%3Aaction=register_form
|
||||
[semver]: http://semver.org/
|
||||
[tox-docs]: https://tox.readthedocs.org/en/latest/
|
||||
[drf-compat]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/compat.py
|
||||
[rest-framework-grid]: https://www.djangopackages.com/grids/g/django-rest-framework/
|
||||
[drf-create-pr]: https://github.com/tomchristie/django-rest-framework/compare
|
||||
[drf-create-issue]: https://github.com/tomchristie/django-rest-framework/issues/new
|
||||
[authentication]: ../api-guide/authentication.md
|
||||
[permissions]: ../api-guide/permissions.md
|
||||
[discussion-group]: https://groups.google.com/forum/#!forum/django-rest-framework
|
||||
[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
|
||||
[django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit
|
||||
[doac]: https://github.com/Rediker-Software/doac
|
||||
[djangorestframework-jwt]: https://github.com/GetBlimp/django-rest-framework-jwt
|
||||
[hawkrest]: https://github.com/kumar303/hawkrest
|
||||
[djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature
|
||||
[djoser]: https://github.com/sunscrapers/djoser
|
||||
[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions
|
||||
[djangorestframework-composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions
|
||||
[rest-condition]: https://github.com/caxap/rest_condition
|
||||
[django-rest-framework-mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine
|
||||
[djangorestframework-gis]: https://github.com/djangonauts/django-rest-framework-gis
|
||||
[djangorestframework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
|
||||
[drf-compound-fields]: https://github.com/estebistec/drf-compound-fields
|
||||
[django-extra-fields]: https://github.com/Hipo/drf-extra-fields
|
||||
[djangorestframework-bulk]: https://github.com/miki725/django-rest-framework-bulk
|
||||
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
|
||||
[wq.db.rest]: http://wq.io/docs/about-rest
|
||||
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
|
||||
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
|
||||
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
|
||||
[drf_ujson]: https://github.com/gizmag/drf-ujson-renderer
|
||||
[rest-pandas]: https://github.com/wq/django-rest-pandas
|
||||
[djangorestframework-chain]: https://github.com/philipn/django-rest-framework-chain
|
||||
[djangorestrelationalhyperlink]: https://github.com/fredkingham/django_rest_model_hyperlink_serializers_project
|
||||
[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
|
||||
[django-rest-framework-proxy]: https://github.com/eofs/django-rest-framework-proxy
|
||||
[gaiarestframework]: https://github.com/AppsFuel/gaiarestframework
|
||||
[drf-extensions]: https://github.com/chibisov/drf-extensions
|
||||
[ember-data-django-rest-adapter]: https://github.com/toranb/ember-data-django-rest-adapter
|
||||
[beginners-guide-to-the-django-rest-framework]: http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786
|
||||
[getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
|
||||
[end-to-end-web-app-with-django-rest-framework-angularjs]: http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework
|
||||
[start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/
|
||||
[permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/
|
||||
[viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/
|
||||
[django-rest-framework-user-endpoint]: http://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/
|
||||
[check-credentials-using-django-rest-framework]: http://richardtier.com/2014/03/06/110/
|
||||
[ember-and-django-part 1-video]: http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1
|
||||
[django-rest-framework-part-1-video]: http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1
|
||||
[pyowa-july-2013-django-rest-framework-video]: http://www.youtube.com/watch?v=e1zrehvxpbo
|
||||
[django-rest-framework-and-angularjs-video]: http://www.youtube.com/watch?v=q8frbgtj020
|
||||
[web-api-performance-profiling-django-rest-framework]: http://dabapps.com/blog/api-performance-profiling-django-rest-framework/
|
||||
[api-development-with-django-and-django-rest-framework]: https://bnotions.com/api-development-with-django-and-django-rest-framework/
|
||||
|
|
|
@ -8,7 +8,7 @@ Although flat data structures serve to properly delineate between the individual
|
|||
|
||||
Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go. However, there are a few more subtleties to using writable nested serializers, due to the dependencies between the various model instances, and the need to save or delete multiple instances in a single action.
|
||||
|
||||
## One-to-many data structures
|
||||
## One-to-many data structures
|
||||
|
||||
*Example of a **read-only** nested serializer. Nothing complex to worry about here.*
|
||||
|
||||
|
@ -16,10 +16,10 @@ Nested data structures are easy enough to work with if they're read-only - simpl
|
|||
class Meta:
|
||||
model = ToDoItem
|
||||
fields = ('text', 'is_completed')
|
||||
|
||||
|
||||
class ToDoListSerializer(serializers.ModelSerializer):
|
||||
items = ToDoItemSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = ToDoList
|
||||
fields = ('title', 'items')
|
||||
|
@ -31,7 +31,7 @@ Some example output from our serializer.
|
|||
'items': {
|
||||
{'text': 'Compile playlist', 'is_completed': True},
|
||||
{'text': 'Send invites', 'is_completed': False},
|
||||
{'text': 'Clean house', 'is_completed': False}
|
||||
{'text': 'Clean house', 'is_completed': False}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,4 +44,4 @@ Let's take a look at updating our nested one-to-many data structure.
|
|||
### Making PATCH requests
|
||||
|
||||
|
||||
[cite]: http://jsonapi.org/format/#url-based-json-api
|
||||
[cite]: http://jsonapi.org/format/#url-based-json-api
|
||||
|
|
|
@ -134,7 +134,7 @@ A serializer class is very similar to a Django `Form` class, and includes simila
|
|||
|
||||
The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The `style={'type': 'textarea'}` flag above is equivelent to using `widget=widgets.Textarea` on a Django `Form` class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.
|
||||
|
||||
We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.
|
||||
We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.
|
||||
|
||||
## Working with Serializers
|
||||
|
||||
|
|
|
@ -1,50 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<title>Django REST framework - 404 - Page not found</title>
|
||||
<link href="http://www.django-rest-framework.org/img/favicon.ico" rel="icon" type="image/x-icon">
|
||||
<link rel="canonical" href="http://www.django-rest-framework.org/404"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Django, API, REST, 404 - Page not found">
|
||||
<meta name="author" content="Tom Christie">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/default.css" rel="stylesheet">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<title>Django REST framework - 404 - Page not found</title>
|
||||
<link href="http://www.django-rest-framework.org/img/favicon.ico" rel="icon" type="image/x-icon">
|
||||
<link rel="canonical" href="http://www.django-rest-framework.org/404" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Django, API, REST, 404 - Page not found">
|
||||
<meta name="author" content="Tom Christie">
|
||||
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<!-- Le styles -->
|
||||
<link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="http://www.django-rest-framework.org/css/default.css" rel="stylesheet">
|
||||
|
||||
<script type="text/javascript">
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
(function() {
|
||||
var ga = document.createElement('script');
|
||||
ga.type = 'text/javascript';
|
||||
ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="prettyPrint()" class="404-page">
|
||||
<body onload="prettyPrint()" class="404-page">
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
|
||||
<a class="repo-link btn btn-inverse btn-small disabled" href="#">Next <i class="icon-arrow-right icon-white"></i></a>
|
||||
<a class="repo-link btn btn-inverse btn-small disabled" href="#"><i class="icon-arrow-left icon-white"></i> Previous</a>
|
||||
<a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
|
||||
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
|
||||
<a class="repo-link btn btn-inverse btn-small disabled" href="#">Next <i class="icon-arrow-right icon-white"></i></a>
|
||||
<a class="repo-link btn btn-inverse btn-small disabled" href="#"><i class="icon-arrow-left icon-white"></i> Previous</a>
|
||||
<a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
|
||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
|
@ -121,81 +125,92 @@
|
|||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
<!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Search Modal -->
|
||||
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Documentation search</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Custom google search -->
|
||||
<script>
|
||||
(function() {
|
||||
var cx = '015016005043623903336:rxraeohqk6w';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
</script>
|
||||
<gcse:search></gcse:search>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Search Modal -->
|
||||
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Documentation search</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Custom google search -->
|
||||
<script>
|
||||
(function() {
|
||||
var cx = '015016005043623903336:rxraeohqk6w';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
</script>
|
||||
<gcse:search></gcse:search>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div id="main-content" class="span12">
|
||||
<h1 id="404-page-not-found" style="text-align: center">404</h1>
|
||||
<p style="text-align: center"><strong>Page not found</strong></p>
|
||||
<p style="text-align: center"><strong>Page not found</strong>
|
||||
</p>
|
||||
<p style="text-align: center">Try the <a href="http://www.django-rest-framework.org/">homepage</a>, or <a href="#searchModal" data-toggle="modal">search the documentation</a>.</p>
|
||||
</div><!--/span-->
|
||||
</div><!--/row-->
|
||||
</div><!--/.fluid-container-->
|
||||
</div><!--/.body content-->
|
||||
</div>
|
||||
<!--/span-->
|
||||
</div>
|
||||
<!--/row-->
|
||||
</div>
|
||||
<!--/.fluid-container-->
|
||||
</div>
|
||||
<!--/.body content-->
|
||||
|
||||
<div id="push"></div>
|
||||
</div><!--/.wrapper -->
|
||||
<div id="push"></div>
|
||||
</div>
|
||||
<!--/.wrapper -->
|
||||
|
||||
<footer class="span12">
|
||||
<p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a></p>
|
||||
<p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<!-- Le javascript
|
||||
<!-- Le javascript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="http://www.django-rest-framework.org/js/jquery-1.8.1-min.js"></script>
|
||||
<script src="http://www.django-rest-framework.org/js/prettify-1.0.js"></script>
|
||||
<script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script>
|
||||
<script>
|
||||
//$('.side-nav').scrollspy()
|
||||
var shiftWindow = function() { scrollBy(0, -50) };
|
||||
if (location.hash) shiftWindow();
|
||||
window.addEventListener("hashchange", shiftWindow);
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="http://www.django-rest-framework.org/js/jquery-1.8.1-min.js"></script>
|
||||
<script src="http://www.django-rest-framework.org/js/prettify-1.0.js"></script>
|
||||
<script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script>
|
||||
<script>
|
||||
//$('.side-nav').scrollspy()
|
||||
var shiftWindow = function() {
|
||||
scrollBy(0, -50)
|
||||
};
|
||||
if (location.hash) shiftWindow();
|
||||
window.addEventListener("hashchange", shiftWindow);
|
||||
|
||||
$('.dropdown-menu').on('click touchstart', function(event) {
|
||||
event.stopPropagation();
|
||||
$('.dropdown-menu').on('click touchstart', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Dynamically force sidenav to no higher than browser window
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
|
||||
$(function() {
|
||||
$(window).resize(function() {
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
// Dynamically force sidenav to no higher than browser window
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
|
||||
$(function(){
|
||||
$(window).resize(function(){
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
||||
</html>
|
196
docs_theme/base.html
Normal file
196
docs_theme/base.html
Normal file
|
@ -0,0 +1,196 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<title>{{ page_title }}</title>
|
||||
<link href="{{ base_url }}/img/favicon.ico" rel="icon" type="image/x-icon">
|
||||
<link rel="canonical" href="{{ canonical_url }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Django, API, REST, {{ current_page.title }}">
|
||||
<meta name="author" content="Tom Christie">
|
||||
|
||||
<!-- 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-responsive.css" rel="stylesheet">
|
||||
<link href="{{ base_url }}/css/default.css" rel="stylesheet">
|
||||
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script');
|
||||
ga.type = 'text/javascript';
|
||||
ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
span.fusion-wrap a {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
color: black;
|
||||
}
|
||||
a.fusion-poweredby {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
div.promo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="prettyPrint()" class="{% if current_page.is_homepage %}index{% endif %}-page">
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
{% include "nav.html" %}
|
||||
|
||||
<div class="body-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Search Modal -->
|
||||
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Documentation search</h3>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<!-- Custom google search -->
|
||||
<script>
|
||||
(function() {
|
||||
var cx = '015016005043623903336:rxraeohqk6w';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
</script>
|
||||
<gcse:search></gcse:search>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
<div class="span3">
|
||||
<!-- TODO
|
||||
<p style="margin-top: -12px">
|
||||
<a class="btn btn-mini btn-primary" style="width: 60px">« previous</a>
|
||||
<a class="btn btn-mini btn-primary" style="float: right; margin-right: 8px; width: 60px;">next »</a>
|
||||
</p>
|
||||
-->
|
||||
<div id="table-of-contents">
|
||||
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
|
||||
|
||||
{% if current_page.is_homepage %}
|
||||
<li class="main">
|
||||
<a href="#">Django REST framework</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for toc_item in toc %}
|
||||
|
||||
<li class="{% if not current_page.is_homepage %}main{% endif %}">
|
||||
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
|
||||
</li>
|
||||
|
||||
{% for toc_item in toc_item.children %}
|
||||
<li>
|
||||
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if current_page.is_homepage %}
|
||||
<div class="promo">
|
||||
<hr/>
|
||||
<script type="text/javascript" src="//cdn.fusionads.net/fusion.js?zoneid=1332&serve=C6SDP2Y&placement=djangorestframework" id="_fusionads_js"></script>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="span9">
|
||||
{% if meta.source %}
|
||||
{% for filename in meta.source %}
|
||||
<a class="github" href="https://github.com/tomchristie/django-rest-framework/tree/master/rest_framework/{{ filename }}">
|
||||
<span class="label label-info">{{ filename }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{{ content }}
|
||||
</div>
|
||||
<!--/span-->
|
||||
</div>
|
||||
<!--/row-->
|
||||
</div>
|
||||
<!--/.fluid-container-->
|
||||
</div>
|
||||
<!--/.body content-->
|
||||
<div id="push"></div>
|
||||
</div>
|
||||
<!--/.wrapper -->
|
||||
|
||||
<footer class="span12">
|
||||
<p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<!-- Le javascript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
|
||||
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
|
||||
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
|
||||
|
||||
<script>
|
||||
//$('.side-nav').scrollspy()
|
||||
var shiftWindow = function() {
|
||||
scrollBy(0, -50)
|
||||
};
|
||||
if (location.hash) shiftWindow();
|
||||
window.addEventListener("hashchange", shiftWindow);
|
||||
|
||||
$('.dropdown-menu').on('click touchstart', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Dynamically force sidenav to no higher than browser window
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
|
||||
$(function() {
|
||||
$(window).resize(function() {
|
||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
47
docs_theme/nav.html
Normal file
47
docs_theme/nav.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
|
||||
<a class="repo-link btn btn-inverse btn-small {% if not next_page %}disabled{% endif %}" rel="prev" {% if next_page %}href="{{ next_page.url }}"{% endif %}>
|
||||
Next <i class="icon-arrow-right icon-white"></i>
|
||||
</a>
|
||||
<a class="repo-link btn btn-inverse btn-small {% if not previous_page %}disabled{% endif %}" rel="next" {% if previous_page %}href="{{ previous_page.url }}"{% endif %}>
|
||||
<i class="icon-arrow-left icon-white"></i> Previous
|
||||
</a>
|
||||
<a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>
|
||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</a>
|
||||
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
|
||||
<div class="nav-collapse collapse">
|
||||
{% if include_nav %}
|
||||
<!-- Main navigation -->
|
||||
<ul class="nav navbar-nav">
|
||||
<li {% if current_page.is_homepage %}class="active"{% endif %}><a href="/">Home</a></li>
|
||||
{% for nav_item in nav %} {% if nav_item.children %}
|
||||
<li class="dropdown{% if nav_item.active %} active{% endif %}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ nav_item.title }} <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for nav_item in nav_item.children %}
|
||||
<li {% if nav_item.active %}class="active" {% endif %}>
|
||||
<a href="{{ nav_item.url }}">{{ nav_item.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li {% if nav_item.active %}class="active" {% endif %}>
|
||||
<a href="{{ nav_item.url }}">{{ nav_item.title }}</a>
|
||||
</li>
|
||||
{% endif %} {% endfor %}
|
||||
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!--/.nav-collapse -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
203
mkdocs.py
203
mkdocs.py
|
@ -1,203 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import markdown
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
root_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
docs_dir = os.path.join(root_dir, 'docs')
|
||||
html_dir = os.path.join(root_dir, 'html')
|
||||
|
||||
local = not '--deploy' in sys.argv
|
||||
preview = '-p' in sys.argv
|
||||
|
||||
if local:
|
||||
base_url = 'file://%s/' % os.path.normpath(os.path.join(os.getcwd(), html_dir))
|
||||
suffix = '.html'
|
||||
index = 'index.html'
|
||||
else:
|
||||
base_url = 'http://www.django-rest-framework.org'
|
||||
suffix = ''
|
||||
index = ''
|
||||
|
||||
|
||||
main_header = '<li class="main"><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/tree/master/rest_framework/\1"><span class="label label-info">\1</span></a>'
|
||||
|
||||
page = open(os.path.join(docs_dir, 'template.html'), 'r').read()
|
||||
|
||||
# Copy static files
|
||||
# for static in ['css', 'js', 'img']:
|
||||
# source = os.path.join(docs_dir, 'static', static)
|
||||
# target = os.path.join(html_dir, static)
|
||||
# if os.path.exists(target):
|
||||
# shutil.rmtree(target)
|
||||
# shutil.copytree(source, target)
|
||||
|
||||
|
||||
# Hacky, but what the hell, it'll do the job
|
||||
path_list = [
|
||||
'index.md',
|
||||
'tutorial/quickstart.md',
|
||||
'tutorial/1-serialization.md',
|
||||
'tutorial/2-requests-and-responses.md',
|
||||
'tutorial/3-class-based-views.md',
|
||||
'tutorial/4-authentication-and-permissions.md',
|
||||
'tutorial/5-relationships-and-hyperlinked-apis.md',
|
||||
'tutorial/6-viewsets-and-routers.md',
|
||||
'api-guide/requests.md',
|
||||
'api-guide/responses.md',
|
||||
'api-guide/views.md',
|
||||
'api-guide/generic-views.md',
|
||||
'api-guide/viewsets.md',
|
||||
'api-guide/routers.md',
|
||||
'api-guide/parsers.md',
|
||||
'api-guide/renderers.md',
|
||||
'api-guide/serializers.md',
|
||||
'api-guide/fields.md',
|
||||
'api-guide/relations.md',
|
||||
'api-guide/validators.md',
|
||||
'api-guide/authentication.md',
|
||||
'api-guide/permissions.md',
|
||||
'api-guide/throttling.md',
|
||||
'api-guide/filtering.md',
|
||||
'api-guide/pagination.md',
|
||||
'api-guide/content-negotiation.md',
|
||||
'api-guide/format-suffixes.md',
|
||||
'api-guide/reverse.md',
|
||||
'api-guide/exceptions.md',
|
||||
'api-guide/status-codes.md',
|
||||
'api-guide/testing.md',
|
||||
'api-guide/settings.md',
|
||||
'topics/documenting-your-api.md',
|
||||
'topics/ajax-csrf-cors.md',
|
||||
'topics/browser-enhancements.md',
|
||||
'topics/browsable-api.md',
|
||||
'topics/rest-hypermedia-hateoas.md',
|
||||
'topics/third-party-resources.md',
|
||||
'topics/contributing.md',
|
||||
'topics/rest-framework-2-announcement.md',
|
||||
'topics/2.2-announcement.md',
|
||||
'topics/2.3-announcement.md',
|
||||
'topics/2.4-announcement.md',
|
||||
'topics/release-notes.md',
|
||||
'topics/credits.md',
|
||||
]
|
||||
|
||||
prev_url_map = {}
|
||||
next_url_map = {}
|
||||
for idx in range(len(path_list)):
|
||||
path = path_list[idx]
|
||||
rel = '../' * path.count('/')
|
||||
|
||||
if idx == 1 and not local:
|
||||
# Link back to '/', not '/index'
|
||||
prev_url_map[path] = '/'
|
||||
elif idx > 0:
|
||||
prev_url_map[path] = rel + path_list[idx - 1][:-3] + suffix
|
||||
|
||||
if idx < len(path_list) - 1:
|
||||
next_url_map[path] = rel + path_list[idx + 1][:-3] + suffix
|
||||
|
||||
|
||||
for (dirpath, dirnames, filenames) in os.walk(docs_dir):
|
||||
relative_dir = dirpath.replace(docs_dir, '').lstrip(os.path.sep)
|
||||
build_dir = os.path.join(html_dir, relative_dir)
|
||||
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
|
||||
for filename in filenames:
|
||||
path = os.path.join(dirpath, filename)
|
||||
relative_path = os.path.join(relative_dir, filename)
|
||||
|
||||
if not filename.endswith('.md'):
|
||||
if relative_dir:
|
||||
output_path = os.path.join(build_dir, filename)
|
||||
shutil.copy(path, output_path)
|
||||
continue
|
||||
|
||||
output_path = os.path.join(build_dir, filename[:-3] + '.html')
|
||||
|
||||
toc = ''
|
||||
text = open(path, 'r').read().decode('utf-8')
|
||||
main_title = None
|
||||
description = 'Django, API, REST'
|
||||
for line in text.splitlines():
|
||||
if line.startswith('# '):
|
||||
title = line[2:].strip()
|
||||
template = main_header
|
||||
description = description + ', ' + title
|
||||
elif line.startswith('## '):
|
||||
title = line[3:].strip()
|
||||
template = sub_header
|
||||
else:
|
||||
continue
|
||||
|
||||
if not main_title:
|
||||
main_title = title
|
||||
anchor = title.lower().replace(' ', '-').replace(':-', '-').replace("'", '').replace('?', '').replace('.', '')
|
||||
template = template.replace('{{ title }}', title)
|
||||
template = template.replace('{{ anchor }}', anchor)
|
||||
toc += template + '\n'
|
||||
|
||||
if filename == 'index.md':
|
||||
main_title = 'Django REST framework - Web APIs for Django'
|
||||
else:
|
||||
main_title = main_title + ' - Django REST framework'
|
||||
|
||||
if relative_path == 'index.md':
|
||||
canonical_url = base_url
|
||||
else:
|
||||
canonical_url = base_url + '/' + relative_path[:-3] + suffix
|
||||
prev_url = prev_url_map.get(relative_path)
|
||||
next_url = next_url_map.get(relative_path)
|
||||
|
||||
content = markdown.markdown(text, ['headerid'])
|
||||
|
||||
output = page.replace('{{ content }}', content).replace('{{ toc }}', toc).replace('{{ base_url }}', base_url).replace('{{ suffix }}', suffix).replace('{{ index }}', index)
|
||||
output = output.replace('{{ title }}', main_title)
|
||||
output = output.replace('{{ description }}', description)
|
||||
output = output.replace('{{ page_id }}', filename[:-3])
|
||||
output = output.replace('{{ canonical_url }}', canonical_url)
|
||||
|
||||
if filename =='index.md':
|
||||
output = output.replace('{{ ad_block }}', """<hr/>
|
||||
<script type="text/javascript" src="//cdn.fusionads.net/fusion.js?zoneid=1332&serve=C6SDP2Y&placement=djangorestframework" id="_fusionads_js"></script>""")
|
||||
else:
|
||||
output = output.replace('{{ ad_block }}', '')
|
||||
|
||||
if prev_url:
|
||||
output = output.replace('{{ prev_url }}', prev_url)
|
||||
output = output.replace('{{ prev_url_disabled }}', '')
|
||||
else:
|
||||
output = output.replace('{{ prev_url }}', '#')
|
||||
output = output.replace('{{ prev_url_disabled }}', 'disabled')
|
||||
|
||||
if next_url:
|
||||
output = output.replace('{{ next_url }}', next_url)
|
||||
output = output.replace('{{ next_url_disabled }}', '')
|
||||
else:
|
||||
output = output.replace('{{ next_url }}', '#')
|
||||
output = output.replace('{{ next_url_disabled }}', 'disabled')
|
||||
|
||||
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(output_path, 'w').write(output.encode('utf-8'))
|
||||
|
||||
if preview:
|
||||
import subprocess
|
||||
|
||||
url = 'html/index.html'
|
||||
|
||||
try:
|
||||
subprocess.Popen(["open", url]) # Mac
|
||||
except OSError:
|
||||
subprocess.Popen(["xdg-open", url]) # Linux
|
||||
except:
|
||||
os.startfile(url) # Windows
|
57
mkdocs.yml
Normal file
57
mkdocs.yml
Normal file
|
@ -0,0 +1,57 @@
|
|||
site_name: Django REST framework
|
||||
site_url: http://www.django-rest-framework.org/
|
||||
site_description: Django REST framework - Web APIs for Django
|
||||
|
||||
repo_url: https://github.com/tomchristie/django-rest-framework
|
||||
|
||||
theme_dir: docs_theme
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['tutorial/quickstart.md', 'Quickstart']
|
||||
- ['tutorial/1-serialization.md', 'Tutorial', '1 - Serialization']
|
||||
- ['tutorial/2-requests-and-responses.md', 'Tutorial', '2 - Requests and responses']
|
||||
- ['tutorial/3-class-based-views.md', 'Tutorial', '3 - Class based views']
|
||||
- ['tutorial/4-authentication-and-permissions.md', 'Tutorial', '4 - Authentication and permissions']
|
||||
- ['tutorial/5-relationships-and-hyperlinked-apis.md', 'Tutorial', '5 - Relationships and hyperlinked APIs']
|
||||
- ['tutorial/6-viewsets-and-routers.md', 'Tutorial', '6 - Viewsets and routers']
|
||||
- ['api-guide/requests.md', 'API Guide', 'Requests']
|
||||
- ['api-guide/responses.md', 'API Guide', 'Responses']
|
||||
- ['api-guide/views.md', 'API Guide', 'Views']
|
||||
- ['api-guide/generic-views.md', 'API Guide', 'Generic views']
|
||||
- ['api-guide/viewsets.md', 'API Guide', 'Viewsets']
|
||||
- ['api-guide/routers.md', 'API Guide', 'Routers']
|
||||
- ['api-guide/parsers.md', 'API Guide', 'Parsers']
|
||||
- ['api-guide/renderers.md', 'API Guide', 'Renderers']
|
||||
- ['api-guide/serializers.md', 'API Guide', 'Serializers']
|
||||
- ['api-guide/fields.md', 'API Guide', 'Serializer fields']
|
||||
- ['api-guide/relations.md', 'API Guide', 'Serializer relations']
|
||||
# - ['api-guide/validators.md', 'API Guide', 'Validators']
|
||||
- ['api-guide/authentication.md', 'API Guide', 'Authentication']
|
||||
- ['api-guide/permissions.md', 'API Guide', 'Permissions']
|
||||
- ['api-guide/throttling.md', 'API Guide', 'Throttling']
|
||||
- ['api-guide/filtering.md', 'API Guide', 'Filtering']
|
||||
- ['api-guide/pagination.md', 'API Guide', 'Pagination']
|
||||
- ['api-guide/content-negotiation.md', 'API Guide', 'Content negotiation']
|
||||
- ['api-guide/format-suffixes.md', 'API Guide', 'Format suffixes']
|
||||
- ['api-guide/reverse.md', 'API Guide', 'Returning URLs']
|
||||
- ['api-guide/exceptions.md', 'API Guide', 'Exceptions']
|
||||
- ['api-guide/status-codes.md', 'API Guide', 'Status codes']
|
||||
- ['api-guide/testing.md', 'API Guide', 'Testing']
|
||||
- ['api-guide/settings.md', 'API Guide', 'Settings']
|
||||
- ['topics/documenting-your-api.md', 'Topics', 'Documenting your API']
|
||||
- ['topics/ajax-csrf-cors.md', 'Topics', 'AJAX, CSRF & CORS']
|
||||
- ['topics/browser-enhancements.md', 'Topics',]
|
||||
- ['topics/browsable-api.md', 'Topics', 'The Browsable API']
|
||||
- ['topics/rest-hypermedia-hateoas.md', 'Topics', 'REST, Hypermedia & HATEOAS']
|
||||
- ['topics/third-party-resources.md', 'Topics', 'Third Party Resources']
|
||||
- ['topics/contributing.md', 'Topics', 'Contributing to REST framework']
|
||||
- ['topics/rest-framework-2-announcement.md', 'Topics', '2.0 Announcement']
|
||||
- ['topics/2.2-announcement.md', 'Topics', '2.2 Announcement']
|
||||
- ['topics/2.3-announcement.md', 'Topics', '2.3 Announcement']
|
||||
- ['topics/2.4-announcement.md', 'Topics', '2.4 Announcement']
|
||||
- ['topics/kickstarter-announcement.md', 'Topics', 'Kickstarter Announcement']
|
||||
- ['topics/release-notes.md', 'Topics', 'Release Notes']
|
||||
- ['topics/credits.md', 'Topics', 'Credits']
|
||||
|
||||
google_analytics: ['UA-18852272-2', 'django-rest-framework.org']
|
|
@ -9,7 +9,7 @@ from django.test import TestCase
|
|||
from django.utils import six, unittest
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import status, permissions
|
||||
from rest_framework.compat import yaml, etree, StringIO
|
||||
from rest_framework.compat import yaml, etree, StringIO, BytesIO
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
|
||||
|
@ -467,7 +467,7 @@ if yaml:
|
|||
obj = {'foo': ['bar', 'baz']}
|
||||
renderer = YAMLRenderer()
|
||||
content = renderer.render(obj, 'application/yaml')
|
||||
self.assertEqual(content, _yaml_repr)
|
||||
self.assertEqual(content.decode('utf-8'), _yaml_repr)
|
||||
|
||||
def test_render_and_parse(self):
|
||||
"""
|
||||
|
@ -480,7 +480,7 @@ if yaml:
|
|||
parser = YAMLParser()
|
||||
|
||||
content = renderer.render(obj, 'application/yaml')
|
||||
data = parser.parse(StringIO(content))
|
||||
data = parser.parse(BytesIO(content))
|
||||
self.assertEqual(obj, data)
|
||||
|
||||
def test_render_decimal(self):
|
||||
|
@ -489,7 +489,7 @@ if yaml:
|
|||
"""
|
||||
renderer = YAMLRenderer()
|
||||
content = renderer.render({'field': Decimal('111.2')}, 'application/yaml')
|
||||
self.assertYAMLContains(content, "field: '111.2'")
|
||||
self.assertYAMLContains(content.decode('utf-8'), "field: '111.2'")
|
||||
|
||||
def assertYAMLContains(self, content, string):
|
||||
self.assertTrue(string in content, '%r not in %r' % (string, content))
|
||||
|
|
Loading…
Reference in New Issue
Block a user