diff options
46 files changed, 576 insertions, 686 deletions
| @@ -3,7 +3,7 @@  *~  .* -html/ +site/  htmlcov/  coverage/  build/ diff --git a/.travis.yml b/.travis.yml index 9dd587be..6191e7e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ sudo: false  env:      - TOX_ENV=py27-flake8 +    - TOX_ENV=py27-docs      - TOX_ENV=py34-django17      - TOX_ENV=py33-django17      - TOX_ENV=py32-django17 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b199534..69802995 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,15 +101,15 @@ There are many great markdown editors that make working with the documentation r  ## Building the documentation -To build the documentation, simply run the `mkdocs.py` script. +To build the documentation, install MkDocs with `pip install mkdocs` and then run the following command. -    ./mkdocs.py +    mkdocs build  This will build the html output into the `html` directory. -You can build the documentation and open a preview in a browser window by using the `-p` flag. +You can build the documentation and open a preview in a browser window by using the `serve` command. -    ./mkdocs.py -p +    mkdocs serve  ## Language style diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..4e332899 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +www.django-rest-framework.org diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 01774c10..b04858e3 100755 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -1,4 +1,4 @@ -<a class="github" href="authentication.py"></a> +source: authentication.py  # Authentication diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index 94dd59ca..bc3b09fb 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -1,4 +1,4 @@ -<a class="github" href="negotiation.py"></a> +source: negotiation.py  # Content negotiation @@ -29,7 +29,7 @@ The priorities for each of the given media types would be:  If the requested view was only configured with renderers for `YAML` and `HTML`, then REST framework would select whichever renderer was listed first in the `renderer_classes` list or `DEFAULT_RENDERER_CLASSES` setting. -For more information on the `HTTP Accept` header, see [RFC 2616][accept-header]  +For more information on the `HTTP Accept` header, see [RFC 2616][accept-header]  --- @@ -62,7 +62,7 @@ request when selecting the appropriate parser or renderer.              Select the first parser in the `.parser_classes` list.              """              return parsers[0] -         +          def select_renderer(self, request, renderers, format_suffix):              """              Select the first renderer in the `.renderer_classes` list. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index e61dcfa9..8e0b1958 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -1,4 +1,4 @@ -<a class="github" href="exceptions.py"></a> +source: exceptions.py  # Exceptions diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 292a51d8..354ec966 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -1,4 +1,4 @@ -<a class="github" href="fields.py"></a> +source: fields.py  # Serializer fields @@ -62,7 +62,7 @@ A dictionary of error codes to error messages.  ### `widget`  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` diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index cfeb4334..83977048 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -1,4 +1,4 @@ -<a class="github" href="filters.py"></a> +source: filters.py  # Filtering @@ -26,7 +26,7 @@ For example:      class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer -  +          def get_queryset(self):              """              This view should return a list of all the purchases @@ -38,7 +38,7 @@ For example:  ## Filtering against the URL -Another style of filtering might involve restricting the queryset based on some part of the URL.   +Another style of filtering might involve restricting the queryset based on some part of the URL.  For example if your URL config contained an entry like this: @@ -48,7 +48,7 @@ You could then write a view that returned a purchase queryset filtered by the us      class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer -  +          def get_queryset(self):              """              This view should return a list of all the purchases for @@ -57,7 +57,7 @@ You could then write a view that returned a purchase queryset filtered by the us              username = self.kwargs['username']              return Purchase.objects.filter(purchaser__username=username) -## Filtering against query parameters  +## Filtering against query parameters  A final example of filtering the initial queryset would be to determine the initial queryset based on query parameters in the url. @@ -65,7 +65,7 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/      class PurchaseList(generics.ListAPIView):          serializer_class = PurchaseSerializer -  +          def get_queryset(self):              """              Optionally restricts the returned purchases to a given user, @@ -113,7 +113,7 @@ For instance, given the previous example, and a product with an id of `4675`, th      http://example.com/api/products/4675/?category=clothing&max_price=10.00  ## Overriding the initial queryset -  +  Note that you can use both an overridden `.get_queryset()` and generic filtering together, and everything will work as expected.  For example, if `Product` had a many-to-many relationship with `User`, named `purchase`, you might want to write a view like this:      class PurchasedProductsList(generics.ListAPIView): @@ -124,7 +124,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering          model = Product          serializer_class = ProductSerializer          filter_class = ProductFilter -         +          def get_queryset(self):              user = self.request.user              return user.purchase_set.all() @@ -135,7 +135,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering  ## DjangoFilterBackend -The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter].   +The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter].  To use REST framework's `DjangoFilterBackend`, first install `django-filter`. @@ -216,7 +216,7 @@ This is nice, but it exposes the Django's double underscore convention as part o  And now you can execute:      http://example.com/api/products?manufacturer=foo -     +  For more details on using filter sets see the [django-filter documentation][django-filter-docs].  --- @@ -224,7 +224,7 @@ For more details on using filter sets see the [django-filter documentation][djan  **Hints & Tips**  * By default filtering is not enabled.  If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'DEFAULT_FILTER_BACKENDS'` setting. -* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`.  (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].)  +* When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`.  (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].)  * `django-filter` supports filtering across relationships, using Django's double-underscore syntax.  * For Django 1.3 support, make sure to install `django-filter` version 0.5.4, as later versions drop support for 1.3. @@ -316,7 +316,7 @@ Typically you'd instead control this by setting `order_by` on the initial querys          queryset = User.objects.all()          serializer_class = UserSerializer          filter_backends = (filters.OrderingFilter,) -        ordering = ('username',)  +        ordering = ('username',)  The `ordering` attribute may be either a string or a list/tuple of strings. diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index 76a3367b..20c1e995 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -1,4 +1,4 @@ -<a class="github" href="urlpatterns.py"></a> +source: urlpatterns.py  # Format suffixes @@ -7,7 +7,7 @@ used all the time.  >  > — Roy Fielding, [REST discuss mailing list][cite] -A common pattern for Web APIs is to use filename extensions on URLs to provide an endpoint for a given media type.  For example, 'http://example.com/api/users.json' to serve a JSON representation.  +A common pattern for Web APIs is to use filename extensions on URLs to provide an endpoint for a given media type.  For example, 'http://example.com/api/users.json' to serve a JSON representation.  Adding format-suffix patterns to each individual entry in the URLconf for your API is error-prone and non-DRY, so REST framework provides a shortcut to adding these patterns to your URLConf. @@ -21,7 +21,7 @@ Arguments:  * **urlpatterns**: Required.  A URL pattern list.  * **suffix_required**:  Optional.  A boolean indicating if suffixes in the URLs should be optional or mandatory.  Defaults to `False`, meaning that suffixes are optional by default. -* **allowed**:  Optional.  A list or tuple of valid format suffixes.  If not provided, a wildcard format suffix pattern will be used.  +* **allowed**:  Optional.  A list or tuple of valid format suffixes.  If not provided, a wildcard format suffix pattern will be used.  Example: @@ -33,7 +33,7 @@ Example:          url(r'^comments/$', views.comment_list),          url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)      ] -     +      urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])  When using `format_suffix_patterns`, you must make sure to add the `'format'` keyword argument to the corresponding views.  For example: @@ -56,12 +56,12 @@ The name of the kwarg used may be modified by using the `FORMAT_SUFFIX_KWARG` se  Also note that `format_suffix_patterns` does not support descending into `include` URL patterns.  --- -         +  ## Accept headers vs. format suffixes  There seems to be a view among some of the Web community that filename extensions are not a RESTful pattern, and that `HTTP Accept` headers should always be used instead. -It is actually a misconception.  For example, take the following quote from Roy Fielding discussing the relative merits of query parameter media-type indicators vs. file extension media-type indicators:  +It is actually a misconception.  For example, take the following quote from Roy Fielding discussing the relative merits of query parameter media-type indicators vs. file extension media-type indicators:  “That's why I always prefer extensions.  Neither choice has anything to do with REST.” — Roy Fielding, [REST discuss mailing list][cite2] diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 49a5e58f..648ece82 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -1,5 +1,5 @@ -<a class="github" href="mixins.py"></a> -<a class="github" href="generics.py"></a> +source: mixins.py +        generics.py  # Generic views diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index e57aed1a..9b7086c5 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -1,4 +1,4 @@ -<a class="github" href="pagination.py"></a> +source: pagination.py  # Pagination @@ -6,7 +6,7 @@  >  > — [Django documentation][cite] -REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.  +REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.  ## Paginating basic data @@ -33,7 +33,7 @@ The `context` argument of the `PaginationSerializer` class may optionally includ      request = RequestFactory().get('/foobar')      serializer = PaginationSerializer(instance=page, context={'request': request})      serializer.data -    # {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}     +    # {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}  We could now return that data in a `Response` object, and it would be rendered into the correct media type. diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 72a4af64..42d77b22 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -1,4 +1,4 @@ -<a class="github" href="parsers.py"></a> +source: parsers.py  # Parsers @@ -161,7 +161,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa  ## 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):      """ @@ -197,4 +197,4 @@ The following third party packages are also available.  [juanriaza]: https://github.com/juanriaza  [vbabiy]: https://github.com/vbabiy  [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack -[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
\ No newline at end of file +[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 446e362e..f068f0f7 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -1,4 +1,4 @@ -<a class="github" href="permissions.py"></a> +source: permissions.py  # Permissions @@ -12,7 +12,7 @@ Permission checks are always run at the very start of the view, before any other  ## How permissions are determined -Permissions in REST framework are always defined as a list of permission classes.   +Permissions in REST framework are always defined as a list of permission classes.  Before running the main body of the view each permission in the list is checked.  If any permission check fails an `exceptions.PermissionDenied` exception will be raised, and the main body of the view will not run. @@ -220,9 +220,9 @@ As well as global permissions, that are run against all incoming requests, you c          def has_object_permission(self, request, view, obj):              # Read permissions are allowed to any request,              # so we'll always allow GET, HEAD or OPTIONS requests. -            if request.method in permissions.SAFE_METHODS:             +            if request.method in permissions.SAFE_METHODS:                  return True -     +              # Instance must have an attribute named `owner`.              return obj.owner == request.user diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index d03a75ae..ad981b2b 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -1,4 +1,4 @@ -<a class="github" href="relations.py"></a> +source: relations.py  # Serializer relations @@ -33,7 +33,7 @@ In order to explain the various types of relational fields, we'll use a couple o          class Meta:              unique_together = ('album', 'order')              order_by = 'order' -         +          def __unicode__(self):              return '%d: %s' % (self.order, self.title) @@ -42,10 +42,10 @@ In order to explain the various types of relational fields, we'll use a couple o  `RelatedField` may be used to represent the target of the relationship using its `__unicode__` method.  For example, the following serializer. -  +      class AlbumSerializer(serializers.ModelSerializer):          tracks = serializers.RelatedField(many=True) -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -74,10 +74,10 @@ This field is read only.  `PrimaryKeyRelatedField` may be used to represent the target of the relationship using its primary key.  For example, the following serializer: -  +      class AlbumSerializer(serializers.ModelSerializer):          tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -108,11 +108,11 @@ By default this field is read-write, although you can change this behavior using  `HyperlinkedRelatedField` may be used to represent the target of the relationship using a hyperlink.  For example, the following serializer: -  +      class AlbumSerializer(serializers.ModelSerializer):          tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True,                                                       view_name='track-detail') -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -146,11 +146,11 @@ By default this field is read-write, although you can change this behavior using  `SlugRelatedField` may be used to represent the target of the relationship using a field on the target.  For example, the following serializer: -  +      class AlbumSerializer(serializers.ModelSerializer):          tracks = serializers.SlugRelatedField(many=True, read_only=True,                                                slug_field='title') -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -222,10 +222,10 @@ For example, the following serializer:          class Meta:              model = Track              fields = ('order', 'title') -     +      class AlbumSerializer(serializers.ModelSerializer):          tracks = TrackSerializer(many=True) -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -262,7 +262,7 @@ For, example, we could define a relational field, to serialize a track to a cust      class AlbumSerializer(serializers.ModelSerializer):          tracks = TrackListingField(many=True) -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -302,7 +302,7 @@ If you have not set a related name for the reverse relationship, you'll need to      class AlbumSerializer(serializers.ModelSerializer):          class Meta: -            fields = ('track_set', ...)  +            fields = ('track_set', ...)  See the Django documentation on [reverse relationships][reverse-relationships] for more details. @@ -315,14 +315,14 @@ For example, given the following model for a tag, which has a generic relationsh      class TaggedItem(models.Model):          """          Tags arbitrary model instances using a generic relation. -         +          See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/          """          tag_name = models.SlugField()          content_type = models.ForeignKey(ContentType)          object_id = models.PositiveIntegerField()          tagged_object = GenericForeignKey('content_type', 'object_id') -     +          def __unicode__(self):              return self.tag @@ -353,7 +353,7 @@ We could define a custom field that could be used to serialize tagged instances,          def to_native(self, value):              """              Serialize tagged objects to a simple textual representation. -            """                             +            """              if isinstance(value, Bookmark):                  return 'Bookmark: ' + value.url              elif isinstance(value, Note): @@ -366,7 +366,7 @@ If you need the target of the relationship to have a nested representation, you              """              Serialize bookmark instances using a bookmark serializer,              and note instances using a note serializer. -            """                             +            """              if isinstance(value, Bookmark):                  serializer = BookmarkSerializer(value)              elif isinstance(value, Note): @@ -391,7 +391,7 @@ to ``True``.  ## Advanced Hyperlinked fields -If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.  +If you have very specific requirements for the style of your hyperlinked relationships you can override `HyperlinkedRelatedField`.  There are two methods you'll need to override. @@ -411,7 +411,7 @@ May raise an `ObjectDoesNotExist` exception.  ### Example -For example, if all your object URLs used both a account and a slug in the the URL to reference the object, you might create a custom field like this:  +For example, if all your object URLs used both a account and a slug in the the URL to reference the object, you might create a custom field like this:      class CustomHyperlinkedField(serializers.HyperlinkedRelatedField):          def get_url(self, obj, view_name, request, format): diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index db7436c2..035ec1d2 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -1,4 +1,4 @@ -<a class="github" href="renderers.py"></a> +source: renderers.py  # Renderers @@ -115,7 +115,7 @@ The `jsonp` approach is essentially a browser hack, and is [only appropriate for  ## YAMLRenderer -Renders the request data into `YAML`.  +Renders the request data into `YAML`.  Requires the `pyyaml` package to be installed. @@ -131,7 +131,7 @@ Note that non-ascii characters will be rendered using `\uXXXX` character escape.  ## UnicodeYAMLRenderer -Renders the request data into `YAML`.  +Renders the request data into `YAML`.  Requires the `pyyaml` package to be installed. @@ -184,7 +184,7 @@ An example of a view that uses `TemplateHTMLRenderer`:          def get(self, request, *args, **kwargs):              self.object = self.get_object()              return Response({'user': self.object}, template_name='user_detail.html') -  +  You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.  If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. @@ -205,7 +205,7 @@ An example of a view that uses `TemplateHTMLRenderer`:      @api_view(('GET',))      @renderer_classes((StaticHTMLRenderer,)) -    def simple_html_view(request):  +    def simple_html_view(request):          data = '<html><body><h1>Hello, world</h1></body></html>'          return Response(data) @@ -300,7 +300,7 @@ The following is an example plaintext renderer that will return a response with      class PlainTextRenderer(renderers.BaseRenderer):          media_type = 'text/plain'          format = 'txt' -         +          def render(self, data, media_type=None, renderer_context=None):              return data.encode(self.charset) @@ -340,7 +340,7 @@ You can do some pretty flexible things using REST framework's renderers.  Some e  * Provide either flat or nested representations from the same endpoint, depending on the requested media type.  * Serve both regular HTML webpages, and JSON based API responses from the same endpoints.  * Specify multiple types of HTML representation for API clients to use. -* Underspecify a renderer's media type, such as using `media_type = 'image/*'`, and use the `Accept` header to vary the encoding of the response.  +* Underspecify a renderer's media type, such as using `media_type = 'image/*'`, and use the `Accept` header to vary the encoding of the response.  ## Varying behaviour by media type diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 87425ed1..8713fa2a 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -1,4 +1,4 @@ -<a class="github" href="request.py"></a> +source: request.py  # 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. -For more information see the [browser enhancements documentation].     +For more information see the [browser enhancements documentation].  ## .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. -For more information see the [browser enhancements documentation].     +For more information see the [browser enhancements documentation].  ## .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. -For more information see the [browser enhancements documentation].     +For more information see the [browser enhancements documentation].  --- diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 5a42aa92..97f31271 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -1,4 +1,4 @@ -<a class="github" href="response.py"></a> +source: response.py  # Responses @@ -90,6 +90,6 @@ The `Response` class extends `SimpleTemplateResponse`, and all the usual attribu  As with any other `TemplateResponse`, this method is called to render the serialized data of the response into the final response content.  When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance.  You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle. -     +  [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/  [statuscodes]: status-codes.md diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 383eca4c..71fb83f9 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -1,4 +1,4 @@ -<a class="github" href="reverse.py"></a> +source: reverse.py  # Returning URLs @@ -30,7 +30,7 @@ You should **include the request as a keyword argument** to the function, for ex      from rest_framework.reverse import reverse      from rest_framework.views import APIView  	from django.utils.timezone import now -    +  	class APIRootView(APIView):  	    def get(self, request):  	        year = now().year diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 61a476b8..080230fa 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -1,4 +1,4 @@ -<a class="github" href="routers.py"></a> +source: routers.py  # Routers @@ -56,10 +56,10 @@ For example, given a method like this on the `UserViewSet` class:      from myapp.permissions import IsAdminOrIsSelf      from rest_framework.decorators import detail_route -     +      class UserViewSet(ModelViewSet):          ... -         +          @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])          def set_password(self, request, pk=None):              ... @@ -228,7 +228,7 @@ For another example of setting the `.routes` attribute, see the source code for  ## Advanced custom routers -If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method.  The method should inspect the registered viewsets and return a list of URL patterns.  The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.   +If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method.  The method should inspect the registered viewsets and return a list of URL patterns.  The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.  You may also want to override the `get_default_base_name(self, viewset)` method, or else always explicitly set the `base_name` argument when registering your viewsets with the router. diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index eeeffa13..2d0ff79a 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1,4 +1,4 @@ -<a class="github" href="serializers.py"></a> +source: serializers.py  # Serializers @@ -21,7 +21,7 @@ Let's start by creating a simple object we can use for example purposes:              self.email = email              self.content = content              self.created = created or datetime.datetime.now() -     +      comment = Comment(email='leila@example.com', content='foo bar')  We'll declare a serializer that we can use to serialize and deserialize `Comment` objects. @@ -45,7 +45,7 @@ Declaring a serializer looks very similar to declaring a form:                  instance.content = attrs.get('content', instance.content)                  instance.created = attrs.get('created', instance.created)                  return instance -            return Comment(**attrs)  +            return Comment(**attrs)  The first part of serializer class defines the fields that get serialized/deserialized.  The `restore_object` method defines how fully fledged instances get created when deserializing data. @@ -83,8 +83,8 @@ If you need to customize the serialized value of a particular field, you can do  These methods are essentially the reverse of `validate_<fieldname>` (see *Validation* below.)  ## Deserializing objects -         -Deserialization is similar.  First we parse a stream into Python native datatypes...  + +Deserialization is similar.  First we parse a stream into Python native datatypes...      from StringIO import StringIO      from rest_framework.parsers import JSONParser @@ -174,7 +174,7 @@ To save the deserialized objects created by a serializer, call the `.save()` met  The default behavior of the method is to simply call `.save()` on the deserialized object instance.  You can override the default save behaviour by overriding the `.save_object(obj)` method on the serializer class. -The generic views provided by REST framework call the `.save()` method when updating or creating entities.   +The generic views provided by REST framework call the `.save()` method when updating or creating entities.  ## Dealing with nested objects @@ -288,12 +288,12 @@ By default the serializer class will use the `id` key on the incoming data to de          slug = serializers.CharField(max_length=100)          created = serializers.DateTimeField()          ...  # Various other fields -         +          def get_identity(self, data):              """              This hook is required for bulk update.              We need to override the default, to use the slug as the identity. -             +              Note that the data has not yet been validated at this point,              so we need to deal gracefully with incorrect datatypes.              """ @@ -361,7 +361,7 @@ The `depth` option should be set to an integer value that indicates the depth of  If you want to customize the way the serialization is done (e.g. using `allow_add_remove`) you'll need to define the field yourself. -## Specifying which fields should be read-only  +## Specifying which fields should be read-only  You may wish to specify multiple fields as read-only.  Instead of adding each field explicitly with the `read_only=True` attribute, you may use the `read_only_fields` Meta option, like so: @@ -371,9 +371,9 @@ You may wish to specify multiple fields as read-only.  Instead of adding each fi              fields = ('id', 'account_name', 'users', 'created')              read_only_fields = ('account_name',) -Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.  +Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option. -## Specifying which fields should be write-only  +## Specifying which fields should be write-only  You may wish to specify multiple fields as write-only.  Instead of adding each field explicitly with the `write_only=True` attribute, you may use the `write_only_fields` Meta option, like so: @@ -387,12 +387,12 @@ You may wish to specify multiple fields as write-only.  Instead of adding each f              """              Instantiate a new User instance.              """ -            assert instance is None, 'Cannot update users with CreateUserSerializer'                                 +            assert instance is None, 'Cannot update users with CreateUserSerializer'              user = User(email=attrs['email'], username=attrs['username'])              user.set_password(attrs['password'])              return user -  -## Specifying fields explicitly  + +## Specifying fields explicitly  You can add extra fields to a `ModelSerializer` or override the default fields by declaring fields on the class, just as you would for a `Serializer` class. @@ -524,10 +524,10 @@ For example, if you wanted to be able to set which fields should be used by a se          def __init__(self, *args, **kwargs):              # Don't pass the 'fields' arg up to the superclass              fields = kwargs.pop('fields', None) -             +              # Instantiate the superclass normally              super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) -     +              if fields:                  # Drop any fields that are not specified in the `fields` argument.                  allowed = set(fields) @@ -550,7 +550,7 @@ This would then allow you to do the following:  ## Customising the default fields -The `field_mapping` attribute is a dictionary that maps model classes to serializer classes.  Overriding the attribute will let you set a different set of default serializer classes.  +The `field_mapping` attribute is a dictionary that maps model classes to serializer classes.  Overriding the attribute will let you set a different set of default serializer classes.  For more advanced customization than simply changing the default serializer class you can override various `get_<field_type>_field` methods.  Doing so will allow you to customize the arguments that each serializer field is initialized with.  Each of these methods may either return a field or serializer instance, or `None`. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 96d715ea..0aa4b6a9 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -1,4 +1,4 @@ -<a class="github" href="settings.py"></a> +source: settings.py  # Settings diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 64c46434..d81e092c 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -1,4 +1,4 @@ -<a class="github" href="status.py"></a> +source: status.py  # Status Codes @@ -27,7 +27,7 @@ The module also includes a set of helper functions for testing if a status code  	        url = reverse('index')  	        response = self.client.get(url)  	        self.assertTrue(status.is_success(response.status_code)) -         +  For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616]  and [RFC 6585][rfc6585]. @@ -51,7 +51,7 @@ This class of status code indicates that the client's request was successfully r      HTTP_205_RESET_CONTENT      HTTP_206_PARTIAL_CONTENT -## Redirection - 3xx  +## Redirection - 3xx  This class of status code indicates that further action needs to be taken by the user agent in order to fulfill the request. diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 72c33961..d059fdab 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -1,4 +1,4 @@ -<a class="github" href="test.py"></a> +source: test.py  # Testing @@ -170,7 +170,7 @@ This can be a useful shortcut if you're testing the API but don't want to have t  To unauthenticate subsequent requests, call `force_authenticate` setting the user and/or token to `None`. -    client.force_authenticate(user=None)  +    client.force_authenticate(user=None)  ## CSRF validation @@ -197,7 +197,7 @@ You can use any of REST framework's test case classes as you would for the regul      from django.core.urlresolvers import reverse      from rest_framework import status -    from rest_framework.test import APITestCase  +    from rest_framework.test import APITestCase      class AccountTests(APITestCase):          def test_create_account(self): diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 147c16ff..3f668867 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -1,4 +1,4 @@ -<a class="github" href="throttling.py"></a> +source: throttling.py  # Throttling @@ -83,7 +83,7 @@ The throttle classes provided by REST framework use Django's cache backend.  You  If you need to use a cache other than `'default'`, you can do so by creating a custom throttle class and setting the `cache` attribute.  For example:      class CustomAnonRateThrottle(AnonRateThrottle): -        cache = get_cache('alternate')  +        cache = get_cache('alternate')  You'll need to remember to also set your custom throttle class in the `'DEFAULT_THROTTLE_CLASSES'` settings key, or using the `throttle_classes` view attribute. @@ -147,15 +147,15 @@ For example, given the following views...      class ContactListView(APIView):          throttle_scope = 'contacts'          ... -     +      class ContactDetailView(ApiView):          throttle_scope = 'contacts'          ... -    class UploadView(APIView):         +    class UploadView(APIView):          throttle_scope = 'uploads'          ... -     +  ...and the following settings.      REST_FRAMEWORK = { diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 194a7a6b..31c62682 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,4 +1,5 @@ -<a class="github" href="decorators.py"></a> <a class="github" href="views.py"></a> +source: decorators.py +        views.py  # Class Based Views @@ -26,7 +27,7 @@ For example:      class ListUsers(APIView):          """          View to list all users in the system. -         +          * Requires token authentication.          * Only admin users are able to access this view.          """ @@ -54,7 +55,7 @@ The following attributes control the pluggable aspects of API views.  ### .permission_classes -### .content_negotiation_class  +### .content_negotiation_class  ## API policy instantiation methods diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index 9030e3ee..9249d875 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -1,4 +1,4 @@ -<a class="github" href="viewsets.py"></a> +source: viewsets.py  # ViewSets diff --git a/docs/css/default.css b/docs/css/default.css index 7f3acfed..8c9cd536 100644 --- a/docs/css/default.css +++ b/docs/css/default.css @@ -181,7 +181,7 @@ body{  }  .navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand{ -  color: white;  +  color: white;  }  .nav-list > .active > a, .navbar .navbar-inner .nav li a:hover { @@ -190,8 +190,20 @@ body{  }  .navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li{ - color: #A30000;  + color: #A30000; +} + +.dropdown-menu .active > a, +.dropdown-menu .active > a:hover { +  background-image: none;  } + +.navbar-inverse .nav .dropdown .active > a, +.navbar-inverse .nav .dropdown .active > a:hover, +.navbar-inverse .nav .dropdown .active > a:focus { +  background-color: #eeeeee; +} +  .navbar .navbar-inner .dropdown-menu li a:hover{    background: #eeeeee;    color: #c20000; diff --git a/docs/index.md b/docs/index.md index 6288efa3..ca10befe 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,9 +26,6 @@  <img alt="Django REST Framework" title="Logo by Jake 'Sid' Smith" src="img/logo.png" width="600px" style="display: block; margin: 0 auto 0 auto">  </p> -<!-- -# Django REST framework --->  Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs. diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index a91fb978..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -markdown>=2.1.0 diff --git a/docs/template.html b/docs/template.html deleted file mode 100644 index f36cffc6..00000000 --- a/docs/template.html +++ /dev/null @@ -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> diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md index a997c782..1df52cff 100644 --- a/docs/topics/2.2-announcement.md +++ b/docs/topics/2.2-announcement.md @@ -42,7 +42,7 @@ The 2.2 release makes a few changes to the API, in order to make it more consist  The `ManyRelatedField()` style is being deprecated in favor of a new `RelatedField(many=True)` syntax. -For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:  +For example, if a user is associated with multiple questions, which we want to represent using a primary key relationship, we might use something like the following:      class UserSerializer(serializers.HyperlinkedModelSerializer):          questions = serializers.PrimaryKeyRelatedField(many=True) @@ -58,10 +58,10 @@ The change also applies to serializers.  If you have a nested serializer, you sh          class Meta:              model = Track              fields = ('name', 'duration') -     +      class AlbumSerializer(serializer.ModelSerializer):          tracks = TrackSerializer(many=True) -         +          class Meta:              model = Album              fields = ('album_name', 'artist', 'tracks') @@ -87,7 +87,7 @@ For example, is a user account has an optional foreign key to a company, that yo  This is in line both with the rest of the serializer fields API, and with Django's `Form` and `ModelForm` API. -Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.  +Using `required` throughout the serializers API means you won't need to consider if a particular field should take `blank` or `null` arguments instead of `required`, and also means there will be more consistent behavior for how fields are treated when they are not present in the incoming data.  The `null=True` argument will continue to function, and will imply `required=False`, but will raise a `PendingDeprecationWarning`. diff --git a/docs/topics/2.3-announcement.md b/docs/topics/2.3-announcement.md index 7c800afa..9c9f3e9f 100644 --- a/docs/topics/2.3-announcement.md +++ b/docs/topics/2.3-announcement.md @@ -27,7 +27,7 @@ As an example of just how simple REST framework APIs can now be, here's an API w      class GroupViewSet(viewsets.ModelViewSet):          model = Group -     +      # Routers provide an easy way of automatically determining the URL conf      router = routers.DefaultRouter()      router.register(r'users', UserViewSet) @@ -197,13 +197,13 @@ Usage of the old-style attributes continues to be supported, but will raise a `P  For most cases APIs using model fields will behave as previously, however if you are using a custom renderer, not provided by REST framework, then you may now need to add support for rendering `Decimal` instances to your renderer implementation. -## ModelSerializers and reverse relationships  +## ModelSerializers and reverse relationships  The support for adding reverse relationships to the `fields` option on a `ModelSerializer` class means that the `get_related_field` and `get_nested_field` method signatures have now changed.  In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now `(self, model_field, related_model, to_many)`.  For reverse relationships `model_field` will be `None`. -The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.  +The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.  ## View names and descriptions @@ -211,7 +211,7 @@ The mechanics of how the names and descriptions used in the browseable API are g  If you've been customizing this behavior, for example perhaps to use `rst` markup for the browseable API, then you'll need to take a look at the implementation to see what updates you need to make. -Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.  +Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.  --- diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index 52f6e287..c7991a0f 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -135,15 +135,15 @@ There are many great Markdown editors that make working with the documentation r  ## Building the documentation -To build the documentation, simply run the `mkdocs.py` script. +To build the documentation, install MkDocs with `pip install mkdocs` and then run the following command. -    ./mkdocs.py +    mkdocs build  This will build the html output into the `html` directory. -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 diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index e20f9712..d65e251f 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -54,7 +54,7 @@ The title that is used in the browsable API is generated from the view class nam  For example, the view `UserListView`, will be named `User List` when presented in the browsable API. -When working with viewsets, an appropriate suffix is appended to each generated view.  For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.  +When working with viewsets, an appropriate suffix is appended to each generated view.  For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.  #### Setting the description @@ -65,9 +65,9 @@ If the python `markdown` library is installed, then [markdown syntax][markdown]      class AccountListView(views.APIView):          """          Returns a list of all **active** accounts in the system. -         +          For more details on how accounts are activated please [see here][ref]. -         +          [ref]: http://example.com/activating-accounts          """ @@ -84,7 +84,7 @@ You can modify the response behavior to `OPTIONS` requests by overriding the `me      def metadata(self, request):          """          Don't include the view description in OPTIONS responses. -        """  +        """          data = super(ExampleView, self).metadata(request)          data.pop('description')          return data diff --git a/docs/topics/kickstarter-announcement.md b/docs/topics/kickstarter-announcement.md index 7d1f6d0e..e8bad95b 100644 --- a/docs/topics/kickstarter-announcement.md +++ b/docs/topics/kickstarter-announcement.md @@ -160,4 +160,4 @@ The following individuals made a significant financial contribution to the devel  ### Supporters -There were also almost 300 further individuals choosing to help fund the project at other levels or choosing to give anonymously. Again, thank you, thank you, thank you!
\ No newline at end of file +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! diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 88780c3f..9fca949a 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -63,7 +63,7 @@ You can determine your currently installed version using `pip freeze`:  * Bugfix: Fix migration in `authtoken` application.  * Bugfix: Allow selection of integer keys in nested choices.  * Bugfix: Return `None` instead of `'None'` in `CharField` with `allow_none=True`. -* Bugfix: Ensure custom model fields map to equivelent serializer fields more reliably.  +* Bugfix: Ensure custom model fields map to equivelent serializer fields more reliably.  * Bugfix: `DjangoFilterBackend` no longer quietly changes queryset ordering.  ### 2.4.2 diff --git a/docs/topics/rest-framework-2-announcement.md b/docs/topics/rest-framework-2-announcement.md index f1060d90..a7746932 100644 --- a/docs/topics/rest-framework-2-announcement.md +++ b/docs/topics/rest-framework-2-announcement.md @@ -8,7 +8,7 @@ What it is, and why you should care.  --- -**Announcement:** REST framework 2 released - Tue 30th Oct 2012  +**Announcement:** REST framework 2 released - Tue 30th Oct 2012  --- @@ -37,7 +37,7 @@ REST framework 2 includes a totally re-worked serialization engine, that was ini  * A declarative serialization API, that mirrors Django's `Forms`/`ModelForms` API.  * Structural concerns are decoupled from encoding concerns.  * Able to support rendering and parsing to many formats, including both machine-readable representations and HTML forms. -* Validation that can be mapped to obvious and comprehensive error responses.  +* Validation that can be mapped to obvious and comprehensive error responses.  * Serializers that support both nested, flat, and partially-nested representations.  * Relationships that can be expressed as primary keys, hyperlinks, slug fields, and other custom representations. diff --git a/docs/topics/writable-nested-serializers.md b/docs/topics/writable-nested-serializers.md index abc6a82f..ed614bd2 100644 --- a/docs/topics/writable-nested-serializers.md +++ b/docs/topics/writable-nested-serializers.md @@ -8,7 +8,7 @@ Although flat data structures serve to properly delineate between the individual  Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go.  However, there are a few more subtleties to using writable nested serializers, due to the dependencies between the various model instances, and the need to save or delete multiple instances in a single action. -## One-to-many data structures  +## One-to-many data structures  *Example of a **read-only** nested serializer.  Nothing complex to worry about here.* @@ -16,10 +16,10 @@ Nested data structures are easy enough to work with if they're read-only - simpl  	    class Meta:  	        model = ToDoItem  	        fields = ('text', 'is_completed') -	 +  	class ToDoListSerializer(serializers.ModelSerializer):  	    items = ToDoItemSerializer(many=True, read_only=True) -	 +  	    class Meta:  	        model = ToDoList  	        fields = ('title', 'items') @@ -31,7 +31,7 @@ Some example output from our serializer.          'items': {              {'text': 'Compile playlist', 'is_completed': True},              {'text': 'Send invites', 'is_completed': False}, -            {'text': 'Clean house', 'is_completed': False}             +            {'text': 'Clean house', 'is_completed': False}          }      } @@ -44,4 +44,4 @@ Let's take a look at updating our nested one-to-many data structure.  ### Making PATCH requests -[cite]: http://jsonapi.org/format/#url-based-json-api
\ No newline at end of file +[cite]: http://jsonapi.org/format/#url-based-json-api diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index db5b9ea7..f9027b68 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -134,7 +134,7 @@ A serializer class is very similar to a Django `Form` class, and includes simila  The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The `style={'type': 'textarea'}` flag above is equivelent to using `widget=widgets.Textarea` on a Django `Form` class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial. -We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.   +We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.  ## Working with Serializers diff --git a/docs/404.html b/docs_theme/404.html index 864247e7..44993e37 100644 --- a/docs/404.html +++ b/docs_theme/404.html @@ -1,50 +1,54 @@  <!DOCTYPE html>  <html lang="en"> -<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> -    <meta charset="utf-8"> -    <title>Django REST framework - 404 - Page not found</title> -    <link href="http://www.django-rest-framework.org/img/favicon.ico" rel="icon" type="image/x-icon"> -    <link rel="canonical" href="http://www.django-rest-framework.org/404"/> -    <meta name="viewport" content="width=device-width, initial-scale=1.0"> -    <meta name="description" content="Django, API, REST, 404 - Page not found"> -    <meta name="author" content="Tom Christie"> - -    <!-- Le styles --> -    <link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet"> -    <link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet"> -    <link href="http://www.django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet"> -    <link href="http://www.django-rest-framework.org/css/default.css" rel="stylesheet"> - -    <!-- 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> -  </head> -  <body onload="prettyPrint()" class="404-page"> + +<head> +  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +  <meta charset="utf-8"> +  <title>Django REST framework - 404 - Page not found</title> +  <link href="http://www.django-rest-framework.org/img/favicon.ico" rel="icon" type="image/x-icon"> +  <link rel="canonical" href="http://www.django-rest-framework.org/404" /> +  <meta name="viewport" content="width=device-width, initial-scale=1.0"> +  <meta name="description" content="Django, API, REST, 404 - Page not found"> +  <meta name="author" content="Tom Christie"> + +  <!-- Le styles --> +  <link href="http://www.django-rest-framework.org/css/prettify.css" rel="stylesheet"> +  <link href="http://www.django-rest-framework.org/css/bootstrap.css" rel="stylesheet"> +  <link href="http://www.django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet"> +  <link href="http://www.django-rest-framework.org/css/default.css" rel="stylesheet"> + +  <!-- 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> +</head> + +<body onload="prettyPrint()" class="404-page">    <div class="wrapper">      <div class="navbar navbar-inverse navbar-fixed-top">        <div class="navbar-inner">          <div class="container-fluid"> -            <a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a> -            <a class="repo-link btn btn-inverse btn-small disabled" href="#">Next <i class="icon-arrow-right icon-white"></i></a> -            <a class="repo-link btn btn-inverse btn-small disabled" href="#"><i class="icon-arrow-left icon-white"></i> Previous</a> -            <a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a> +          <a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a> +          <a class="repo-link btn btn-inverse btn-small disabled" href="#">Next <i class="icon-arrow-right icon-white"></i></a> +          <a class="repo-link btn btn-inverse btn-small disabled" href="#"><i class="icon-arrow-left icon-white"></i> Previous</a> +          <a class="repo-link btn btn-inverse btn-small" href="#searchModal" data-toggle="modal"><i class="icon-search icon-white"></i> Search</a>            <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">              <span class="icon-bar"></span>              <span class="icon-bar"></span> @@ -121,81 +125,92 @@                </li>              -->              </ul> -          </div><!--/.nav-collapse --> +          </div> +          <!--/.nav-collapse -->          </div>        </div>      </div>      <div class="body-content">        <div class="container-fluid"> - -<!-- Search Modal --> -<div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> -  <div class="modal-header"> -    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> -    <h3 id="myModalLabel">Documentation search</h3> -  </div> -  <div class="modal-body"> -    <!-- Custom google search --> -    <script> -      (function() { -        var cx = '015016005043623903336:rxraeohqk6w'; -        var gcse = document.createElement('script'); -        gcse.type = 'text/javascript'; -        gcse.async = true; -        gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + -            '//www.google.com/cse/cse.js?cx=' + cx; -        var s = document.getElementsByTagName('script')[0]; -        s.parentNode.insertBefore(gcse, s); -      })(); -    </script> -    <gcse:search></gcse:search> -  </div> -  <div class="modal-footer"> -    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> -  </div> -</div> +        <!-- Search Modal --> +        <div id="searchModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> +          <div class="modal-header"> +            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> +            <h3 id="myModalLabel">Documentation search</h3> +          </div> +          <div class="modal-body"> +            <!-- Custom google search --> +            <script> +              (function() { +                var cx = '015016005043623903336:rxraeohqk6w'; +                var gcse = document.createElement('script'); +                gcse.type = 'text/javascript'; +                gcse.async = true; +                gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + +                  '//www.google.com/cse/cse.js?cx=' + cx; +                var s = document.getElementsByTagName('script')[0]; +                s.parentNode.insertBefore(gcse, s); +              })(); +            </script> +            <gcse:search></gcse:search> +          </div> +          <div class="modal-footer"> +            <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> +          </div> +        </div>          <div class="row-fluid">            <div id="main-content" class="span12">              <h1 id="404-page-not-found" style="text-align: center">404</h1> -            <p style="text-align: center"><strong>Page not found</strong></p> +            <p style="text-align: center"><strong>Page not found</strong> +            </p>              <p style="text-align: center">Try the <a href="http://www.django-rest-framework.org/">homepage</a>, or <a href="#searchModal" data-toggle="modal">search the documentation</a>.</p> -          </div><!--/span--> -        </div><!--/row--> -      </div><!--/.fluid-container--> -    </div><!--/.body content--> +          </div> +          <!--/span--> +        </div> +        <!--/row--> +      </div> +      <!--/.fluid-container--> +    </div> +    <!--/.body content--> -      <div id="push"></div> -  </div><!--/.wrapper --> +    <div id="push"></div> +  </div> +  <!--/.wrapper -->    <footer class="span12"> -    <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a></p> +    <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</a> +    </p>    </footer> -    <!-- Le javascript +  <!-- Le javascript      ================================================== --> -    <!-- Placed at the end of the document so the pages load faster --> -    <script src="http://www.django-rest-framework.org/js/jquery-1.8.1-min.js"></script> -    <script src="http://www.django-rest-framework.org/js/prettify-1.0.js"></script> -    <script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script> -    <script> -      //$('.side-nav').scrollspy() -      var shiftWindow = function() { scrollBy(0, -50) }; -      if (location.hash) shiftWindow(); -      window.addEventListener("hashchange", shiftWindow); - -      $('.dropdown-menu').on('click touchstart', function(event) { -        event.stopPropagation(); +  <!-- Placed at the end of the document so the pages load faster --> +  <script src="http://www.django-rest-framework.org/js/jquery-1.8.1-min.js"></script> +  <script src="http://www.django-rest-framework.org/js/prettify-1.0.js"></script> +  <script src="http://www.django-rest-framework.org/js/bootstrap-2.1.1-min.js"></script> +  <script> +    //$('.side-nav').scrollspy() +    var shiftWindow = function() { +      scrollBy(0, -50) +    }; +    if (location.hash) shiftWindow(); +    window.addEventListener("hashchange", shiftWindow); + +    $('.dropdown-menu').on('click touchstart', function(event) { +      event.stopPropagation(); +    }); + +    // Dynamically force sidenav to no higher than browser window +    $('.side-nav').css('max-height', window.innerHeight - 130); + +    $(function() { +      $(window).resize(function() { +        $('.side-nav').css('max-height', window.innerHeight - 130);        }); +    }); +  </script> +</body> -      // Dynamically force sidenav to no higher than browser window -      $('.side-nav').css('max-height', window.innerHeight - 130); - -      $(function(){ -        $(window).resize(function(){ -          $('.side-nav').css('max-height', window.innerHeight - 130); -        }); -      }); -    </script> -</body></html> +</html> diff --git a/docs_theme/base.html b/docs_theme/base.html new file mode 100644 index 00000000..6bfccab2 --- /dev/null +++ b/docs_theme/base.html @@ -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> diff --git a/docs_theme/nav.html b/docs_theme/nav.html new file mode 100644 index 00000000..ca1afc0e --- /dev/null +++ b/docs_theme/nav.html @@ -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> diff --git a/mkdocs.py b/mkdocs.py deleted file mode 100755 index 3787d792..00000000 --- a/mkdocs.py +++ /dev/null @@ -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 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..f956ecf0 --- /dev/null +++ b/mkdocs.yml @@ -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 + +theme_dir: docs_theme + +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'] + +copyright: Copyright © 2014, <a href='https://twitter.com/_tomchristie'>Tom Christie</a>. +google_analytics: ['UA-18852272-2', 'django-rest-framework.org'] @@ -29,3 +29,8 @@ deps =         pytest==2.5.2         flake8==2.2.2  commands = ./runtests.py --lintonly + +[testenv:py27-docs] +deps = +       mkdocs>=0.11.1 +commands = mkdocs build | 
