mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
Merge branch 'mkdocs' of git://github.com/d0ugal/django-rest-framework into d0ugal-mkdocs
This commit is contained in:
commit
d7f8047add
|
@ -4,6 +4,7 @@ sudo: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- TOX_ENV=py27-flake8
|
- TOX_ENV=py27-flake8
|
||||||
|
- TOX_ENV=py27-docs
|
||||||
- TOX_ENV=py34-django17
|
- TOX_ENV=py34-django17
|
||||||
- TOX_ENV=py33-django17
|
- TOX_ENV=py33-django17
|
||||||
- TOX_ENV=py32-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
|
## 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 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
|
## Language style
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="authentication.py"></a>
|
source: authentication.py
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="negotiation.py"></a>
|
source: negotiation.py
|
||||||
|
|
||||||
# Content negotiation
|
# 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.
|
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.
|
Select the first parser in the `.parser_classes` list.
|
||||||
"""
|
"""
|
||||||
return parsers[0]
|
return parsers[0]
|
||||||
|
|
||||||
def select_renderer(self, request, renderers, format_suffix):
|
def select_renderer(self, request, renderers, format_suffix):
|
||||||
"""
|
"""
|
||||||
Select the first renderer in the `.renderer_classes` list.
|
Select the first renderer in the `.renderer_classes` list.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="exceptions.py"></a>
|
source: exceptions.py
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="fields.py"></a>
|
source: fields.py
|
||||||
|
|
||||||
# Serializer fields
|
# Serializer fields
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ A dictionary of error codes to error messages.
|
||||||
### `widget`
|
### `widget`
|
||||||
|
|
||||||
Used only if rendering the field to HTML.
|
Used only if rendering the field to HTML.
|
||||||
This argument sets the widget that should be used to render the field. For more details, and a list of available widgets, see [the Django documentation on form widgets][django-widgets].
|
This argument sets the widget that should be used to render the field. For more details, and a list of available widgets, see [the Django documentation on form widgets][django-widgets].
|
||||||
|
|
||||||
### `label`
|
### `label`
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="filters.py"></a>
|
source: filters.py
|
||||||
|
|
||||||
# Filtering
|
# Filtering
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ For example:
|
||||||
|
|
||||||
class PurchaseList(generics.ListAPIView):
|
class PurchaseList(generics.ListAPIView):
|
||||||
serializer_class = PurchaseSerializer
|
serializer_class = PurchaseSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
This view should return a list of all the purchases
|
This view should return a list of all the purchases
|
||||||
|
@ -38,7 +38,7 @@ For example:
|
||||||
|
|
||||||
## Filtering against the URL
|
## 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:
|
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):
|
class PurchaseList(generics.ListAPIView):
|
||||||
serializer_class = PurchaseSerializer
|
serializer_class = PurchaseSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
This view should return a list of all the purchases for
|
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']
|
username = self.kwargs['username']
|
||||||
return Purchase.objects.filter(purchaser__username=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.
|
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):
|
class PurchaseList(generics.ListAPIView):
|
||||||
serializer_class = PurchaseSerializer
|
serializer_class = PurchaseSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Optionally restricts the returned purchases to a given user,
|
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
|
http://example.com/api/products/4675/?category=clothing&max_price=10.00
|
||||||
|
|
||||||
## Overriding the initial queryset
|
## 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:
|
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):
|
class PurchasedProductsList(generics.ListAPIView):
|
||||||
|
@ -124,7 +124,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
|
||||||
model = Product
|
model = Product
|
||||||
serializer_class = ProductSerializer
|
serializer_class = ProductSerializer
|
||||||
filter_class = ProductFilter
|
filter_class = ProductFilter
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
return user.purchase_set.all()
|
return user.purchase_set.all()
|
||||||
|
@ -135,7 +135,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
|
||||||
|
|
||||||
## DjangoFilterBackend
|
## 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`.
|
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:
|
And now you can execute:
|
||||||
|
|
||||||
http://example.com/api/products?manufacturer=foo
|
http://example.com/api/products?manufacturer=foo
|
||||||
|
|
||||||
For more details on using filter sets see the [django-filter documentation][django-filter-docs].
|
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**
|
**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.
|
* 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.
|
* `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.
|
* 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()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
filter_backends = (filters.OrderingFilter,)
|
filter_backends = (filters.OrderingFilter,)
|
||||||
ordering = ('username',)
|
ordering = ('username',)
|
||||||
|
|
||||||
The `ordering` attribute may be either a string or a list/tuple of strings.
|
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
|
# Format suffixes
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ used all the time.
|
||||||
>
|
>
|
||||||
> — Roy Fielding, [REST discuss mailing list][cite]
|
> — 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.
|
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.
|
* **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.
|
* **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:
|
Example:
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ Example:
|
||||||
url(r'^comments/$', views.comment_list),
|
url(r'^comments/$', views.comment_list),
|
||||||
url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)
|
url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
|
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:
|
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.
|
Also note that `format_suffix_patterns` does not support descending into `include` URL patterns.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Accept headers vs. format suffixes
|
## 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.
|
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]
|
“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>
|
source: mixins.py
|
||||||
<a class="github" href="generics.py"></a>
|
generics.py
|
||||||
|
|
||||||
# Generic views
|
# Generic views
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="pagination.py"></a>
|
source: pagination.py
|
||||||
|
|
||||||
# Pagination
|
# Pagination
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
||||||
>
|
>
|
||||||
> — [Django documentation][cite]
|
> — [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
|
## Paginating basic data
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ The `context` argument of the `PaginationSerializer` class may optionally includ
|
||||||
request = RequestFactory().get('/foobar')
|
request = RequestFactory().get('/foobar')
|
||||||
serializer = PaginationSerializer(instance=page, context={'request': request})
|
serializer = PaginationSerializer(instance=page, context={'request': request})
|
||||||
serializer.data
|
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.
|
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
|
# Parsers
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request.
|
The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request.
|
||||||
|
|
||||||
class PlainTextParser(BaseParser):
|
class PlainTextParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
|
@ -197,4 +197,4 @@ The following third party packages are also available.
|
||||||
[juanriaza]: https://github.com/juanriaza
|
[juanriaza]: https://github.com/juanriaza
|
||||||
[vbabiy]: https://github.com/vbabiy
|
[vbabiy]: https://github.com/vbabiy
|
||||||
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
|
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
|
||||||
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
|
[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="permissions.py"></a>
|
source: permissions.py
|
||||||
|
|
||||||
# Permissions
|
# Permissions
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ Permission checks are always run at the very start of the view, before any other
|
||||||
|
|
||||||
## How permissions are determined
|
## 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.
|
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.
|
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):
|
def has_object_permission(self, request, view, obj):
|
||||||
# Read permissions are allowed to any request,
|
# Read permissions are allowed to any request,
|
||||||
# so we'll always allow GET, HEAD or OPTIONS requests.
|
# 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
|
return True
|
||||||
|
|
||||||
# Instance must have an attribute named `owner`.
|
# Instance must have an attribute named `owner`.
|
||||||
return obj.owner == request.user
|
return obj.owner == request.user
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="relations.py"></a>
|
source: relations.py
|
||||||
|
|
||||||
# Serializer relations
|
# Serializer relations
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ In order to explain the various types of relational fields, we'll use a couple o
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('album', 'order')
|
unique_together = ('album', 'order')
|
||||||
order_by = 'order'
|
order_by = 'order'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return '%d: %s' % (self.order, self.title)
|
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.
|
`RelatedField` may be used to represent the target of the relationship using its `__unicode__` method.
|
||||||
|
|
||||||
For example, the following serializer.
|
For example, the following serializer.
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = serializers.RelatedField(many=True)
|
tracks = serializers.RelatedField(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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.
|
`PrimaryKeyRelatedField` may be used to represent the target of the relationship using its primary key.
|
||||||
|
|
||||||
For example, the following serializer:
|
For example, the following serializer:
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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.
|
`HyperlinkedRelatedField` may be used to represent the target of the relationship using a hyperlink.
|
||||||
|
|
||||||
For example, the following serializer:
|
For example, the following serializer:
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True,
|
tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True,
|
||||||
view_name='track-detail')
|
view_name='track-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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.
|
`SlugRelatedField` may be used to represent the target of the relationship using a field on the target.
|
||||||
|
|
||||||
For example, the following serializer:
|
For example, the following serializer:
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = serializers.SlugRelatedField(many=True, read_only=True,
|
tracks = serializers.SlugRelatedField(many=True, read_only=True,
|
||||||
slug_field='title')
|
slug_field='title')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
fields = ('album_name', 'artist', 'tracks')
|
||||||
|
@ -222,10 +222,10 @@ For example, the following serializer:
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Track
|
model = Track
|
||||||
fields = ('order', 'title')
|
fields = ('order', 'title')
|
||||||
|
|
||||||
class AlbumSerializer(serializers.ModelSerializer):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = TrackSerializer(many=True)
|
tracks = TrackSerializer(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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):
|
class AlbumSerializer(serializers.ModelSerializer):
|
||||||
tracks = TrackListingField(many=True)
|
tracks = TrackListingField(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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 AlbumSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('track_set', ...)
|
fields = ('track_set', ...)
|
||||||
|
|
||||||
See the Django documentation on [reverse relationships][reverse-relationships] for more details.
|
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):
|
class TaggedItem(models.Model):
|
||||||
"""
|
"""
|
||||||
Tags arbitrary model instances using a generic relation.
|
Tags arbitrary model instances using a generic relation.
|
||||||
|
|
||||||
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
|
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
|
||||||
"""
|
"""
|
||||||
tag_name = models.SlugField()
|
tag_name = models.SlugField()
|
||||||
content_type = models.ForeignKey(ContentType)
|
content_type = models.ForeignKey(ContentType)
|
||||||
object_id = models.PositiveIntegerField()
|
object_id = models.PositiveIntegerField()
|
||||||
tagged_object = GenericForeignKey('content_type', 'object_id')
|
tagged_object = GenericForeignKey('content_type', 'object_id')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.tag
|
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):
|
def to_native(self, value):
|
||||||
"""
|
"""
|
||||||
Serialize tagged objects to a simple textual representation.
|
Serialize tagged objects to a simple textual representation.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Bookmark):
|
if isinstance(value, Bookmark):
|
||||||
return 'Bookmark: ' + value.url
|
return 'Bookmark: ' + value.url
|
||||||
elif isinstance(value, Note):
|
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,
|
Serialize bookmark instances using a bookmark serializer,
|
||||||
and note instances using a note serializer.
|
and note instances using a note serializer.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Bookmark):
|
if isinstance(value, Bookmark):
|
||||||
serializer = BookmarkSerializer(value)
|
serializer = BookmarkSerializer(value)
|
||||||
elif isinstance(value, Note):
|
elif isinstance(value, Note):
|
||||||
|
@ -391,7 +391,7 @@ to ``True``.
|
||||||
|
|
||||||
## Advanced Hyperlinked fields
|
## 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.
|
There are two methods you'll need to override.
|
||||||
|
|
||||||
|
@ -411,7 +411,7 @@ May raise an `ObjectDoesNotExist` exception.
|
||||||
|
|
||||||
### Example
|
### 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):
|
class CustomHyperlinkedField(serializers.HyperlinkedRelatedField):
|
||||||
def get_url(self, obj, view_name, request, format):
|
def get_url(self, obj, view_name, request, format):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="renderers.py"></a>
|
source: renderers.py
|
||||||
|
|
||||||
# Renderers
|
# Renderers
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ The `jsonp` approach is essentially a browser hack, and is [only appropriate for
|
||||||
|
|
||||||
## YAMLRenderer
|
## YAMLRenderer
|
||||||
|
|
||||||
Renders the request data into `YAML`.
|
Renders the request data into `YAML`.
|
||||||
|
|
||||||
Requires the `pyyaml` package to be installed.
|
Requires the `pyyaml` package to be installed.
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ Note that non-ascii characters will be rendered using `\uXXXX` character escape.
|
||||||
|
|
||||||
## UnicodeYAMLRenderer
|
## UnicodeYAMLRenderer
|
||||||
|
|
||||||
Renders the request data into `YAML`.
|
Renders the request data into `YAML`.
|
||||||
|
|
||||||
Requires the `pyyaml` package to be installed.
|
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):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return Response({'user': self.object}, template_name='user_detail.html')
|
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.
|
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.
|
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',))
|
@api_view(('GET',))
|
||||||
@renderer_classes((StaticHTMLRenderer,))
|
@renderer_classes((StaticHTMLRenderer,))
|
||||||
def simple_html_view(request):
|
def simple_html_view(request):
|
||||||
data = '<html><body><h1>Hello, world</h1></body></html>'
|
data = '<html><body><h1>Hello, world</h1></body></html>'
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ The following is an example plaintext renderer that will return a response with
|
||||||
class PlainTextRenderer(renderers.BaseRenderer):
|
class PlainTextRenderer(renderers.BaseRenderer):
|
||||||
media_type = 'text/plain'
|
media_type = 'text/plain'
|
||||||
format = 'txt'
|
format = 'txt'
|
||||||
|
|
||||||
def render(self, data, media_type=None, renderer_context=None):
|
def render(self, data, media_type=None, renderer_context=None):
|
||||||
return data.encode(self.charset)
|
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.
|
* 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.
|
* 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.
|
* 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
|
## Varying behaviour by media type
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="request.py"></a>
|
source: request.py
|
||||||
|
|
||||||
# Requests
|
# Requests
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ REST framework supports a few browser enhancements such as browser-based `PUT`,
|
||||||
|
|
||||||
Browser-based `PUT`, `PATCH` and `DELETE` forms are transparently supported.
|
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
|
## .content_type
|
||||||
|
|
||||||
|
@ -115,7 +115,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.
|
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
|
## .stream
|
||||||
|
|
||||||
|
@ -125,7 +125,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.
|
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
|
# 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.
|
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.
|
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/
|
[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/
|
||||||
[statuscodes]: status-codes.md
|
[statuscodes]: status-codes.md
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="reverse.py"></a>
|
source: reverse.py
|
||||||
|
|
||||||
# Returning URLs
|
# 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.reverse import reverse
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
class APIRootView(APIView):
|
class APIRootView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
year = now().year
|
year = now().year
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="routers.py"></a>
|
source: routers.py
|
||||||
|
|
||||||
# Routers
|
# Routers
|
||||||
|
|
||||||
|
@ -56,10 +56,10 @@ For example, given a method like this on the `UserViewSet` class:
|
||||||
|
|
||||||
from myapp.permissions import IsAdminOrIsSelf
|
from myapp.permissions import IsAdminOrIsSelf
|
||||||
from rest_framework.decorators import detail_route
|
from rest_framework.decorators import detail_route
|
||||||
|
|
||||||
class UserViewSet(ModelViewSet):
|
class UserViewSet(ModelViewSet):
|
||||||
...
|
...
|
||||||
|
|
||||||
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
|
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
|
||||||
def set_password(self, request, pk=None):
|
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
|
## 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.
|
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
|
# Serializers
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ Let's start by creating a simple object we can use for example purposes:
|
||||||
self.email = email
|
self.email = email
|
||||||
self.content = content
|
self.content = content
|
||||||
self.created = created or datetime.datetime.now()
|
self.created = created or datetime.datetime.now()
|
||||||
|
|
||||||
comment = Comment(email='leila@example.com', content='foo bar')
|
comment = Comment(email='leila@example.com', content='foo bar')
|
||||||
|
|
||||||
We'll declare a serializer that we can use to serialize and deserialize `Comment` objects.
|
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.content = attrs.get('content', instance.content)
|
||||||
instance.created = attrs.get('created', instance.created)
|
instance.created = attrs.get('created', instance.created)
|
||||||
return instance
|
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.
|
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.)
|
These methods are essentially the reverse of `validate_<fieldname>` (see *Validation* below.)
|
||||||
|
|
||||||
## Deserializing objects
|
## 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 StringIO import StringIO
|
||||||
from rest_framework.parsers import JSONParser
|
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 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
|
## 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)
|
slug = serializers.CharField(max_length=100)
|
||||||
created = serializers.DateTimeField()
|
created = serializers.DateTimeField()
|
||||||
... # Various other fields
|
... # Various other fields
|
||||||
|
|
||||||
def get_identity(self, data):
|
def get_identity(self, data):
|
||||||
"""
|
"""
|
||||||
This hook is required for bulk update.
|
This hook is required for bulk update.
|
||||||
We need to override the default, to use the slug as the identity.
|
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,
|
Note that the data has not yet been validated at this point,
|
||||||
so we need to deal gracefully with incorrect datatypes.
|
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.
|
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:
|
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')
|
fields = ('id', 'account_name', 'users', 'created')
|
||||||
read_only_fields = ('account_name',)
|
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:
|
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.
|
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 = User(email=attrs['email'], username=attrs['username'])
|
||||||
user.set_password(attrs['password'])
|
user.set_password(attrs['password'])
|
||||||
return user
|
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.
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
# Don't pass the 'fields' arg up to the superclass
|
# Don't pass the 'fields' arg up to the superclass
|
||||||
fields = kwargs.pop('fields', None)
|
fields = kwargs.pop('fields', None)
|
||||||
|
|
||||||
# Instantiate the superclass normally
|
# Instantiate the superclass normally
|
||||||
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
|
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
if fields:
|
if fields:
|
||||||
# Drop any fields that are not specified in the `fields` argument.
|
# Drop any fields that are not specified in the `fields` argument.
|
||||||
allowed = set(fields)
|
allowed = set(fields)
|
||||||
|
@ -550,7 +550,7 @@ This would then allow you to do the following:
|
||||||
|
|
||||||
## Customising the default fields
|
## 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`.
|
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
|
# Settings
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="status.py"></a>
|
source: status.py
|
||||||
|
|
||||||
# Status Codes
|
# Status Codes
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ The module also includes a set of helper functions for testing if a status code
|
||||||
url = reverse('index')
|
url = reverse('index')
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertTrue(status.is_success(response.status_code))
|
self.assertTrue(status.is_success(response.status_code))
|
||||||
|
|
||||||
|
|
||||||
For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616]
|
For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616]
|
||||||
and [RFC 6585][rfc6585].
|
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_205_RESET_CONTENT
|
||||||
HTTP_206_PARTIAL_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.
|
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
|
# 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`.
|
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
|
## 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 django.core.urlresolvers import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
class AccountTests(APITestCase):
|
class AccountTests(APITestCase):
|
||||||
def test_create_account(self):
|
def test_create_account(self):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="throttling.py"></a>
|
source: throttling.py
|
||||||
|
|
||||||
# Throttling
|
# 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:
|
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):
|
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.
|
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):
|
class ContactListView(APIView):
|
||||||
throttle_scope = 'contacts'
|
throttle_scope = 'contacts'
|
||||||
...
|
...
|
||||||
|
|
||||||
class ContactDetailView(ApiView):
|
class ContactDetailView(ApiView):
|
||||||
throttle_scope = 'contacts'
|
throttle_scope = 'contacts'
|
||||||
...
|
...
|
||||||
|
|
||||||
class UploadView(APIView):
|
class UploadView(APIView):
|
||||||
throttle_scope = 'uploads'
|
throttle_scope = 'uploads'
|
||||||
...
|
...
|
||||||
|
|
||||||
...and the following settings.
|
...and the following settings.
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
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
|
# Class Based Views
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ For example:
|
||||||
class ListUsers(APIView):
|
class ListUsers(APIView):
|
||||||
"""
|
"""
|
||||||
View to list all users in the system.
|
View to list all users in the system.
|
||||||
|
|
||||||
* Requires token authentication.
|
* Requires token authentication.
|
||||||
* Only admin users are able to access this view.
|
* 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
|
### .permission_classes
|
||||||
|
|
||||||
### .content_negotiation_class
|
### .content_negotiation_class
|
||||||
|
|
||||||
## API policy instantiation methods
|
## API policy instantiation methods
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a class="github" href="viewsets.py"></a>
|
source: viewsets.py
|
||||||
|
|
||||||
# ViewSets
|
# ViewSets
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ body{
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand{
|
.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 {
|
.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{
|
.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{
|
.navbar .navbar-inner .dropdown-menu li a:hover{
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
color: #c20000;
|
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">
|
<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>
|
</p>
|
||||||
|
|
||||||
<!--
|
|
||||||
# Django REST framework
|
|
||||||
-->
|
|
||||||
|
|
||||||
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.
|
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
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):
|
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
questions = serializers.PrimaryKeyRelatedField(many=True)
|
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:
|
class Meta:
|
||||||
model = Track
|
model = Track
|
||||||
fields = ('name', 'duration')
|
fields = ('name', 'duration')
|
||||||
|
|
||||||
class AlbumSerializer(serializer.ModelSerializer):
|
class AlbumSerializer(serializer.ModelSerializer):
|
||||||
tracks = TrackSerializer(many=True)
|
tracks = TrackSerializer(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Album
|
model = Album
|
||||||
fields = ('album_name', 'artist', 'tracks')
|
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.
|
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`.
|
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):
|
class GroupViewSet(viewsets.ModelViewSet):
|
||||||
model = Group
|
model = Group
|
||||||
|
|
||||||
|
|
||||||
# Routers provide an easy way of automatically determining the URL conf
|
# Routers provide an easy way of automatically determining the URL conf
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'users', UserViewSet)
|
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.
|
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.
|
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`.
|
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
|
## 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.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -135,15 +135,15 @@ There are many great Markdown editors that make working with the documentation r
|
||||||
|
|
||||||
## Building the documentation
|
## 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 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
|
## 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.
|
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
|
#### Setting the description
|
||||||
|
|
||||||
|
@ -65,9 +65,9 @@ If the python `markdown` library is installed, then [markdown syntax][markdown]
|
||||||
class AccountListView(views.APIView):
|
class AccountListView(views.APIView):
|
||||||
"""
|
"""
|
||||||
Returns a list of all **active** accounts in the system.
|
Returns a list of all **active** accounts in the system.
|
||||||
|
|
||||||
For more details on how accounts are activated please [see here][ref].
|
For more details on how accounts are activated please [see here][ref].
|
||||||
|
|
||||||
[ref]: http://example.com/activating-accounts
|
[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):
|
def metadata(self, request):
|
||||||
"""
|
"""
|
||||||
Don't include the view description in OPTIONS responses.
|
Don't include the view description in OPTIONS responses.
|
||||||
"""
|
"""
|
||||||
data = super(ExampleView, self).metadata(request)
|
data = super(ExampleView, self).metadata(request)
|
||||||
data.pop('description')
|
data.pop('description')
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -160,4 +160,4 @@ The following individuals made a significant financial contribution to the devel
|
||||||
|
|
||||||
### Supporters
|
### 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: Fix migration in `authtoken` application.
|
||||||
* Bugfix: Allow selection of integer keys in nested choices.
|
* Bugfix: Allow selection of integer keys in nested choices.
|
||||||
* Bugfix: Return `None` instead of `'None'` in `CharField` with `allow_none=True`.
|
* 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.
|
* Bugfix: `DjangoFilterBackend` no longer quietly changes queryset ordering.
|
||||||
|
|
||||||
### 2.4.2
|
### 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.
|
* A declarative serialization API, that mirrors Django's `Forms`/`ModelForms` API.
|
||||||
* Structural concerns are decoupled from encoding concerns.
|
* Structural concerns are decoupled from encoding concerns.
|
||||||
* Able to support rendering and parsing to many formats, including both machine-readable representations and HTML forms.
|
* 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.
|
* 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.
|
* Relationships that can be expressed as primary keys, hyperlinks, slug fields, and other custom representations.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
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.*
|
*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:
|
class Meta:
|
||||||
model = ToDoItem
|
model = ToDoItem
|
||||||
fields = ('text', 'is_completed')
|
fields = ('text', 'is_completed')
|
||||||
|
|
||||||
class ToDoListSerializer(serializers.ModelSerializer):
|
class ToDoListSerializer(serializers.ModelSerializer):
|
||||||
items = ToDoItemSerializer(many=True, read_only=True)
|
items = ToDoItemSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ToDoList
|
model = ToDoList
|
||||||
fields = ('title', 'items')
|
fields = ('title', 'items')
|
||||||
|
@ -31,7 +31,7 @@ Some example output from our serializer.
|
||||||
'items': {
|
'items': {
|
||||||
{'text': 'Compile playlist', 'is_completed': True},
|
{'text': 'Compile playlist', 'is_completed': True},
|
||||||
{'text': 'Send invites', 'is_completed': False},
|
{'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
|
### 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.
|
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
|
## Working with Serializers
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,54 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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 -->
|
<head>
|
||||||
<link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet">
|
<meta charset="utf-8">
|
||||||
<link href="http://www.django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet">
|
<title>Django REST framework - 404 - Page not found</title>
|
||||||
<link href="http://www.django-rest-framework.org/css/default.css" rel="stylesheet">
|
<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 -->
|
<!-- Le styles -->
|
||||||
<!--[if lt IE 9]>
|
<link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet">
|
||||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet">
|
||||||
<![endif]-->
|
<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 || [];
|
<script type="text/javascript">
|
||||||
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
var _gaq = _gaq || [];
|
||||||
_gaq.push(['_trackPageview']);
|
_gaq.push(['_setAccount', 'UA-18852272-2']);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
var ga = document.createElement('script');
|
||||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
ga.type = 'text/javascript';
|
||||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
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>
|
<body onload="prettyPrint()" class="404-page">
|
||||||
</head>
|
|
||||||
<body onload="prettyPrint()" class="404-page">
|
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
|
||||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="container-fluid">
|
<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-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="#">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 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-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">
|
<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>
|
<span class="icon-bar"></span>
|
||||||
|
@ -121,81 +125,92 @@
|
||||||
</li>
|
</li>
|
||||||
-->
|
-->
|
||||||
</ul>
|
</ul>
|
||||||
</div><!--/.nav-collapse -->
|
</div>
|
||||||
|
<!--/.nav-collapse -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body-content">
|
<div class="body-content">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
<!-- Search Modal -->
|
||||||
<!-- Search Modal -->
|
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal-header">
|
||||||
<div class="modal-header">
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<h3 id="myModalLabel">Documentation search</h3>
|
||||||
<h3 id="myModalLabel">Documentation search</h3>
|
</div>
|
||||||
</div>
|
<div class="modal-body">
|
||||||
<div class="modal-body">
|
<!-- Custom google search -->
|
||||||
<!-- Custom google search -->
|
<script>
|
||||||
<script>
|
(function() {
|
||||||
(function() {
|
var cx = '015016005043623903336:rxraeohqk6w';
|
||||||
var cx = '015016005043623903336:rxraeohqk6w';
|
var gcse = document.createElement('script');
|
||||||
var gcse = document.createElement('script');
|
gcse.type = 'text/javascript';
|
||||||
gcse.type = 'text/javascript';
|
gcse.async = true;
|
||||||
gcse.async = true;
|
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
var s = document.getElementsByTagName('script')[0];
|
||||||
var s = document.getElementsByTagName('script')[0];
|
s.parentNode.insertBefore(gcse, s);
|
||||||
s.parentNode.insertBefore(gcse, s);
|
})();
|
||||||
})();
|
</script>
|
||||||
</script>
|
<gcse:search></gcse:search>
|
||||||
<gcse:search></gcse:search>
|
</div>
|
||||||
</div>
|
<div class="modal-footer">
|
||||||
<div class="modal-footer">
|
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div id="main-content" class="span12">
|
<div id="main-content" class="span12">
|
||||||
<h1 id="404-page-not-found" style="text-align: center">404</h1>
|
<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>
|
<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>
|
||||||
</div><!--/row-->
|
<!--/span-->
|
||||||
</div><!--/.fluid-container-->
|
</div>
|
||||||
</div><!--/.body content-->
|
<!--/row-->
|
||||||
|
</div>
|
||||||
|
<!--/.fluid-container-->
|
||||||
|
</div>
|
||||||
|
<!--/.body content-->
|
||||||
|
|
||||||
<div id="push"></div>
|
<div id="push"></div>
|
||||||
</div><!--/.wrapper -->
|
</div>
|
||||||
|
<!--/.wrapper -->
|
||||||
|
|
||||||
<footer class="span12">
|
<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>
|
</footer>
|
||||||
|
|
||||||
<!-- Le javascript
|
<!-- Le javascript
|
||||||
================================================== -->
|
================================================== -->
|
||||||
<!-- Placed at the end of the document so the pages load faster -->
|
<!-- Placed at the end of the document so the pages load faster -->
|
||||||
<script src="http://www.django-rest-framework.org/js/jquery-1.8.1-min.js"></script>
|
<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/prettify-1.0.js"></script>
|
||||||
<script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script>
|
<script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
//$('.side-nav').scrollspy()
|
//$('.side-nav').scrollspy()
|
||||||
var shiftWindow = function() { scrollBy(0, -50) };
|
var shiftWindow = function() {
|
||||||
if (location.hash) shiftWindow();
|
scrollBy(0, -50)
|
||||||
window.addEventListener("hashchange", shiftWindow);
|
};
|
||||||
|
if (location.hash) shiftWindow();
|
||||||
|
window.addEventListener("hashchange", shiftWindow);
|
||||||
|
|
||||||
$('.dropdown-menu').on('click touchstart', function(event) {
|
$('.dropdown-menu').on('click touchstart', function(event) {
|
||||||
event.stopPropagation();
|
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
|
</html>
|
||||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
|
||||||
|
|
||||||
$(function(){
|
|
||||||
$(window).resize(function(){
|
|
||||||
$('.side-nav').css('max-height', window.innerHeight - 130);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body></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
|
|
58
mkdocs.yml
Normal file
58
mkdocs.yml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
pages:
|
||||||
|
- ['index.md', ]
|
||||||
|
- ['tutorial/quickstart.md', ]
|
||||||
|
- ['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', ]
|
||||||
|
- ['api-guide/responses.md', 'API Guide', ]
|
||||||
|
- ['api-guide/views.md', 'API Guide', ]
|
||||||
|
- ['api-guide/generic-views.md', 'API Guide', ]
|
||||||
|
- ['api-guide/viewsets.md', 'API Guide', ]
|
||||||
|
- ['api-guide/routers.md', 'API Guide', ]
|
||||||
|
- ['api-guide/parsers.md', 'API Guide', ]
|
||||||
|
- ['api-guide/renderers.md', 'API Guide', ]
|
||||||
|
- ['api-guide/serializers.md', 'API Guide', ]
|
||||||
|
- ['api-guide/fields.md', 'API Guide', 'Serializer fields']
|
||||||
|
- ['api-guide/relations.md', 'API Guide', 'Serializer relations']
|
||||||
|
- ['api-guide/validators.md', 'API Guide', ]
|
||||||
|
- ['api-guide/authentication.md', 'API Guide', ]
|
||||||
|
- ['api-guide/permissions.md', 'API Guide', ]
|
||||||
|
- ['api-guide/throttling.md', 'API Guide', ]
|
||||||
|
- ['api-guide/filtering.md', 'API Guide', ]
|
||||||
|
- ['api-guide/pagination.md', 'API Guide', ]
|
||||||
|
- ['api-guide/content-negotiation.md', 'API Guide', ]
|
||||||
|
- ['api-guide/format-suffixes.md', 'API Guide', ]
|
||||||
|
- ['api-guide/reverse.md', 'API Guide', 'Returning URLs']
|
||||||
|
- ['api-guide/exceptions.md', 'API Guide', ]
|
||||||
|
- ['api-guide/status-codes.md', 'API Guide', ]
|
||||||
|
- ['api-guide/testing.md', 'API Guide', ]
|
||||||
|
- ['api-guide/settings.md', 'API Guide', ]
|
||||||
|
- ['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']
|
||||||
|
|
||||||
|
site_dir: html
|
||||||
|
theme_dir: docs_theme
|
||||||
|
copyright: Copyright © 2014, <a href='https://twitter.com/_tomchristie'>Tom Christie</a>.
|
||||||
|
google_analytics: ['UA-18852272-2', 'django-rest-framework.org']
|
Loading…
Reference in New Issue
Block a user