diff options
| author | Tom Christie | 2013-12-13 16:32:34 +0000 | 
|---|---|---|
| committer | Tom Christie | 2013-12-13 16:32:34 +0000 | 
| commit | 9c41c007afc71c899306bcb02e40bdfc36b09146 (patch) | |
| tree | ca0da04aed0c1b96ddf14a801dc54b5a72a72461 /docs | |
| parent | ed931b90ae9e72f963673e6e188b1802a5a65360 (diff) | |
| parent | ca244ad614e2f6fb4fef1dc9987be996d2624303 (diff) | |
| download | django-rest-framework-9c41c007afc71c899306bcb02e40bdfc36b09146.tar.bz2 | |
Merge branch 'master' into 2.4.0
Conflicts:
	.travis.yml
	docs/api-guide/routers.md
	docs/topics/release-notes.md
	rest_framework/compat.py
Diffstat (limited to 'docs')
29 files changed, 583 insertions, 84 deletions
| diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 00000000..4938da6e --- /dev/null +++ b/docs/404.html @@ -0,0 +1,201 @@ +<!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://django-rest-framework.org/img/favicon.ico" rel="icon" type="image/x-icon"> +    <link rel="canonical" href="http://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://django-rest-framework.org/css/prettify.css" rel="stylesheet"> +    <link href="http://django-rest-framework.org/css/bootstrap.css" rel="stylesheet"> +    <link href="http://django-rest-framework.org/css/bootstrap-responsive.css" rel="stylesheet"> +    <link href="http://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="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://django-rest-framework.org">Django REST framework</a> +          <div class="nav-collapse collapse"> +            <ul class="nav"> +              <li><a href="http://django-rest-framework.org">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="http://django-rest-framework.org/tutorial/quickstart">Quickstart</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/1-serialization">1 - Serialization</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/2-requests-and-responses">2 - Requests and responses</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/3-class-based-views">3 - Class based views</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/4-authentication-and-permissions">4 - Authentication and permissions</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis">5 - Relationships and hyperlinked APIs</a></li> +                  <li><a href="http://django-rest-framework.org/tutorial/6-viewsets-and-routers">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="http://django-rest-framework.org/api-guide/requests">Requests</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/responses">Responses</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/views">Views</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/generic-views">Generic views</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/viewsets">Viewsets</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/routers">Routers</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/parsers">Parsers</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/renderers">Renderers</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/serializers">Serializers</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/fields">Serializer fields</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/relations">Serializer relations</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/authentication">Authentication</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/permissions">Permissions</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/throttling">Throttling</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/filtering">Filtering</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/pagination">Pagination</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/content-negotiation">Content negotiation</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/format-suffixes">Format suffixes</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/reverse">Returning URLs</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/exceptions">Exceptions</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/status-codes">Status codes</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/testing">Testing</a></li> +                  <li><a href="http://django-rest-framework.org/api-guide/settings">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="http://django-rest-framework.org/topics/documenting-your-api">Documenting your API</a></li> +                  <li><a href="http://django-rest-framework.org/topics/ajax-csrf-cors">AJAX, CSRF & CORS</a></li> +                  <li><a href="http://django-rest-framework.org/topics/browser-enhancements">Browser enhancements</a></li> +                  <li><a href="http://django-rest-framework.org/topics/browsable-api">The Browsable API</a></li> +                  <li><a href="http://django-rest-framework.org/topics/rest-hypermedia-hateoas">REST, Hypermedia & HATEOAS</a></li> +                  <li><a href="http://django-rest-framework.org/topics/rest-framework-2-announcement">2.0 Announcement</a></li> +                  <li><a href="http://django-rest-framework.org/topics/2.2-announcement">2.2 Announcement</a></li> +                  <li><a href="http://django-rest-framework.org/topics/2.3-announcement">2.3 Announcement</a></li> +                  <li><a href="http://django-rest-framework.org/topics/release-notes">Release Notes</a></li> +                  <li><a href="http://django-rest-framework.org/topics/credits">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 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">Try the <a href="http://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 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="http://django-rest-framework.org/js/jquery-1.8.1-min.js"></script> +    <script src="http://django-rest-framework.org/js/prettify-1.0.js"></script> +    <script src="http://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></html> diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 7caeac1e..53efc49a 100755 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -162,10 +162,12 @@ The `curl` command line tool may be useful for testing token authenticated APIs.  If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal. +    from django.contrib.auth import get_user_model +    from django.db.models.signals import post_save      from django.dispatch import receiver      from rest_framework.authtoken.models import Token -    @receiver(post_save, sender=User) +    @receiver(post_save, sender=get_user_model())      def create_auth_token(sender, instance=None, created=False, **kwargs):          if created:              Token.objects.create(user=instance) @@ -265,6 +267,12 @@ This authentication class depends on the optional [django-oauth2-provider][djang          'provider.oauth2',      ) +Then add `OAuth2Authentication` to your global `DEFAULT_AUTHENTICATION` setting: + +    'DEFAULT_AUTHENTICATION_CLASSES': ( +        'rest_framework.authentication.OAuth2Authentication', +    ), +  You must also include the following in your root `urls.py` module:      url(r'^oauth2/', include('provider.oauth2.urls', namespace='oauth2')), diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index 0c48783a..c46d415e 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -82,7 +82,7 @@ Note that the exception handler will only be called for responses generated by r  ## APIException -**Signature:** `APIException(detail=None)` +**Signature:** `APIException()`  The **base class** for all exceptions raised inside REST framework. diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 962c49e2..e05c0306 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -41,7 +41,7 @@ Defaults to `True`.  ### `default` -If set, this gives the default value that will be used for the field if none is supplied.  If not set the default behavior is to not populate the attribute at all.  +If set, this gives the default value that will be used for the field if no input value is supplied.  If not set the default behavior is to not populate the attribute at all.   May be set to a function or other callable, in which case the value will be evaluated each time it is used. @@ -286,7 +286,7 @@ An image representation.  Corresponds to `django.forms.fields.ImageField`. -Requires the `PIL` package. +Requires either the `Pillow` package or `PIL` package.  The `Pillow` package is recommended, as `PIL` is no longer actively maintained.  Signature and validation is the same as with `FileField`. @@ -299,9 +299,9 @@ Django's regular [FILE_UPLOAD_HANDLERS] are used for handling uploaded files.  # Custom fields -If you want to create a custom field, you'll probably want to override either one or both of the `.to_native()` and `.from_native()` methods.  These two methods are used to convert between the initial datatype, and a primative, serializable datatype.  Primative datatypes may be any of a number, string, date/time/datetime or None.  They may also be any list or dictionary like object that only contains other primative objects. +If you want to create a custom field, you'll probably want to override either one or both of the `.to_native()` and `.from_native()` methods.  These two methods are used to convert between the initial datatype, and a primitive, serializable datatype.  Primitive datatypes may be any of a number, string, date/time/datetime or None.  They may also be any list or dictionary like object that only contains other primitive objects. -The `.to_native()` method is called to convert the initial datatype into a primative, serializable datatype.  The `from_native()` method is called to restore a primative datatype into it's initial representation. +The `.to_native()` method is called to convert the initial datatype into a primitive, serializable datatype.  The `from_native()` method is called to restore a primitive datatype into it's initial representation.  ## Examples diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 859e8d52..0e02a2a7 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -165,8 +165,8 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha      from rest_framework import generics      class ProductFilter(django_filters.FilterSet): -        min_price = django_filters.NumberFilter(lookup_type='gte') -        max_price = django_filters.NumberFilter(lookup_type='lte') +        min_price = django_filters.NumberFilter(name="price", lookup_type='gte') +        max_price = django_filters.NumberFilter(name="price", lookup_type='lte')          class Meta:              model = Product              fields = ['category', 'in_stock', 'min_price', 'max_price'] @@ -176,10 +176,49 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha          serializer_class = ProductSerializer          filter_class = ProductFilter +  Which will allow you to make requests such as:      http://example.com/api/products?category=clothing&max_price=10.00 +You can also span relationships using `django-filter`, let's assume that each +product has foreign key to `Manufacturer` model, so we create filter that +filters using `Manufacturer` name. For example: + +    import django_filters +    from myapp.models import Product +    from myapp.serializers import ProductSerializer +    from rest_framework import generics + +    class ProductFilter(django_filters.FilterSet): +        class Meta: +            model = Product +            fields = ['category', 'in_stock', 'manufacturer__name`] + +This enables us to make queries like: + +    http://example.com/api/products?manufacturer__name=foo + +This is nice, but it shows underlying model structure in REST API, which may +be undesired, but you can use: + +    import django_filters +    from myapp.models import Product +    from myapp.serializers import ProductSerializer +    from rest_framework import generics + +    class ProductFilter(django_filters.FilterSet): + +        manufacturer = django_filters.CharFilter(name="manufacturer__name") + +        class Meta: +            model = Product +            fields = ['category', 'in_stock', 'manufacturer`] + +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].  --- @@ -195,9 +234,9 @@ For more details on using filter sets see the [django-filter documentation][djan  ## SearchFilter -The `SearchFilterBackend` class supports simple single query parameter based searching, and is based on the [Django admin's search functionality][search-django-admin]. +The `SearchFilter` class supports simple single query parameter based searching, and is based on the [Django admin's search functionality][search-django-admin]. -The `SearchFilterBackend` class will only be applied if the view has a `search_fields` attribute set.  The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`. +The `SearchFilter` class will only be applied if the view has a `search_fields` attribute set.  The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`.      class UserListView(generics.ListAPIView):          queryset = User.objects.all() @@ -321,6 +360,14 @@ For example, you might need to restrict users to only being able to see objects  We could achieve the same behavior by overriding `get_queryset()` on the views, but using a filter backend allows you to more easily add this restriction to multiple views, or to apply it across the entire API. +# Third party packages + +The following third party packages provide additional filter implementations. + +## Django REST framework chain + +The [django-rest-framework-chain package][django-rest-framework-chain] works together with the `DjangoFilterBackend` class, and allows you to easily create filters across relationships, or create multiple filter lookup types for a given field. +  [cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters  [django-filter]: https://github.com/alex/django-filter  [django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html @@ -329,3 +376,4 @@ We could achieve the same behavior by overriding `get_queryset()` on the views,  [view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models  [nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py  [search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields +[django-rest-framework-chain]: https://github.com/philipn/django-rest-framework-chain diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index dc0076df..83c3e45f 100755 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -65,7 +65,8 @@ The following attributes control the basic view behavior.  * `queryset` - The queryset that should be used for returning objects from this view.  Typically, you must either set this attribute, or override the `get_queryset()` method.  * `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output.  Typically, you must either set this attribute, or override the `get_serializer_class()` method. -* `lookup_field` - The field that should be used to lookup individual model instances.  Defaults to `'pk'`.  The URL conf should include a keyword argument corresponding to this value.  More complex lookup styles can be supported by overriding the `get_object()` method.  Note that when using hyperlinked APIs you'll need to ensure that *both* the API views *and* the serializer classes use lookup fields that correctly correspond with the URL conf. +* `lookup_field` - The model field that should be used to for performing object lookup of individual model instances.  Defaults to `'pk'`.  Note that when using hyperlinked APIs you'll need to ensure that *both* the API views *and* the serializer classes set the lookup fields if you need to use a custom value. +* `lookup_url_kwarg` - The URL keyword argument that should be used for object lookup.  The URL conf should include a keyword argument corresponding to this value.  If unset this defaults to using the same value as `lookup_field`.  **Shortcuts**: @@ -120,11 +121,27 @@ For example:  Note that if your API doesn't include any object level permissions, you may optionally exclude the ``self.check_object_permissions, and simply return the object from the `get_object_or_404` lookup. +#### `get_filter_backends(self)` + +Returns the classes that should be used to filter the queryset. Defaults to returning the `filter_backends` attribute. + +May be override to provide more complex behavior with filters, as using different (or even exlusive) lists of filter_backends depending on different criteria. + +For example: + +    def get_filter_backends(self): +        if "geo_route" in self.request.QUERY_PARAMS: +            return (GeoRouteFilter, CategoryFilter) +        elif "geo_point" in self.request.QUERY_PARAMS: +            return (GeoPointFilter, CategoryFilter) + +        return (CategoryFilter,) +  #### `get_serializer_class(self)`  Returns the class that should be used for the serializer.  Defaults to returning the `serializer_class` attribute, or dynamically generating a serializer class if the `model` shortcut is being used. -May be override to provide dynamic behavior such as using different serializers for read and write operations, or providing different serializers to different types of uesr. +May be override to provide dynamic behavior such as using different serializers for read and write operations, or providing different serializers to different types of users.  For example: @@ -146,12 +163,14 @@ For example:              return 20          return 100 -**Save hooks**: +**Save / deletion hooks**:  The following methods are provided as placeholder interfaces.  They contain empty implementations and are not called directly by `GenericAPIView`, but they are overridden and used by some of the mixin classes.  * `pre_save(self, obj)` - A hook that is called before saving an object.  * `post_save(self, obj, created=False)` - A hook that is called after saving an object. +* `pre_delete(self, obj)` - A hook that is called before deleting an object. +* `post_delete(self, obj)` - A hook that is called after deleting an object.  The `pre_save` method in particular is a useful hook for setting attributes that are implicit in the request, but are not part of the request data.  For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument. @@ -327,7 +346,7 @@ You can then simply apply this mixin to a view or viewset anytime you need to ap          serializer_class = UserSerializer          lookup_fields = ('account', 'username') -Using custom mixins is a good option if you have custom behavior that needs to be used  +Using custom mixins is a good option if you have custom behavior that needs to be used  ## Creating custom base classes @@ -336,7 +355,7 @@ If you are using a mixin across multiple views, you can take this a step further      class BaseRetrieveView(MultipleFieldLookupMixin,                             generics.RetrieveAPIView):          pass -     +      class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,                                          generics.RetrieveUpdateDestroyAPIView):          pass diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 871de84e..6a0f48f4 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -230,6 +230,10 @@ The [DRF Any Permissions][drf-any-permissions] packages provides a different per  The [Composed Permissions][composed-permissions] package provides a simple way to define complex and multi-depth (with logic operators) permission objects, using small and reusable components. +## REST Condition + +The [REST Condition][rest-condition] package is another extension for building complex permissions in a simple and convenient way.  The extension allows you to combine permissions with logical operators. +  [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html  [authentication]: authentication.md  [throttling]: throttling.md @@ -243,3 +247,4 @@ The [Composed Permissions][composed-permissions] package provides a simple way t  [filtering]: filtering.md  [drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions  [composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions +[rest-condition]: https://github.com/caxap/rest_condition diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 5ec4b22f..556429bb 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -44,7 +44,7 @@ In order to explain the various types of relational fields, we'll use a couple o  For example, the following serializer.      class AlbumSerializer(serializers.ModelSerializer): -        tracks = RelatedField(many=True) +        tracks = serializers.RelatedField(many=True)          class Meta:              model = Album @@ -54,7 +54,7 @@ Would serialize to the following representation.      {          'album_name': 'Things We Lost In The Fire', -        'artist': 'Low' +        'artist': 'Low',          'tracks': [              '1: Sunflower',              '2: Whitetail', @@ -86,7 +86,7 @@ Would serialize to a representation like this:      {          'album_name': 'The Roots', -        'artist': 'Undun' +        'artist': 'Undun',          'tracks': [              89,              90, @@ -121,7 +121,7 @@ Would serialize to a representation like this:      {          'album_name': 'Graceland', -        'artist': 'Paul Simon' +        'artist': 'Paul Simon',          'tracks': [              'http://www.example.com/api/tracks/45/',              'http://www.example.com/api/tracks/46/', @@ -159,7 +159,7 @@ Would serialize to a representation like this:      {          'album_name': 'Dear John', -        'artist': 'Loney Dear' +        'artist': 'Loney Dear',          'tracks': [              'Airport Surroundings',              'Everything Turns to You', @@ -194,7 +194,7 @@ Would serialize to a representation like this:      {          'album_name': 'The Eraser', -        'artist': 'Thom Yorke' +        'artist': 'Thom Yorke',          'track_listing': 'http://www.example.com/api/track_list/12/',      } @@ -234,7 +234,7 @@ Would serialize to a nested representation like this:      {          'album_name': 'The Grey Album', -        'artist': 'Danger Mouse' +        'artist': 'Danger Mouse',          'tracks': [              {'order': 1, 'title': 'Public Service Announcement'},              {'order': 2, 'title': 'What More Can I Say'}, @@ -271,7 +271,7 @@ This custom field would then serialize to the following representation.      {          'album_name': 'Sometimes I Wish We Were an Eagle', -        'artist': 'Bill Callahan' +        'artist': 'Bill Callahan',          'tracks': [              'Track 1: Jim Cain (04:39)',              'Track 2: Eid Ma Clack Shaw (04:19)', diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 657377d9..cf200569 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -118,7 +118,13 @@ Renders the request data into `JSONP`.  The `JSONP` media type provides a mechan  The javascript callback function must be set by the client including a `callback` URL query parameter.  For example `http://example.com/api/users?callback=jsonpCallback`.  If the callback function is not explicitly set by the client it will default to `'callback'`. -**Note**: If you require cross-domain AJAX requests, you may want to consider using the more modern approach of [CORS][cors] as an alternative to `JSONP`.  See the [CORS documentation][cors-docs] for more details. +--- + +**Warning**: If you require cross-domain AJAX requests, you should almost certainly be using the more modern approach of [CORS][cors] as an alternative to `JSONP`.  See the [CORS documentation][cors-docs] for more details. + +The `jsonp` approach is essentially a browser hack, and is [only appropriate for globally  readable API endpoints][jsonp-security], where `GET` requests are unauthenticated and do not require any user permissions. + +---  **.media_type**: `application/javascript` @@ -167,14 +173,14 @@ The template name is determined by (in order of preference):  An example of a view that uses `TemplateHTMLRenderer`: -    class UserDetail(generics.RetrieveUserAPIView): +    class UserDetail(generics.RetrieveAPIView):          """          A view that returns a templated HTML representations of a given user.          """          queryset = User.objects.all()          renderer_classes = (TemplateHTMLRenderer,) -        def get(self, request, *args, **kwargs) +        def get(self, request, *args, **kwargs):              self.object = self.get_object()              return Response({'user': self.object}, template_name='user_detail.html') @@ -409,12 +415,17 @@ The following third party packages are also available.  Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications.  [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework. +## UltraJSON + +[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Jacob Haslehurst][hzy] maintains the [drf-ujson-renderer][drf-ujson-renderer] package which implements JSON rendering using the UJSON package. +  [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process  [conneg]: content-negotiation.md  [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers  [rfc4627]: http://www.ietf.org/rfc/rfc4627.txt  [cors]: http://www.w3.org/TR/cors/  [cors-docs]: ../topics/ajax-csrf-cors.md +[jsonp-security]: http://stackoverflow.com/questions/613962/is-jsonp-safe-to-use  [testing]: testing.md  [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas  [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven @@ -426,3 +437,6 @@ Comma-separated values are a plain-text tabular data format, that can be easily  [mjumbewu]: https://github.com/mjumbewu  [djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack  [djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv +[ultrajson]: https://github.com/esnme/ultrajson +[hzy]: https://github.com/hzy +[drf-ujson-renderer]: https://github.com/gizmag/drf-ujson-renderer diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index f20a695b..654cf404 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -12,7 +12,7 @@ REST framework adds support for automatic URL routing to Django, and provides yo  ## Usage -Here's an example of a simple URL conf, that uses `DefaultRouter`. +Here's an example of a simple URL conf, that uses `SimpleRouter`.      from rest_framework import routers @@ -214,5 +214,27 @@ If you want to provide totally custom behavior, you can override `BaseRouter` an  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. +# Third Party Packages + +The following third party packages are also available. + +## DRF Nested Routers + +The [drf-nested-routers package][drf-nested-routers] provides routers and relationship fields for working with nested resources. + +[cite]: http://guides.rubyonrails.org/routing.html +[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers + +## wq.db + +The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (and singleton instance) that extends `DefaultRouter` with a `register_model()` API. Much like Django's `admin.site.register`, the only required argument to `app.router.register_model` is a model class.  Reasonable defaults for a url prefix and viewset will be inferred from the model and global configuration. + +    from wq.db.rest import app +    from myapp.models import MyModel + +    app.router.register_model(MyModel) +  [cite]: http://guides.rubyonrails.org/routing.html  [route-decorators]: viewsets.html#marking-extra-actions-for-routing +[wq.db]: http://wq.io/wq.db +[wq.db-router]: http://wq.io/docs/app.py diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index a3cd1d6a..6fc25f57 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -67,6 +67,21 @@ At this point we've translated the model instance into Python native datatypes.      json      # '{"email": "leila@example.com", "content": "foo bar", "created": "2012-08-22T16:20:09.822"}' +### Customizing field representation + +Sometimes when serializing objects, you may not want to represent everything exactly the way it is in your model. + +If you need to customize the serialized value of a particular field, you can do this by creating a `transform_<fieldname>` method. For example if you needed to render some markdown from a text field: + +    description = serializers.TextField() +    description_html = serializers.TextField(source='description', read_only=True) + +    def transform_description_html(self, obj, value): +        from django.contrib.markup.templatetags.markup import markdown +        return markdown(value) + +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...  @@ -84,7 +99,6 @@ Deserialization is similar.  First we parse a stream into Python native datatype      # True      serializer.object      # <Comment object at 0x10633b2d0> -    >>> serializer.deserialize('json', stream)  When deserializing data, we can either create a new instance, or update an existing instance. @@ -411,7 +425,7 @@ You can change the field that is used for object lookups by setting the `lookup_              fields = ('url', 'account_name', 'users', 'created')              lookup_field = 'slug' -Not that the `lookup_field` will be used as the default on *all* hyperlinked fields, including both the URL identity, and any hyperlinked relationships. +Note that the `lookup_field` will be used as the default on *all* hyperlinked fields, including both the URL identity, and any hyperlinked relationships.  For more specific requirements such as specifying a different lookup for each field, you'll want to set the fields on the serializer explicitly.  For example: diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 409f659b..64c46434 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -17,6 +17,18 @@ Using bare status codes in your responses isn't recommended.  REST framework inc  The full set of HTTP status codes included in the `status` module is listed below. +The module also includes a set of helper functions for testing if a status code is in a given range. + +    from rest_framework import status +	from rest_framework.test import APITestCase + +	class ExampleTestCase(APITestCase): +	    def test_url_root(self): +	        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]. @@ -90,6 +102,15 @@ Response status codes beginning with the digit "5" indicate cases in which the s      HTTP_505_HTTP_VERSION_NOT_SUPPORTED      HTTP_511_NETWORK_AUTHENTICATION_REQUIRED +## Helper functions + +The following helper functions are available for identifying the category of the response code. + +    is_informational()  # 1xx +    is_success()        # 2xx +    is_redirect()       # 3xx +    is_client_error()   # 4xx +    is_server_error()   # 5xx  [rfc2324]: http://www.ietf.org/rfc/rfc2324.txt  [rfc2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 35c1f766..4a8a9168 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -205,10 +205,10 @@ You can use any of REST framework's test case classes as you would for the regul              Ensure we can create a new account object.              """              url = reverse('account-list') -            expected = {'name': 'DabApps'} +            data = {'name': 'DabApps'}              response = self.client.post(url, data, format='json')              self.assertEqual(response.status_code, status.HTTP_201_CREATED) -            self.assertEqual(response.data, expected) +            self.assertEqual(response.data, data)  --- diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 536f0ab7..bedecb36 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -59,7 +59,7 @@ using the `APIView` class based views.  Or, if you're using the `@api_view` decorator with function based views.      @api_view('GET') -    @throttle_classes(UserRateThrottle) +    @throttle_classes([UserRateThrottle])      def example_view(request, format=None):          content = {              'status': 'request was permitted' diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 15581e09..194a7a6b 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -168,5 +168,5 @@ Each of these decorators takes a single argument which must be a list or tuple o  [cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html  [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html -[settings]: api-guide/settings.md -[throttling]: api-guide/throttling.md +[settings]: settings.md +[throttling]: throttling.md diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md index a5359e99..dfd9d22a 100644 --- a/docs/api-guide/viewsets.md +++ b/docs/api-guide/viewsets.md @@ -178,7 +178,7 @@ The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`,  #### Example -Because `ModelViewSet` extends `GenericAPIView`, you'll normally need to provide at least the `queryset` and `serializer_class` attributes.  For example: +Because `ModelViewSet` extends `GenericAPIView`, you'll normally need to provide at least the `queryset` and `serializer_class` attributes, or the `model` attribute shortcut.  For example:      class AccountViewSet(viewsets.ModelViewSet):          """ diff --git a/docs/img/travis-status.png b/docs/img/travis-status.pngBinary files differ new file mode 100644 index 00000000..fec98cf9 --- /dev/null +++ b/docs/img/travis-status.png diff --git a/docs/index.md b/docs/index.md index bb2129f6..04804fa7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -100,7 +100,7 @@ Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_  We're ready to create our API now.  Here's our project's root `urls.py` module: -    from django.conf.urls.defaults import url, patterns, include +    from django.conf.urls import url, patterns, include      from django.contrib.auth.models import User, Group      from rest_framework import viewsets, routers @@ -112,7 +112,7 @@ Here's our project's root `urls.py` module:          model = Group -    # Routers provide an easy way of automatically determining the URL conf +    # Routers provide an easy way of automatically determining the URL conf.      router = routers.DefaultRouter()      router.register(r'users', UserViewSet)      router.register(r'groups', GroupViewSet) @@ -177,6 +177,7 @@ General guides to using REST framework.  * [Browser enhancements][browser-enhancements]  * [The Browsable API][browsableapi]  * [REST, Hypermedia & HATEOAS][rest-hypermedia-hateoas] +* [Contributing to REST framework][contributing]  * [2.0 Announcement][rest-framework-2-announcement]  * [2.2 Announcement][2.2-announcement]  * [2.3 Announcement][2.3-announcement] @@ -255,11 +256,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X  [image]: img/quickstart.png  [index]: . -[oauth1-section]: api-guide/authentication.html#oauthauthentication -[oauth2-section]: api-guide/authentication.html#oauth2authentication -[serializer-section]: api-guide/serializers.html#serializers -[modelserializer-section]: api-guide/serializers.html#modelserializer -[functionview-section]: api-guide/views.html#function-based-views +[oauth1-section]: api-guide/authentication#oauthauthentication +[oauth2-section]: api-guide/authentication#oauth2authentication +[serializer-section]: api-guide/serializers#serializers +[modelserializer-section]: api-guide/serializers#modelserializer +[functionview-section]: api-guide/views#function-based-views  [sandbox]: http://restframework.herokuapp.com/  [quickstart]: tutorial/quickstart.md diff --git a/docs/template.html b/docs/template.html index a20c8111..c065237a 100644 --- a/docs/template.html +++ b/docs/template.html @@ -4,6 +4,7 @@      <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"> @@ -101,6 +102,7 @@                    <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/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> @@ -167,7 +169,32 @@              <div id="table-of-contents">                <ul class="nav nav-list side-nav well sidebar-nav-fixed">                  {{ toc }} +              <div> +              <hr> + +<p><strong>The team behind REST framework is launching a new API service.</strong></p> + +<p>If you want to be first in line when we start issuing invitations, please sign up here:</p> + +<!-- Begin MailChimp Signup Form --> +<link href="//cdn-images.mailchimp.com/embedcode/slim-081711.css" rel="stylesheet" type="text/css"> +<style type="text/css"> +    #mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; } +    /* Add your own MailChimp form style overrides in your site stylesheet or in this style block. +       We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */ +</style> +<div id="mc_embed_signup" style="background: rgb(245, 245, 245)"> +<form action="http://dabapps.us1.list-manage1.com/subscribe/post?u=cf73a9994eb5b8d8d461b5dfb&id=cb6af8e8bd" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate> +<!--     <label for="mce-EMAIL">Keep me posted!</label> + -->    <input style="width: 90%" type="email" value="" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email address" required> +    <div class="clear"><input class="btn btn-success" type="submit" value="Yes, keep me posted!" name="subscribe" id="mc-embedded-subscribe" class="button"></div> +</form> +</div> +</style></div>                </ul> + + +<!--End mc_embed_signup-->              </div>            </div> diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md index 7d276049..0f980e1c 100644 --- a/docs/topics/2.2-announcement.md +++ b/docs/topics/2.2-announcement.md @@ -151,7 +151,7 @@ From version 2.2 onwards, serializers with hyperlinked relationships *always* re  [porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/  [python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility  [django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy -[credits]: http://django-rest-framework.org/topics/credits.html +[credits]: http://django-rest-framework.org/topics/credits  [mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework  [django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs  [marcgibbons]: https://github.com/marcgibbons/ diff --git a/docs/topics/ajax-csrf-cors.md b/docs/topics/ajax-csrf-cors.md index 0555b84d..97dd4710 100644 --- a/docs/topics/ajax-csrf-cors.md +++ b/docs/topics/ajax-csrf-cors.md @@ -6,7 +6,7 @@  ## Javascript clients -If your building a javascript client to interface with your Web API, you'll need to consider if the client can use the same authentication policy that is used by the rest of the website, and also determine if you need to use CSRF tokens or CORS headers. +If you’re building a JavaScript client to interface with your Web API, you'll need to consider if the client can use the same authentication policy that is used by the rest of the website, and also determine if you need to use CSRF tokens or CORS headers.  AJAX requests that are made within the same context as the API they are interacting with will typically use `SessionAuthentication`.  This ensures that once a user has logged in, any AJAX requests made can be authenticated using the same session-based authentication that is used for the rest of the website. diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index 123e4a8a..906950bb 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -6,50 +6,92 @@  There are many ways you can contribute to Django REST framework.  We'd like it to be a community-led project, so please get involved and help shape the future of the project. -# Community +## Community -If you use and enjoy REST framework please consider [staring the project on GitHub][github], and [upvoting it on Django packages][django-packages].  Doing so helps potential new users see that the project is well used, and help us continue to attract new users. +The most important thing you can do to help push the REST framework project forward is to be actively involved wherever possible.  Code contributions are often overvalued as being the primary way to get involved in a project, we don't believe that needs to be the case. -You might also consider writing a blog post on your experience with using REST framework, writing a tutorial about using the project with a particular javascript framework, or simply sharing the love on Twitter. +If you use REST framework, we'd love you to be vocal about your experiences with it - you might consider writing a blog post about using REST framework, or publishing a tutorial about building a project with a particularJjavascript framework.  Experiences from beginners can be particularly helpful because you'll be in the best position to assess which bits of REST framework are more difficult to understand and work with.  Other really great ways you can help move the community forward include helping answer questions on the [discussion group][google-group], or setting up an [email alert on StackOverflow][so-filter] so that you get notified of any new questions with the `django-rest-framework` tag.  When answering questions make sure to help future contributors find their way around by hyperlinking wherever possible to related threads and tickets, and include backlinks from those items if relevant.  +## Code of conduct + +Please keep the tone polite & professional.  For some users a discussion on the REST framework mailing list or ticket tracker may be their first engagement with the open source community.  First impressions count, so let's try to make everyone feel welcome. + +Be mindful in the language you choose.  As an example, in an environment that is heavily male-dominated, posts that start 'Hey guys,' can come across as unintentionally exclusive.  It's just as easy, and more inclusive to use gender neutral language in those situations. + +The [Django code of conduct][code-of-conduct] gives a fuller set of guidelines for participating in community forums. +  # Issues -It's really helpful if you make sure you address issues to the correct channel.  Usage questions should be directed to the [discussion group][google-group].  Feature requests, bug reports and other issues should be raised on the GitHub [issue tracker][issues]. +It's really helpful if you can make sure to address issues on the correct channel.  Usage questions should be directed to the [discussion group][google-group].  Feature requests, bug reports and other issues should be raised on the GitHub [issue tracker][issues].  Some tips on good issue reporting:  * When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing.  * Search the issue list first for related items, and make sure you're running the latest version of REST framework before reporting an issue.  * If reporting a bug, then try to include a pull request with a failing test case.  This will help us quickly identify if there is a valid issue, and make sure that it gets fixed more quickly if there is one. +* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library.  Keeping new feature requests implemented as third party libraries allows us to keep down the maintainence overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation. +* Closing an issue doesn't necessarily mean the end of a discussion.  If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened. +## Triaging issues +Getting involved in triaging incoming issues is a good way to start contributing.  Every single ticket that comes into the ticket tracker needs to be reviewed in order to determine what the next steps should be.  Anyone can help out with this, you just need to be willing to  -* TODO: Triage +* Read through the ticket - does it make sense, is it missing any context that would help explain it better? +* Is the ticket reported in the correct place, would it be better suited as a discussion on the discussion group? +* If the ticket is a bug report, can you reproduce it? Are you able to write a failing test case that demonstrates the issue and that can be submitted as a pull request? +* If the ticket is a feature request, do you agree with it, and could the feature request instead be implemented as a third party package? +* If a ticket hasn't had much activity and it addresses something you need, then comment on the ticket and try to find out what's needed to get it moving again.  # Development +To start developing on Django REST framework, clone the repo: + +    git clone git@github.com:tomchristie/django-rest-framework.git + +Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you setup your editor to automatically indicated non-conforming styles. + +## Testing + +To run the tests, clone the repository, and then: + +    # Setup the virtual environment +    virtualenv env +    env/bin/activate +    pip install -r requirements.txt +    pip install -r optionals.txt + +    # Run the tests +    rest_framework/runtests/runtests.py -* git clone & PYTHONPATH -* Pep8 -* Recommend editor that runs pep8 +You can also use the excellent `[tox][tox]` testing tool to run the tests against all supported versions of Python and Django.  Install `tox` globally, and then simply run: -### Pull requests +    tox -* Make pull requests early -* Describe branching +## Pull requests -### Managing compatibility issues +It's a good idea to make pull requests early on.  A pull request represents the start of a discussion, and doesn't necessarily need to be the final, finished submission. -* Describe compat module +It's also always best to make a new branch before starting work on a pull request.  This means that you'll be able to later switch back to working on another seperate issue without interfering with an ongoing pull requests. -# Testing +It's also useful to remember that if you have an outstanding pull request then pushing new commits to your GitHub repo will also automatically update the pull requests. -* Running the tests -* tox +GitHub's documentation for working on pull requests is [available here][pull-requests]. + +Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django. + +Once you've made a pull request take a look at the travis build status in the GitHub interface and make sure the tests are runnning as you'd expect. + +![Travis status][travis-status] + +*Above: Travis build notifications* + +## Managing compatibility issues + +Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment.  Any code that branches in this way should be isolated into the `compat.py` module, and should provide a single common interface that the rest of the codebase can use.  # Documentation @@ -77,7 +119,7 @@ Some other tips:  * Keep paragraphs reasonably short.  * Use double spacing after the end of sentences. -* Don't use the abbreviations such as 'e.g..' but instead use long form, such as 'For example'. +* Don't use the abbreviations such as 'e.g.' but instead use long form, such as 'For example'.  ## Markdown style @@ -118,25 +160,34 @@ If you want to draw attention to a note or warning, use a pair of enclosing line      --- -    **Note:** Make sure you do this thing. +    **Note:** A useful documentation note.      ---  # Third party packages -* Django reusable app +New features to REST framework are generally recommended to be implemented as third party libraries that are developed outside of the core framework.  Ideally third party libraries should be properly documented and packaged, and made available on PyPI. + +## Getting started + +If you have some functionality that you would like to implement as a third party package it's worth contacting the [discussion group][google-group] as others may be willing to get involved.  We strongly encourage third party package development and will always try to prioritize time spent helping their development, documentation and packaging. + +We recommend the [`django-reusable-app`][django-reusable-app] template as a good resource for getting up and running with implementing a third party Django package. -# Core committers +## Linking to your package -* Still use pull reqs -* Credits +Once your package is decently documented and available on PyPI open a pull request or issue, and we'll add a link to it from the main REST framework documentation.  [cite]: http://www.w3.org/People/Berners-Lee/FAQ.html -[github]: https://github.com/tomchristie/django-rest-framework -[django-packages]: https://www.djangopackages.com/grids/g/api/ +[code-of-conduct]: https://www.djangoproject.com/conduct/  [google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework  [so-filter]: http://stackexchange.com/filters/66475/rest-framework  [issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open +[pep-8]: http://www.python.org/dev/peps/pep-0008/ +[travis-status]: ../img/travis-status.png +[pull-requests]: https://help.github.com/articles/using-pull-requests +[tox]: http://tox.readthedocs.org/en/latest/  [markdown]: http://daringfireball.net/projects/markdown/basics  [docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs  [mou]: http://mouapp.com/ +[django-reusable-app]: https://github.com/dabapps/django-reusable-app diff --git a/docs/topics/credits.md b/docs/topics/credits.md index 4483f170..1a838421 100644 --- a/docs/topics/credits.md +++ b/docs/topics/credits.md @@ -169,6 +169,18 @@ The following people have helped make REST framework great.  * Edmond Wong - [edmondwong]  * Ben Reilly - [bwreilly]  * Tai Lee - [mrmachine] +* Markus Kaiserswerth - [mkai] +* Henry Clifford - [hcliff] +* Thomas Badaud - [badale] +* Colin Huang - [tamakisquare] +* Ross McFarland - [ross] +* Jacek Bzdak - [jbzdak] +* Alexander Lukanin - [alexanderlukanin13] +* Yamila Moreno - [yamila-moreno] +* Rob Hudson - [robhudson] +* Alex Good - [alexjg] +* Ian Foote - [ian-foote] +* Chuck Harmston - [chuckharmston]  Many thanks to everyone who's contributed to the project. @@ -374,3 +386,15 @@ You can also contact [@_tomchristie][twitter] directly on twitter.  [edmondwong]: https://github.com/edmondwong  [bwreilly]: https://github.com/bwreilly  [mrmachine]: https://github.com/mrmachine +[mkai]: https://github.com/mkai +[hcliff]: https://github.com/hcliff +[badale]: https://github.com/badale +[tamakisquare]: https://github.com/tamakisquare +[ross]: https://github.com/ross +[jbzdak]: https://github.com/jbzdak +[alexanderlukanin13]: https://github.com/alexanderlukanin13 +[yamila-moreno]: https://github.com/yamila-moreno +[robhudson]: https://github.com/robhudson +[alexjg]: https://github.com/alexjg +[ian-foote]: https://github.com/ian-foote +[chuckharmston]: https://github.com/chuckharmston diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index a3f3ed3c..0815bcfb 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -45,11 +45,43 @@ You can determine your currently installed version using `pip freeze`:  * `@detail_route` and `@list_route` decorators replace `@action` and `@link`.  * `six` no longer bundled.  For Django <= 1.4.1, install `six` package.  * Support customizable view name and description functions, using the `VIEW_NAME_FUNCTION` and `VIEW_DESCRIPTION_FUNCTION` settings. +* Added `NUM_PROXIES` setting for smarter client IP identification.  * Added `MAX_PAGINATE_BY` setting and `max_paginate_by` generic view attribute.  * Added `cache` attribute to throttles to allow overriding of default cache.  * Bugfix: `?page_size=0` query parameter now falls back to default page size for view, instead of always turning pagination off. + +### Master + +* JSON renderer now deals with objects that implement a dict-like interface. +* Bugfix: Refine behavior that calls model manager `all()` across nested serializer relationships, preventing erronous behavior with some non-ORM objects, and preventing unneccessary queryset re-evaluations. + +### 2.3.10 + +**Date**: 6th December 2013 + +* Add in choices information for ChoiceFields in response to `OPTIONS` requests. +* Added `pre_delete()` and `post_delete()` method hooks. +* Added status code category helper functions. +* Bugfix: Partial updates which erronously set a related field to `None` now correctly fail validation instead of raising an exception. +* Bugfix: Responses without any content no longer include an HTTP `'Content-Type'` header. +* Bugfix: Correctly handle validation errors in PUT-as-create case, responding with 400. + +### 2.3.9 + +**Date**: 15th November 2013 + +* Fix Django 1.6 exception API compatibility issue caused by `ValidationError`. +* Include errors in HTML forms in browsable API. +>>>>>>> master  * Added JSON renderer support for numpy scalars. +* Added `transform_<fieldname>` hooks on serializers for easily modifying field output.  * Added `get_context` hook in `BrowsableAPIRenderer`. +* Allow serializers to be passed `files` but no `data`. +* `HTMLFormRenderer` now renders serializers directly to HTML without needing to create an intermediate form object. +* Added `get_filter_backends` hook. +* Added queryset aggregates to allowed fields in `OrderingFilter`. +* Bugfix: Fix decimal suppoprt with `YAMLRenderer`. +* Bugfix: Fix submission of unicode in browsable API through raw data form.  ### 2.3.8 @@ -64,7 +96,7 @@ You can determine your currently installed version using `pip freeze`:  * 'Raw data' and 'HTML form' tab preference in browseable API now saved between page views.  * Bugfix: `required=True` argument fixed for boolean serializer fields.  * Bugfix: `client.force_authenticate(None)` should also clear session info if it exists. -* Bugfix: Client sending emptry string instead of file now clears `FileField`. +* Bugfix: Client sending empty string instead of file now clears `FileField`.  * Bugfix: Empty values on ChoiceFields with `required=False` now consistently return `None`.  ### 2.3.7 diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 22d29285..e1c0009c 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -225,7 +225,7 @@ For the moment we won't use any of REST framework's other features, we'll just w  We'll start off by creating a subclass of HttpResponse that we can use to render any data we return into `json`. -Edit the `snippet/views.py` file, and add the following. +Edit the `snippets/views.py` file, and add the following.      from django.http import HttpResponse      from django.views.decorators.csrf import csrf_exempt diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 30966a10..7fa4f3e4 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -35,7 +35,7 @@ The wrappers also provide behaviour such as returning `405 Method Not Allowed` r  Okay, let's go ahead and start using these new components to write a few views.  -We don't need our `JSONResponse` class anymore, so go ahead and delete that.  Once that's done we can start refactoring our views slightly. +We don't need our `JSONResponse` class in `views.py` anymore, so go ahead and delete that.  Once that's done we can start refactoring our views slightly.      from rest_framework import status      from rest_framework.decorators import api_view @@ -64,7 +64,7 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that.  On  Our instance view is an improvement over the previous example.  It's a little more concise, and the code now feels very similar to if we were working with the Forms API.  We're also using named status codes, which makes the response meanings more obvious. -Here is the view for an individual snippet. +Here is the view for an individual snippet, in the `views.py` module.      @api_view(['GET', 'PUT', 'DELETE'])      def snippet_detail(request, pk): @@ -147,7 +147,7 @@ Similarly, we can control the format of the request that we send, using the `Con      # POST using form data      curl -X POST http://127.0.0.1:8000/snippets/ -d "code=print 123" -    {"id": 3, "title": "", "code": "123", "linenos": false, "language": "python", "style": "friendly"} +    {"id": 3, "title": "", "code": "print 123", "linenos": false, "language": "python", "style": "friendly"}      # POST using JSON      curl -X POST http://127.0.0.1:8000/snippets/ -d '{"code": "print 456"}' -H "Content-Type: application/json" diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 9fc424fe..b37bc31b 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -4,7 +4,7 @@ We can also write our API views using class based views, rather than function ba  ## Rewriting our API using class based views -We'll start by rewriting the root view as a class based view.  All this involves is a little bit of refactoring. +We'll start by rewriting the root view as a class based view.  All this involves is a little bit of refactoring of `views.py`.      from snippets.models import Snippet      from snippets.serializers import SnippetSerializer @@ -30,7 +30,7 @@ We'll start by rewriting the root view as a class based view.  All this involves                  return Response(serializer.data, status=status.HTTP_201_CREATED)              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -So far, so good.  It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods.  We'll also need to update the instance view.  +So far, so good.  It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods.  We'll also need to update the instance view in `views.py`.       class SnippetDetail(APIView):          """ @@ -62,7 +62,7 @@ So far, so good.  It looks pretty similar to the previous case, but we've got be  That's looking good.  Again, it's still pretty similar to the function based view right now. -We'll also need to refactor our URLconf slightly now we're using class based views. +We'll also need to refactor our `urls.py` slightly now we're using class based views.      from django.conf.urls import patterns, url      from rest_framework.urlpatterns import format_suffix_patterns @@ -83,7 +83,7 @@ One of the big wins of using class based views is that it allows us to easily co  The create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create.  Those bits of common behaviour are implemented in REST framework's mixin classes. -Let's take a look at how we can compose our views by using the mixin classes. +Let's take a look at how we can compose the views by using the mixin classes.  Here's our `views.py` module again.      from snippets.models import Snippet      from snippets.serializers import SnippetSerializer @@ -126,7 +126,7 @@ Pretty similar.  Again we're using the `GenericAPIView` class to provide the cor  ## Using generic class based views -Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further.  REST framework provides a set of already mixed-in generic views that we can use. +Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further.  REST framework provides a set of already mixed-in generic views that we can use to trim down our `views.py` module even more.      from snippets.models import Snippet      from snippets.serializers import SnippetSerializer diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 393d879a..b472322a 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -12,7 +12,7 @@ Currently our API doesn't have any restrictions on who can edit or delete code s  We're going to make a couple of changes to our `Snippet` model class.  First, let's add a couple of fields.  One of those fields will be used to represent the user who created the code snippet.  The other field will be used to store the highlighted HTML representation of the code. -Add the following two fields to the model. +Add the following two fields to the `Snippet` model in `models.py`.      owner = models.ForeignKey('auth.User', related_name='snippets')      highlighted = models.TextField() @@ -52,7 +52,7 @@ You might also want to create a few different users, to use for testing the API.  ## Adding endpoints for our User models -Now that we've got some users to work with, we'd better add representations of those users to our API.  Creating a new serializer is easy: +Now that we've got some users to work with, we'd better add representations of those users to our API.  Creating a new serializer is easy. In `serializers.py` add:      from django.contrib.auth.models import User @@ -65,7 +65,10 @@ Now that we've got some users to work with, we'd better add representations of t  Because `'snippets'` is a *reverse* relationship on the User model, it will not be included by default when using the `ModelSerializer` class, so we needed to add an explicit field for it. -We'll also add a couple of views.  We'd like to just use read-only views for the user representations, so we'll use the `ListAPIView` and `RetrieveAPIView` generic class based views. +We'll also add a couple of views to `views.py`.  We'd like to just use read-only views for the user representations, so we'll use the `ListAPIView` and `RetrieveAPIView` generic class based views. + +    from django.contrib.auth.models import User +      class UserList(generics.ListAPIView):          queryset = User.objects.all() @@ -75,8 +78,12 @@ We'll also add a couple of views.  We'd like to just use read-only views for the      class UserDetail(generics.RetrieveAPIView):          queryset = User.objects.all()          serializer_class = UserSerializer +         +Make sure to also import the `UserSerializer` class + +	from snippets.serializers import UserSerializer -Finally we need to add those views into the API, by referencing them from the URL conf. +Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in `urls.py`.      url(r'^users/$', views.UserList.as_view()),      url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()), @@ -94,7 +101,7 @@ On **both** the `SnippetList` and `SnippetDetail` view classes, add the followin  ## Updating our serializer -Now that snippets are associated with the user that created them, let's update our `SnippetSerializer` to reflect that.  Add the following field to the serializer definition: +Now that snippets are associated with the user that created them, let's update our `SnippetSerializer` to reflect that.  Add the following field to the serializer definition in `serializers.py`:      owner = serializers.Field(source='owner.username') diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md index 06eec3c4..8bf8c7f5 100644 --- a/docs/tutorial/quickstart.md +++ b/docs/tutorial/quickstart.md @@ -85,10 +85,14 @@ Right, we'd better write some views then.  Open `quickstart/views.py` and get ty          queryset = Group.objects.all()          serializer_class = GroupSerializer -Rather that write multiple views we're grouping together all the common behavior into classes called `ViewSets`. +Rather than write multiple views we're grouping together all the common behavior into classes called `ViewSets`.  We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise. +Notice that our viewset classes here are a little different from those in the [frontpage example][readme-example-api], as they include `queryset` and `serializer_class` attributes, instead of a `model` attribute. + +For trivial cases you can simply set a `model` attribute on the `ViewSet` class and the serializer and queryset will be automatically generated for you.  Setting the `queryset` and/or `serializer_class` attributes gives you more explicit control of the API behaviour, and is the recommended style for most applications. +  ## URLs  Okay, now let's wire up the API URLs.  On to `tutorial/urls.py`... @@ -169,6 +173,7 @@ Great, that was easy!  If you want to get a more in depth understanding of how REST framework fits together head on over to [the tutorial][tutorial], or start browsing the [API guide][guide]. +[readme-example-api]: ../#example  [image]: ../img/quickstart.png  [tutorial]: 1-serialization.md  [guide]: ../#api-guide | 
