From deedf6957d14c2808c00a009ac2c1d4528cb80c9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 1 Sep 2012 20:26:27 +0100 Subject: REST framework 2 docs --- docs/api-guide/contentnegotiation.md | 1 + docs/api-guide/exceptions.md | 3 + docs/api-guide/parsers.md | 3 + docs/api-guide/renderers.md | 4 + docs/api-guide/requests.md | 66 ++++++++++ docs/api-guide/responses.md | 23 ++++ docs/api-guide/serializers.md | 241 +++++++++++++++++++++++++++++++++++ docs/api-guide/status-codes.md | 93 ++++++++++++++ docs/api-guide/urls.md | 41 ++++++ docs/api-guide/views.md | 39 ++++++ 10 files changed, 514 insertions(+) create mode 100644 docs/api-guide/contentnegotiation.md create mode 100644 docs/api-guide/exceptions.md create mode 100644 docs/api-guide/parsers.md create mode 100644 docs/api-guide/renderers.md create mode 100644 docs/api-guide/requests.md create mode 100644 docs/api-guide/responses.md create mode 100644 docs/api-guide/serializers.md create mode 100644 docs/api-guide/status-codes.md create mode 100644 docs/api-guide/urls.md create mode 100644 docs/api-guide/views.md (limited to 'docs/api-guide') diff --git a/docs/api-guide/contentnegotiation.md b/docs/api-guide/contentnegotiation.md new file mode 100644 index 00000000..f01627d8 --- /dev/null +++ b/docs/api-guide/contentnegotiation.md @@ -0,0 +1 @@ +> HTTP has provisions for several mechanisms for "content negotiation" -- the process of selecting the best representation for a given response when there are multiple representations available. -- RFC 2616, Fielding et al. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md new file mode 100644 index 00000000..d41327c6 --- /dev/null +++ b/docs/api-guide/exceptions.md @@ -0,0 +1,3 @@ +# Exceptions + + diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md new file mode 100644 index 00000000..2edc11de --- /dev/null +++ b/docs/api-guide/parsers.md @@ -0,0 +1,3 @@ +# Parsers + +## .parse(request) diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md new file mode 100644 index 00000000..5a66da69 --- /dev/null +++ b/docs/api-guide/renderers.md @@ -0,0 +1,4 @@ +# Renderers + +## .render(response) + diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md new file mode 100644 index 00000000..67ddfdac --- /dev/null +++ b/docs/api-guide/requests.md @@ -0,0 +1,66 @@ +# Requests + +> If you're doing REST-based web service stuff ... you should ignore request.POST. +> +> — Malcom Tredinnick, [Django developers group][cite] + +REST framework's `Request` class extends the standard `HttpRequest`, adding support for parsing multiple content types, allowing browser-based `PUT`, `DELETE` and other methods, and adding flexible per-request authentication. + +## .method + +`request.method` returns the uppercased string representation of the request's HTTP method. + +Browser-based `PUT`, `DELETE` and other requests are supported, and can be made by using a hidden form field named `_method` in a regular `POST` form. + + + +## .content_type + +`request.content`, returns a string object representing the mimetype of the HTTP request's body, if one exists. + + + +## .DATA + +`request.DATA` returns the parsed content of the request body. This is similar to the standard `HttpRequest.POST` attribute except that: + +1. It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests. +2. It supports parsing multiple content types, rather than just form data. For example you can handle incoming json data in the same way that you handle incoming form data. + +## .FILES + +`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing that is used for `request.DATA`. + +This allows you to support file uploads from multiple content-types. For example you can write a parser that supports `POST`ing the raw content of a file, instead of using form-encoded file uploads. + +## .user + +`request.user` returns a `django.contrib.auth.models.User` instance. + +## .auth + +`request.auth` returns any additional authentication context that may not be contained in `request.user`. The exact behavior of `request.auth` depends on what authentication has been set in `request.authentication`. For many types of authentication this will simply be `None`, but it may also be an object representing a permission scope, an expiry time, or any other information that might be contained in a token-based authentication scheme. + +## .parsers + +`request.parsers` should be set to a list of `Parser` instances that can be used to parse the content of the request body. + +`request.parsers` may no longer be altered once `request.DATA`, `request.FILES` or `request.POST` have been accessed. + +If you're using the `djangorestframework.views.View` class... **[TODO]** + +## .stream + +`request.stream` returns a stream representing the content of the request body. + +You will not typically need to access `request.stream`, unless you're writing a `Parser` class. + +## .authentication + +`request.authentication` should be set to a list of `Authentication` instances that can be used to authenticate the request. + +`request.authentication` may no longer be altered once `request.user` or `request.auth` have been accessed. + +If you're using the `djangorestframework.views.View` class... **[TODO]** + +[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion \ No newline at end of file diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md new file mode 100644 index 00000000..38f6e8cb --- /dev/null +++ b/docs/api-guide/responses.md @@ -0,0 +1,23 @@ +# Responses + +> Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process. +> +> — [Django documentation][cite] + +REST framework supports HTTP content negotiation by providing a `Response` class which allows you to return content that can be rendered into multiple content types, depending on the client request. + +The `Response` class subclasses Django's `TemplateResponse`. `Response` objects are initialised with content, which should consist of native python primatives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content. + +There's no requirement for you to use the `Response` class, you can also return regular `HttpResponse` objects from your views if you want, but it does provide a better interface for returning Web API responses. + +## Response(content, headers=None, renderers=None, view=None, format=None, status=None) + + +## .renderers + +## .view + +## .format + + +[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ \ No newline at end of file diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md new file mode 100644 index 00000000..377b0c10 --- /dev/null +++ b/docs/api-guide/serializers.md @@ -0,0 +1,241 @@ +# Serializers + +> Expanding the usefulness of the serializers is something that we would +like to address. However, it's not a trivial problem, and it +will take some serious design work. Any offers to help out in this +area would be gratefully accepted. +> +> — Russell Keith-Magee, [Django users group][cite] + +Serializers allow complex data such as querysets and model instances to be converted to native python datatypes that can then be easily rendered into `JSON`, `XML` or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data. + +REST framework's serializers work very similarly to Django's `Form` and `ModelForm` classes. It provides a `Serializer` class which gives you a powerful, generic way to control the output of your responses, as well as a `ModelSerializer` class which provides a useful shortcut for creating serializers that deal with model instances and querysets. + +## Declaring Serializers + +Let's start by creating a simple object we can use for example purposes: + + class Comment(object): + def __init__(self, email, content, created=None): + 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. +Declaring a serializer looks very similar to declaring a form: + + class CommentSerializer(serializers.Serializer): + email = serializers.EmailField() + content = serializers.CharField(max_length=200) + created = serializers.DateTimeField() + + def restore_object(self, attrs, instance=None): + if instance: + instance.title = attrs['title'] + instance.content = attrs['content'] + instance.created = attrs['created'] + return instance + return Comment(**attrs) + +The first part of serializer class defines the fields that get serialized/deserialized. The `restore_object` method defines how fully fledged instances get created when deserializing data. The `restore_object` method is optional, and is only required if we want our serializer to support deserialization. + +## Serializing objects + +We can now use `CommentSerializer` to serialize a comment, or list of comments. Again, using the `Serializer` class looks a lot like using a `Form` class. + + serializer = CommentSerializer(instance=comment) + serializer.data + # {'email': u'leila@example.com', 'content': u'foo bar', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)} + +At this point we've translated the model instance into python native datatypes. To finalise the serialization process we render the data into `json`. + + stream = JSONRenderer().render(data) + stream + # '{"email": "leila@example.com", "content": "foo bar", "created": "2012-08-22T16:20:09.822"}' + +## Deserializing objects + +Deserialization is similar. First we parse a stream into python native datatypes... + + data = JSONParser().parse(stream) + +...then we restore those native datatypes into a fully populated object instance. + + serializer = CommentSerializer(data) + serializer.is_valid() + # True + serializer.object + # + >>> serializer.deserialize('json', stream) + +## Validation + +When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. + +**TODO: Describe validation in more depth** + +## Dealing with nested objects + +The previous example is fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, +where some of the attributes of an object might not be simple datatypes such as strings, dates or integers. + +The `Serializer` class is itself a type of `Field`, and can be used to represent relationships where one object type is nested inside another. + + class UserSerializer(serializers.Serializer): + email = serializers.EmailField() + username = serializers.CharField() + + def restore_object(self, attrs, instance=None): + return User(**attrs) + + + class CommentSerializer(serializers.Serializer): + user = serializers.UserSerializer() + title = serializers.CharField() + content = serializers.CharField(max_length=200) + created = serializers.DateTimeField() + + def restore_object(self, attrs, instance=None): + return Comment(**attrs) + +## Creating 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 intial 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. + +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. + +Let's look at an example of serializing a class that represents an RGB color value: + + class Color(object): + """ + A color represented in the RGB colorspace. + """ + + def __init__(self, red, green, blue): + assert(red >= 0 and green >= 0 and blue >= 0) + assert(red < 256 and green < 256 and blue < 256) + self.red, self.green, self.blue = red, green, blue + + class ColourField(Field): + """ + Color objects are serialized into "rgb(#, #, #)" notation. + """ + + def to_native(self, obj): + return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue) + + def from_native(self, data): + data = data.strip('rgb(').rstrip(')') + red, green, blue = [int(col) for col in data.split(',')] + return Color(red, green, blue) + + +By default field values are treated as mapping to an attribute on the object. If you need to customize how the field value is accessed and set you need to override `.field_to_native()` and/or `.field_from_native()`. + +As an example, let's create a field that can be used represent the class name of the object being serialized: + + class ClassNameField(Field): + def field_to_native(self, obj, field_name): + """ + Serialize the object's class name, not an attribute of the object. + """ + return obj.__class__.__name__ + + def field_from_native(self, data, field_name, into): + """ + We don't want to set anything when we revert this field. + """ + pass + +--- + +# ModelSerializers + +Often you'll want serializer classes that map closely to model definitions. +The `ModelSerializer` class lets you automatically create a Serializer class with fields that corrospond to the Model fields. + + class AccountSerializer(ModelSerializer): + class Meta: + model = Account + +**[TODO: Explain model field to serializer field mapping in more detail]** + +## 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. + + class AccountSerializer(ModelSerializer): + url = CharField(source='get_absolute_url', readonly=True) + group = NaturalKeyField() + + class Meta: + model = Account + +Extra fields can corrospond to any property or callable on the model. + +## Relational fields + +When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation is to use the primary keys of the related instances. + +Alternative representations include serializing using natural keys, serializing complete nested representations, or serializing using a custom representation, such as a URL that uniquely identifies the model instances. + +The `PrimaryKeyField` and `NaturalKeyField` fields provide alternative flat representations. + +The `ModelSerializer` class can itself be used as a field, in order to serialize relationships using nested representations. + +The `RelatedField` class may be subclassed to create a custom represenation of a relationship. The subclass should override `.to_native()`, and optionally `.from_native()` if deserialization is supported. + +All the relational fields may be used for any relationship or reverse relationship on a model. + +## Specifying which fields should be included + +If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. + +For example: + + class AccountSerializer(ModelSerializer): + class Meta: + model = Account + exclude = ('id',) + +The `fields` and `exclude` options may also be set by passing them to the `serialize()` method. + +**[TODO: Possibly only allow .serialize(fields=…) in FixtureSerializer for backwards compatability, but remove for ModelSerializer]** + +## Specifiying nested serialization + +The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `nested` option: + + class AccountSerializer(ModelSerializer): + class Meta: + model = Account + exclude = ('id',) + nested = True + +The `nested` option may be set to either `True`, `False`, or an integer value. If given an integer value it indicates the depth of relationships that should be traversed before reverting to a flat representation. + +When serializing objects using a nested representation any occurances of recursion will be recognised, and will fall back to using a flat representation. + +The `nested` option may also be set by passing it to the `serialize()` method. + +**[TODO: Possibly only allow .serialize(nested=…) in FixtureSerializer]** + +## Customising the default fields used by a ModelSerializer + + class AccountSerializer(ModelSerializer): + class Meta: + model = Account + + def get_nested_field(self, model_field): + return ModelSerializer() + + def get_related_field(self, model_field): + return NaturalKeyField() + + def get_field(self, model_field): + return Field() + + +[cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md new file mode 100644 index 00000000..c1d45905 --- /dev/null +++ b/docs/api-guide/status-codes.md @@ -0,0 +1,93 @@ +# Status Codes + +> 418 I'm a teapot - Any attempt to brew coffee with a teapot should result in the error code "418 I'm a teapot". The resulting entity body MAY be short and stout. +> +> — [RFC 2324][rfc2324], Hyper Text Coffee Pot Control Protocol + +Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable. + + from djangorestframework import status + + def empty_view(self): + content = {'please move along': 'nothing to see here'} + return Response(content, status=status.HTTP_404_NOT_FOUND) + +The full set of HTTP status codes included in the `status` module is listed below. + +For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616] +and [RFC 6585][rfc6585]. + +## Informational - 1xx + +This class of status code indicates a provisional response. There are no 1xx status codes used in REST framework by default. + + HTTP_100_CONTINUE + HTTP_101_SWITCHING_PROTOCOLS + +## Successful - 2xx + +This class of status code indicates that the client's request was successfully received, understood, and accepted. + + HTTP_200_OK + HTTP_201_CREATED + HTTP_202_ACCEPTED + HTTP_203_NON_AUTHORITATIVE_INFORMATION + HTTP_204_NO_CONTENT + HTTP_205_RESET_CONTENT + HTTP_206_PARTIAL_CONTENT + +## 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. + + HTTP_300_MULTIPLE_CHOICES + HTTP_301_MOVED_PERMANENTLY + HTTP_302_FOUND + HTTP_303_SEE_OTHER + HTTP_304_NOT_MODIFIED + HTTP_305_USE_PROXY + HTTP_306_RESERVED + HTTP_307_TEMPORARY_REDIRECT + +## Client Error - 4xx + +The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. + + HTTP_400_BAD_REQUEST + HTTP_401_UNAUTHORIZED + HTTP_402_PAYMENT_REQUIRED + HTTP_403_FORBIDDEN + HTTP_404_NOT_FOUND + HTTP_405_METHOD_NOT_ALLOWED + HTTP_406_NOT_ACCEPTABLE + HTTP_407_PROXY_AUTHENTICATION_REQUIRED + HTTP_408_REQUEST_TIMEOUT + HTTP_409_CONFLICT + HTTP_410_GONE + HTTP_411_LENGTH_REQUIRED + HTTP_412_PRECONDITION_FAILED + HTTP_413_REQUEST_ENTITY_TOO_LARGE + HTTP_414_REQUEST_URI_TOO_LONG + HTTP_415_UNSUPPORTED_MEDIA_TYPE + HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE + HTTP_417_EXPECTATION_FAILED + HTTP_428_PRECONDITION_REQUIRED + HTTP_429_TOO_MANY_REQUESTS + HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE + +## Server Error - 5xx + +Response status codes beginning with the digit "5" indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. + + HTTP_500_INTERNAL_SERVER_ERROR + HTTP_501_NOT_IMPLEMENTED + HTTP_502_BAD_GATEWAY + HTTP_503_SERVICE_UNAVAILABLE + HTTP_504_GATEWAY_TIMEOUT + HTTP_505_HTTP_VERSION_NOT_SUPPORTED + HTTP_511_NETWORD_AUTHENTICATION_REQUIRED + + +[rfc2324]: http://www.ietf.org/rfc/rfc2324.txt +[rfc2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +[rfc6585]: http://tools.ietf.org/html/rfc6585 diff --git a/docs/api-guide/urls.md b/docs/api-guide/urls.md new file mode 100644 index 00000000..c39ff8f6 --- /dev/null +++ b/docs/api-guide/urls.md @@ -0,0 +1,41 @@ +# Returning URIs from your Web APIs + +> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. +> +> — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite] + +As a rule, it's probably better practice to return absolute URIs from you web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. + +The advantages of doing so are: + +* It's more explicit. +* It leaves less work for your API clients. +* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type. +* It allows use to easily do things like markup HTML representations with hyperlinks. + +REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API. + +There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier. + +## reverse(viewname, request, *args, **kwargs) + +Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. + + from djangorestframework.utils import reverse + from djangorestframework.views import APIView + + class MyView(APIView): + def get(self, request): + content = { + ... + 'url': reverse('year-summary', request, args=[1945]) + } + return Response(content) + +## reverse_lazy(viewname, request, *args, **kwargs) + +Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. + +[cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 +[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy \ No newline at end of file diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md new file mode 100644 index 00000000..dd1dbebe --- /dev/null +++ b/docs/api-guide/views.md @@ -0,0 +1,39 @@ +> Django's class based views are a welcome departure from the old-style views. +> +> — [Reinout van Rees][cite] + +# Views + +REST framework provides a simple `APIView` class, built on Django's `django.generics.views.View`. The `APIView` class ensures five main things: + +1. Any requests inside the view will become `Request` instances. +2. `Request` instances will have their `renderers` and `authentication` attributes automatically set. +3. `Response` instances will have their `parsers` and `serializer` attributes automatically set. +4. `APIException` exceptions will be caught and return appropriate responses. +5. Any permissions provided will be checked prior to passing the request to a handler method. + +Additionally there are a some minor extras, such as providing a default `options` handler, setting some common headers on the response prior to return, and providing the useful `initial()` and `final()` hooks. + +## APIView + +## Method handlers + +Describe that APIView handles regular .get(), .post(), .put(), .delete() etc... + +## .initial(request, *args, **kwargs) + +## .final(request, response, *args, **kwargs) + +## .parsers + +## .renderers + +## .serializer + +## .authentication + +## .permissions + +## .headers + +[cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html \ No newline at end of file -- cgit v1.2.3 From 40d1821f2d81f822fff9b85691073ab069edb847 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 10:01:43 +0100 Subject: Add missing doc files --- docs/api-guide/authentication.md | 9 +++++++++ docs/api-guide/permissions.md | 1 + docs/api-guide/reverse.md | 41 ++++++++++++++++++++++++++++++++++++++++ docs/api-guide/settings.md | 31 ++++++++++++++++++++++++++++++ docs/api-guide/throttling.md | 1 + docs/api-guide/urls.md | 41 ---------------------------------------- 6 files changed, 83 insertions(+), 41 deletions(-) create mode 100644 docs/api-guide/authentication.md create mode 100644 docs/api-guide/permissions.md create mode 100644 docs/api-guide/reverse.md create mode 100644 docs/api-guide/settings.md create mode 100644 docs/api-guide/throttling.md delete mode 100644 docs/api-guide/urls.md (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md new file mode 100644 index 00000000..76aaba8a --- /dev/null +++ b/docs/api-guide/authentication.md @@ -0,0 +1,9 @@ +# Authentication + +REST framework provides a number of authentication policies out of the box, and also allows you to implement custom policies. + +## BasicAuthentication + +## TokenBasicAuthentication + +## SessionAuthentication diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/api-guide/permissions.md @@ -0,0 +1 @@ + diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md new file mode 100644 index 00000000..c39ff8f6 --- /dev/null +++ b/docs/api-guide/reverse.md @@ -0,0 +1,41 @@ +# Returning URIs from your Web APIs + +> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. +> +> — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite] + +As a rule, it's probably better practice to return absolute URIs from you web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. + +The advantages of doing so are: + +* It's more explicit. +* It leaves less work for your API clients. +* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type. +* It allows use to easily do things like markup HTML representations with hyperlinks. + +REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API. + +There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier. + +## reverse(viewname, request, *args, **kwargs) + +Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. + + from djangorestframework.utils import reverse + from djangorestframework.views import APIView + + class MyView(APIView): + def get(self, request): + content = { + ... + 'url': reverse('year-summary', request, args=[1945]) + } + return Response(content) + +## reverse_lazy(viewname, request, *args, **kwargs) + +Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. + +[cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 +[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy \ No newline at end of file diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md new file mode 100644 index 00000000..c7bae30d --- /dev/null +++ b/docs/api-guide/settings.md @@ -0,0 +1,31 @@ +# Settings + +Settings for REST framework are all namespaced in the `API_SETTINGS` setting. +For example your project's `settings.py` file might look like this: + + API_SETTINGS = { + 'DEFAULT_RENDERERS': ( + 'djangorestframework.renderers.JSONRenderer', + 'djangorestframework.renderers.YAMLRenderer', + ) + 'DEFAULT_PARSERS': ( + 'djangorestframework.parsers.JSONParser', + 'djangorestframework.parsers.YAMLParser', + ) + } + +## DEFAULT_RENDERERS + +A list or tuple of renderer classes. + +Default: + + ( + 'djangorestframework.renderers.JSONRenderer', + 'djangorestframework.renderers.DocumentingHTMLRenderer')` + +## DEFAULT_PARSERS + +A list or tuple of parser classes. + +Default: `()` diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/api-guide/throttling.md @@ -0,0 +1 @@ + diff --git a/docs/api-guide/urls.md b/docs/api-guide/urls.md deleted file mode 100644 index c39ff8f6..00000000 --- a/docs/api-guide/urls.md +++ /dev/null @@ -1,41 +0,0 @@ -# Returning URIs from your Web APIs - -> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. -> -> — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite] - -As a rule, it's probably better practice to return absolute URIs from you web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. - -The advantages of doing so are: - -* It's more explicit. -* It leaves less work for your API clients. -* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type. -* It allows use to easily do things like markup HTML representations with hyperlinks. - -REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API. - -There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier. - -## reverse(viewname, request, *args, **kwargs) - -Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. - - from djangorestframework.utils import reverse - from djangorestframework.views import APIView - - class MyView(APIView): - def get(self, request): - content = { - ... - 'url': reverse('year-summary', request, args=[1945]) - } - return Response(content) - -## reverse_lazy(viewname, request, *args, **kwargs) - -Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. - -[cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 -[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy \ No newline at end of file -- cgit v1.2.3 From ef5279e97c0aa083d44489cf374fa75c7c8f53b7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 13:03:55 +0100 Subject: Improve docs --- docs/api-guide/authentication.md | 81 +++++++++++++++++++++++++++++++++++++++- docs/api-guide/settings.md | 74 +++++++++++++++++++++++++++++++++--- 2 files changed, 148 insertions(+), 7 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 76aaba8a..0a144a94 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -1,9 +1,88 @@ # Authentication +Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted. + REST framework provides a number of authentication policies out of the box, and also allows you to implement custom policies. -## BasicAuthentication +Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized. + +## Setting the authentication policy + +The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION_CLASSES` setting. For example. + + API_SETTINGS = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'djangorestframework.authentication.SessionAuthentication', + ) + } + +You can also set the authentication policy on a per-view basis, using the `APIView` class based views. + + class ExampleView(APIView): + authentication_classes = (SessionAuthentication,) + + def get(self, request, format=None): + content = { + 'user': unicode(request.user), # `django.contrib.auth.User` instance. + 'auth': unicode(request.auth), # None + } + return Response(content) + +Or, if you're using the `@api_view` decorator with function based views. + + @api_view(allowed=('GET',), authentication_classes=(SessionAuthentication,)) + def example_view(request, format=None): + content = { + 'user': unicode(request.user), # `django.contrib.auth.User` instance. + 'auth': unicode(request.auth), # None + } + return Response(content) + +## UserBasicAuthentication + +This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. User basic authentication is generally only appropriate for testing. + +**Note:** If you run `UserBasicAuthentication` in production your API must be `https` only, or it will be completely insecure. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. + +If successfully authenticated, `UserBasicAuthentication` provides the following credentials. + +* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.auth` will be `None`. ## TokenBasicAuthentication +This policy uses [HTTP Basic Authentication][basicauth], signed against a token key and secret. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. + +**Note:** If you run `TokenBasicAuthentication` in production your API must be `https` only, or it will be completely insecure. + +If successfully authenticated, `TokenBasicAuthentication` provides the following credentials. + +* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.auth` will be a `djangorestframework.models.BasicToken` instance. + +## OAuthAuthentication + +This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAuth is appropriate for server-server setups, such as when you want to allow a third-party service to access your API on a user's behalf. + +If successfully authenticated, `OAuthAuthentication` provides the following credentials. + +* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.auth` will be a `djangorestframework.models.OAuthToken` instance. + ## SessionAuthentication + +This policy uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website. + +If successfully authenticated, `SessionAuthentication` provides the following credentials. + +* `request.user` will be a `django.contrib.auth.models.User` instance. +* `request.auth` will be `None`. + +## Custom authentication policies + +To implement a custom authentication policy, subclass `BaseAuthentication` and override the `authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. + +[basicauth]: http://tools.ietf.org/html/rfc2617 +[oauth]: http://oauth.net/2/ +[permission]: permissions.md +[throttling]: throttling.md \ No newline at end of file diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index c7bae30d..af8c4ec9 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -5,27 +5,89 @@ For example your project's `settings.py` file might look like this: API_SETTINGS = { 'DEFAULT_RENDERERS': ( - 'djangorestframework.renderers.JSONRenderer', 'djangorestframework.renderers.YAMLRenderer', ) 'DEFAULT_PARSERS': ( - 'djangorestframework.parsers.JSONParser', 'djangorestframework.parsers.YAMLParser', ) } ## DEFAULT_RENDERERS -A list or tuple of renderer classes. +A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. Default: ( - 'djangorestframework.renderers.JSONRenderer', - 'djangorestframework.renderers.DocumentingHTMLRenderer')` + 'djangorestframework.renderers.JSONRenderer', + 'djangorestframework.renderers.DocumentingHTMLRenderer' + 'djangorestframework.renderers.TemplateHTMLRenderer' + ) ## DEFAULT_PARSERS -A list or tuple of parser classes. +A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.DATA` property. + +Default: + + ( + 'djangorestframework.parsers.JSONParser', + 'djangorestframework.parsers.FormParser' + ) + +## DEFAULT_AUTHENTICATION + +A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the `request.user` or `request.auth` properties. + +Default if `DEBUG` is `True`: + + ( + 'djangorestframework.authentication.SessionAuthentication', + 'djangorestframework.authentication.UserBasicAuthentication' + ) + +Default if `DEBUG` is `False`: + + ( + 'djangorestframework.authentication.SessionAuthentication', + ) + +## DEFAULT_PERMISSIONS + +Default: `()` + +## DEFAULT_THROTTLES Default: `()` + +## DEFAULT_MODEL_SERIALIZER + +Default: `djangorestframework.serializers.ModelSerializer` + +## DEFAULT_PAGINATION_SERIALIZER + +Default: `djangorestframework.pagination.PaginationSerializer` + +## FORMAT_SUFFIX_KWARG + +Default: `format` + +## UNAUTHENTICATED_USER_CLASS + +Default: `django.contrib.auth.models.AnonymousUser` + +## FORM_METHOD_OVERRIDE + +Default: `_method` + +## FORM_CONTENT_OVERRIDE + +Default: `_content` + +## FORM_CONTENTTYPE_OVERRIDE + +Default: `_content_type` + +## URL_ACCEPT_OVERRIDE + +Default: `_accept` -- cgit v1.2.3 From 3a106aed7958d8a3a31e39415e4b2a5514a41fa1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 20:10:06 +0100 Subject: Flesh out authentication docs --- docs/api-guide/authentication.md | 24 +++++++++++++++++++----- docs/api-guide/settings.md | 28 ++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 11 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 0a144a94..c663e2de 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -6,12 +6,21 @@ REST framework provides a number of authentication policies out of the box, and Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized. +## How authentication is determined + +Authentication is always set as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set `request.user` and `request.auth` using the return value of the first class that successfully authenticates. + +If no class authenticates, `request.user` will be set to an instance of `django.contrib.auth.models.AnonymousUser`, and `request.auth` will be set to `None`. + +The value of `request.user` and `request.auth` for unauthenticated requests can be modified using the [`UNAUTHENTICATED_USER`][UNAUTHENTICATED_USER] and [`UNAUTHENTICATED_TOKEN`][UNAUTHENTICATED_TOKEN] settings. + ## Setting the authentication policy -The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION_CLASSES` setting. For example. +The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION` setting. For example. API_SETTINGS = { - 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'DEFAULT_AUTHENTICATION': ( + 'djangorestframework.authentication.UserBasicAuthentication', 'djangorestframework.authentication.SessionAuthentication', ) } @@ -19,7 +28,7 @@ The default authentication policy may be set globally, using the `DEFAULT_AUTHEN You can also set the authentication policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): - authentication_classes = (SessionAuthentication,) + authentication_classes = (SessionAuthentication, UserBasicAuthentication) def get(self, request, format=None): content = { @@ -30,7 +39,10 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. - @api_view(allowed=('GET',), authentication_classes=(SessionAuthentication,)) + @api_view( + allowed=('GET',), + authentication_classes=(SessionAuthentication, UserBasicAuthentication) + ) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. @@ -85,4 +97,6 @@ To implement a custom authentication policy, subclass `BaseAuthentication` and o [basicauth]: http://tools.ietf.org/html/rfc2617 [oauth]: http://oauth.net/2/ [permission]: permissions.md -[throttling]: throttling.md \ No newline at end of file +[throttling]: throttling.md +[UNAUTHENTICATED_USER]: settings.md#UNAUTHENTICATED_USER +[UNAUTHENTICATED_TOKEN]: settings.md#UNAUTHENTICATED_TOKEN \ No newline at end of file diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index af8c4ec9..7ade76b0 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -70,24 +70,40 @@ Default: `djangorestframework.pagination.PaginationSerializer` ## FORMAT_SUFFIX_KWARG -Default: `format` +Default: `'format'` -## UNAUTHENTICATED_USER_CLASS +## UNAUTHENTICATED_USER + +The class that should be used to initialize `request.user` for unauthenticated requests. Default: `django.contrib.auth.models.AnonymousUser` +## UNAUTHENTICATED_USER + +The class that should be used to initialize `request.auth` for unauthenticated requests. + +Default: `None` + ## FORM_METHOD_OVERRIDE -Default: `_method` +The name of a form field that may be used to override the HTTP method of the form. + +Default: `'_method'` ## FORM_CONTENT_OVERRIDE -Default: `_content` +The name of a form field that may be used to override the content of the form payload. + +Default: `'_content'` ## FORM_CONTENTTYPE_OVERRIDE -Default: `_content_type` +The name of a form field that may be used to override the content type of the form payload. + +Default: `'_content_type'` ## URL_ACCEPT_OVERRIDE -Default: `_accept` +The name of a URL parameter that may be used to override the HTTP `Accept` header. + +Default: `'_accept'` -- cgit v1.2.3 From 224b538b319ad7558e73403278a3932d5564bf75 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Sep 2012 21:14:00 +0100 Subject: Tidying up docs --- docs/api-guide/authentication.md | 8 +++++--- docs/api-guide/reverse.md | 2 +- docs/api-guide/settings.md | 21 +++++++++++++++++---- 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index c663e2de..ed7ac288 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -6,13 +6,17 @@ REST framework provides a number of authentication policies out of the box, and Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized. +The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class. + +The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with. + ## How authentication is determined Authentication is always set as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set `request.user` and `request.auth` using the return value of the first class that successfully authenticates. If no class authenticates, `request.user` will be set to an instance of `django.contrib.auth.models.AnonymousUser`, and `request.auth` will be set to `None`. -The value of `request.user` and `request.auth` for unauthenticated requests can be modified using the [`UNAUTHENTICATED_USER`][UNAUTHENTICATED_USER] and [`UNAUTHENTICATED_TOKEN`][UNAUTHENTICATED_TOKEN] settings. +The value of `request.user` and `request.auth` for unauthenticated requests can be modified using the `UNAUTHENTICATED_USER` and `UNAUTHENTICATED_TOKEN` settings. ## Setting the authentication policy @@ -98,5 +102,3 @@ To implement a custom authentication policy, subclass `BaseAuthentication` and o [oauth]: http://oauth.net/2/ [permission]: permissions.md [throttling]: throttling.md -[UNAUTHENTICATED_USER]: settings.md#UNAUTHENTICATED_USER -[UNAUTHENTICATED_TOKEN]: settings.md#UNAUTHENTICATED_TOKEN \ No newline at end of file diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index c39ff8f6..5a1d6e26 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -11,7 +11,7 @@ The advantages of doing so are: * It's more explicit. * It leaves less work for your API clients. * There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type. -* It allows use to easily do things like markup HTML representations with hyperlinks. +* It makes it easy to do things like markup HTML representations with hyperlinks. REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 7ade76b0..fd35fbc6 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -1,6 +1,7 @@ # Settings -Settings for REST framework are all namespaced in the `API_SETTINGS` setting. +Configuration for REST framework is all namespaced inside the `API_SETTINGS` setting. + For example your project's `settings.py` file might look like this: API_SETTINGS = { @@ -54,10 +55,14 @@ Default if `DEBUG` is `False`: ## DEFAULT_PERMISSIONS +A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. + Default: `()` ## DEFAULT_THROTTLES +A list or tuple of throttle classes, that determines the default set of throttles checked at the start of a view. + Default: `()` ## DEFAULT_MODEL_SERIALIZER @@ -78,7 +83,7 @@ The class that should be used to initialize `request.user` for unauthenticated r Default: `django.contrib.auth.models.AnonymousUser` -## UNAUTHENTICATED_USER +## UNAUTHENTICATED_TOKEN The class that should be used to initialize `request.auth` for unauthenticated requests. @@ -88,17 +93,23 @@ Default: `None` The name of a form field that may be used to override the HTTP method of the form. +If the value of this setting is `None` then form method overloading will be disabled. + Default: `'_method'` ## FORM_CONTENT_OVERRIDE -The name of a form field that may be used to override the content of the form payload. +The name of a form field that may be used to override the content of the form payload. Must be used together with `FORM_CONTENTTYPE_OVERRIDE`. + +If either setting is `None` then form content overloading will be disabled. Default: `'_content'` ## FORM_CONTENTTYPE_OVERRIDE -The name of a form field that may be used to override the content type of the form payload. +The name of a form field that may be used to override the content type of the form payload. Must be used together with `FORM_CONTENT_OVERRIDE`. + +If either setting is `None` then form content overloading will be disabled. Default: `'_content_type'` @@ -106,4 +117,6 @@ Default: `'_content_type'` The name of a URL parameter that may be used to override the HTTP `Accept` header. +If the value of this setting is `None` then URL accept overloading will be disabled. + Default: `'_accept'` -- cgit v1.2.3 From c707034649fa9e24f0c6c3c0580dc06f90eac373 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 6 Sep 2012 15:57:16 +0100 Subject: Add more settings to settings.py --- docs/api-guide/settings.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index fd35fbc6..882571e1 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -40,19 +40,13 @@ Default: A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the `request.user` or `request.auth` properties. -Default if `DEBUG` is `True`: +Default: ( 'djangorestframework.authentication.SessionAuthentication', 'djangorestframework.authentication.UserBasicAuthentication' ) -Default if `DEBUG` is `False`: - - ( - 'djangorestframework.authentication.SessionAuthentication', - ) - ## DEFAULT_PERMISSIONS A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. -- cgit v1.2.3 From c648f2786f37ffe0b64ed2d85de2b7b491ee341b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 6 Sep 2012 16:46:56 +0100 Subject: TODO notes in docs --- docs/api-guide/settings.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 882571e1..1411b9ec 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -13,6 +13,17 @@ For example your project's `settings.py` file might look like this: ) } +## Accessing settings + +If you need to access the values of REST framework's API settings in your project, +you should use the `api_settings` object. For example. + + from djangorestframework.settings import api_settings + + print api_settings.DEFAULT_AUTHENTICATION + +The `api_settings` object will check for any user-defined settings, and otherwise fallback to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal. + ## DEFAULT_RENDERERS A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. @@ -61,14 +72,20 @@ Default: `()` ## DEFAULT_MODEL_SERIALIZER +**TODO** + Default: `djangorestframework.serializers.ModelSerializer` ## DEFAULT_PAGINATION_SERIALIZER +**TODO** + Default: `djangorestframework.pagination.PaginationSerializer` ## FORMAT_SUFFIX_KWARG +**TODO** + Default: `'format'` ## UNAUTHENTICATED_USER -- cgit v1.2.3 From 36cd91bbbe6bc0aeef9b1eb711415988f5c4e501 Mon Sep 17 00:00:00 2001 From: Mjumbe Wawatu Poe Date: Fri, 7 Sep 2012 14:12:46 -0400 Subject: Update docs for tokenauth --- docs/api-guide/authentication.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index ed7ac288..c5e4c1cc 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -8,7 +8,7 @@ Authentication will run the first time either the `request.user` or `request.aut The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class. -The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with. +The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with. ## How authentication is determined @@ -36,7 +36,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi def get(self, request, format=None): content = { - 'user': unicode(request.user), # `django.contrib.auth.User` instance. + 'user': unicode(request.user), # `django.contrib.auth.User` instance. 'auth': unicode(request.auth), # None } return Response(content) @@ -49,7 +49,7 @@ Or, if you're using the `@api_view` decorator with function based views. ) def example_view(request, format=None): content = { - 'user': unicode(request.user), # `django.contrib.auth.User` instance. + 'user': unicode(request.user), # `django.contrib.auth.User` instance. 'auth': unicode(request.auth), # None } return Response(content) @@ -65,16 +65,20 @@ If successfully authenticated, `UserBasicAuthentication` provides the following * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be `None`. -## TokenBasicAuthentication +## TokenAuthentication -This policy uses [HTTP Basic Authentication][basicauth], signed against a token key and secret. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. +This policy uses [HTTP Authentication][basicauth] with a custom authentication scheme called "Token". Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. The token key should be passed in as a string to the "Authorization" HTTP header. For example: -**Note:** If you run `TokenBasicAuthentication` in production your API must be `https` only, or it will be completely insecure. + curl http://my.api.org/ -X POST -H "Authorization: Token 0123456789abcdef0123456789abcdef" -If successfully authenticated, `TokenBasicAuthentication` provides the following credentials. +**Note:** If you run `TokenAuthentication` in production your API must be `https` only, or it will be completely insecure. + +If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.models.BasicToken` instance. +* `request.auth` will be a `djangorestframework.tokenauth.models.Token` instance. + +To use the `TokenAuthentication` scheme, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications. To use your own token model, subclass the `djangorestframework.tokenauth.authentication.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. For convenience, the `djangorestframework.tokenauth.models.BaseToken` abstract model implements this minimum contract, and also randomly populates the key field when none is provided. ## OAuthAuthentication -- cgit v1.2.3 From 3b1404bd7d37d8c60cf45071852f86eea8d4c68f Mon Sep 17 00:00:00 2001 From: Mjumbe Wawatu Poe Date: Fri, 7 Sep 2012 14:23:53 -0400 Subject: Rename the default token class to "BasicToken" --- docs/api-guide/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index c5e4c1cc..c5b7ac9c 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -76,7 +76,7 @@ This policy uses [HTTP Authentication][basicauth] with a custom authentication s If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.tokenauth.models.Token` instance. +* `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. To use the `TokenAuthentication` scheme, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications. To use your own token model, subclass the `djangorestframework.tokenauth.authentication.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. For convenience, the `djangorestframework.tokenauth.models.BaseToken` abstract model implements this minimum contract, and also randomly populates the key field when none is provided. -- cgit v1.2.3 From f741cdae44bc455089a5ed7e1dbea4760ca97b85 Mon Sep 17 00:00:00 2001 From: Mjumbe Wawatu Poe Date: Fri, 7 Sep 2012 16:12:33 -0400 Subject: Move TokenAuthentication class into djangorestframework.authentication --- docs/api-guide/authentication.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index c5b7ac9c..5f176d02 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -67,9 +67,9 @@ If successfully authenticated, `UserBasicAuthentication` provides the following ## TokenAuthentication -This policy uses [HTTP Authentication][basicauth] with a custom authentication scheme called "Token". Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. The token key should be passed in as a string to the "Authorization" HTTP header. For example: +This policy uses [HTTP Authentication][basicauth] with no authentication scheme. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. The token key should be passed in as a string to the "Authorization" HTTP header. For example: - curl http://my.api.org/ -X POST -H "Authorization: Token 0123456789abcdef0123456789abcdef" + curl http://my.api.org/ -X POST -H "Authorization: 0123456789abcdef0123456789abcdef" **Note:** If you run `TokenAuthentication` in production your API must be `https` only, or it will be completely insecure. @@ -78,7 +78,7 @@ If successfully authenticated, `TokenAuthentication` provides the following cred * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. -To use the `TokenAuthentication` scheme, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications. To use your own token model, subclass the `djangorestframework.tokenauth.authentication.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. For convenience, the `djangorestframework.tokenauth.models.BaseToken` abstract model implements this minimum contract, and also randomly populates the key field when none is provided. +To use the `TokenAuthentication` policy, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications and sync your database. To use your own token model, subclass the `djangorestframework.tokenauth.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. For convenience, the `djangorestframework.tokenauth.models.BaseToken` abstract model implements this minimum contract, and also randomly populates the key field when none is provided. ## OAuthAuthentication -- cgit v1.2.3 From 8df71f4d1d9a8a3df8e053d99340fbe5bf78b8ad Mon Sep 17 00:00:00 2001 From: Mjumbe Wawatu Poe Date: Fri, 7 Sep 2012 16:19:15 -0400 Subject: Get rid of the BaseToken abstract model --- docs/api-guide/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 5f176d02..45da2c55 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -78,7 +78,7 @@ If successfully authenticated, `TokenAuthentication` provides the following cred * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. -To use the `TokenAuthentication` policy, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications and sync your database. To use your own token model, subclass the `djangorestframework.tokenauth.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. For convenience, the `djangorestframework.tokenauth.models.BaseToken` abstract model implements this minimum contract, and also randomly populates the key field when none is provided. +To use the `TokenAuthentication` policy, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications and sync your database. To use your own token model, subclass the `djangorestframework.tokenauth.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. Refer to the `djangorestframework.tokenauth.models.BasicToken` model as an example. ## OAuthAuthentication -- cgit v1.2.3 From 247696e820dfe4535b3141d744129d654f9b6aea Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 8 Sep 2012 22:06:13 +0100 Subject: Links to source files in docs --- docs/api-guide/authentication.md | 2 ++ docs/api-guide/exceptions.md | 2 ++ docs/api-guide/parsers.md | 2 ++ docs/api-guide/permissions.md | 2 ++ docs/api-guide/renderers.md | 2 ++ docs/api-guide/requests.md | 2 ++ docs/api-guide/responses.md | 2 ++ docs/api-guide/reverse.md | 2 ++ docs/api-guide/serializers.md | 2 ++ docs/api-guide/settings.md | 2 ++ docs/api-guide/status-codes.md | 2 ++ docs/api-guide/throttling.md | 2 ++ docs/api-guide/views.md | 2 ++ 13 files changed, 26 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 45da2c55..ca29bc4d 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -1,3 +1,5 @@ + + # Authentication Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index d41327c6..bb3ed56e 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -1,3 +1,5 @@ + + # Exceptions diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 2edc11de..5e2344a3 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -1,3 +1,5 @@ + + # Parsers ## .parse(request) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 8b137891..2e15107c 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -1 +1,3 @@ + +# Permissions \ No newline at end of file diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 5a66da69..1cd6d1a0 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -1,3 +1,5 @@ + + # Renderers ## .render(response) diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 67ddfdac..6746bb20 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -1,3 +1,5 @@ + + # Requests > If you're doing REST-based web service stuff ... you should ignore request.POST. diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 38f6e8cb..6c279f17 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -1,3 +1,5 @@ + + # Responses > Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process. diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 5a1d6e26..6e42b68e 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -1,3 +1,5 @@ + + # Returning URIs from your Web APIs > The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 377b0c10..38a1e560 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1,3 +1,5 @@ + + # Serializers > Expanding the usefulness of the serializers is something that we would diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 1411b9ec..ae8dce76 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -1,3 +1,5 @@ + + # Settings Configuration for REST framework is all namespaced inside the `API_SETTINGS` setting. diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index c1d45905..6693c79f 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -1,3 +1,5 @@ + + # Status Codes > 418 I'm a teapot - Any attempt to brew coffee with a teapot should result in the error code "418 I'm a teapot". The resulting entity body MAY be short and stout. diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 8b137891..e3a66c83 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -1 +1,3 @@ + +# Throttling diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index dd1dbebe..43924544 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,3 +1,5 @@ + + > Django's class based views are a welcome departure from the old-style views. > > — [Reinout van Rees][cite] -- cgit v1.2.3 From b5e07abc15f0937263e183b7800f30b558e37a1b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 8 Sep 2012 22:13:11 +0100 Subject: Move quote below title for consistency --- docs/api-guide/views.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 43924544..04647f91 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,11 +1,11 @@ +# Views + > Django's class based views are a welcome departure from the old-style views. > > — [Reinout van Rees][cite] -# Views - REST framework provides a simple `APIView` class, built on Django's `django.generics.views.View`. The `APIView` class ensures five main things: 1. Any requests inside the view will become `Request` instances. -- cgit v1.2.3 From c85f799ade0710dd27838e8bfc78989c80213d6a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Sep 2012 10:12:13 +0100 Subject: Updating docs --- docs/api-guide/authentication.md | 15 ++++-- docs/api-guide/content-negotiation.md | 7 +++ docs/api-guide/contentnegotiation.md | 1 - docs/api-guide/exceptions.md | 79 ++++++++++++++++++++++++++++++++ docs/api-guide/format-suffixes.md | 11 +++++ docs/api-guide/generic-views.md | 10 ++++ docs/api-guide/permissions.md | 86 ++++++++++++++++++++++++++++++++++- docs/api-guide/reverse.md | 4 +- docs/api-guide/settings.md | 10 +++- docs/api-guide/throttling.md | 12 +++++ 10 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 docs/api-guide/content-negotiation.md delete mode 100644 docs/api-guide/contentnegotiation.md create mode 100644 docs/api-guide/format-suffixes.md create mode 100644 docs/api-guide/generic-views.md (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index ca29bc4d..f2878f19 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -2,6 +2,10 @@ # Authentication +> Auth needs to be pluggable. +> +> — Jacob Kaplan-Moss, ["REST worst practices"][cite] + Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted. REST framework provides a number of authentication policies out of the box, and also allows you to implement custom policies. @@ -60,7 +64,7 @@ Or, if you're using the `@api_view` decorator with function based views. This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. User basic authentication is generally only appropriate for testing. -**Note:** If you run `UserBasicAuthentication` in production your API must be `https` only, or it will be completely insecure. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. +**Note:** If you run `UserBasicAuthentication` in production your API should be `https` only. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. If successfully authenticated, `UserBasicAuthentication` provides the following credentials. @@ -69,11 +73,13 @@ If successfully authenticated, `UserBasicAuthentication` provides the following ## TokenAuthentication -This policy uses [HTTP Authentication][basicauth] with no authentication scheme. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. The token key should be passed in as a string to the "Authorization" HTTP header. For example: +This policy uses simple token-based HTTP Authentication. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. + +The token key should be passed in as a string to the "Authorization" HTTP header. For example: curl http://my.api.org/ -X POST -H "Authorization: 0123456789abcdef0123456789abcdef" -**Note:** If you run `TokenAuthentication` in production your API must be `https` only, or it will be completely insecure. +**Note:** If you run `TokenAuthentication` in production your API should be `https` only. If successfully authenticated, `TokenAuthentication` provides the following credentials. @@ -102,8 +108,9 @@ If successfully authenticated, `SessionAuthentication` provides the following cr ## Custom authentication policies -To implement a custom authentication policy, subclass `BaseAuthentication` and override the `authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. +To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. +[cite]: http://jacobian.org/writing/rest-worst-practices/ [basicauth]: http://tools.ietf.org/html/rfc2617 [oauth]: http://oauth.net/2/ [permission]: permissions.md diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md new file mode 100644 index 00000000..01895a4b --- /dev/null +++ b/docs/api-guide/content-negotiation.md @@ -0,0 +1,7 @@ +# Content negotiation + +> HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available. +> +> — [RFC 2616][cite], Fielding et al. + +[cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html diff --git a/docs/api-guide/contentnegotiation.md b/docs/api-guide/contentnegotiation.md deleted file mode 100644 index f01627d8..00000000 --- a/docs/api-guide/contentnegotiation.md +++ /dev/null @@ -1 +0,0 @@ -> HTTP has provisions for several mechanisms for "content negotiation" -- the process of selecting the best representation for a given response when there are multiple representations available. -- RFC 2616, Fielding et al. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index bb3ed56e..c8ccb08b 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -2,4 +2,83 @@ # Exceptions +> Exceptions… allow error handling to be organized cleanly in a central or high-level place within the program structure. +> +> — Doug Hellmann, [Python Exception Handling Techniques][cite] +## Exception handling in REST framework views + +REST framework's views handle various exceptions, and deal with returning appropriate error responses for you. + +The handled exceptions are: + +* Subclasses of `APIException` raised inside REST framework. +* Django's `Http404` exception. +* Django's `PermissionDenied` exception. + +In each case, REST framework will return a response, rendering it to an appropriate content-type. + +By default all error messages will include a key `details` in the body of the response, but other keys may also be included. + +For example, the following request: + + DELETE http://api.example.com/foo/bar HTTP/1.1 + Accept: application/json + +Might recieve an error response indicating that the `DELETE` method is not allowed on that resource: + + HTTP/1.1 405 Method Not Allowed + Content-Type: application/json; charset=utf-8 + Content-Length: 42 + + {"detail": "Method 'DELETE' not allowed."} + +## APIException + +**Signature:** `APIException(detail=None)` + +The base class for all exceptions raised inside REST framework. + +To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class. + +## ParseError + +**Signature:** `ParseError(detail=None)` + +Raised if the request contains malformed data when accessing `request.DATA` or `request.FILES`. + +By default this exception results in a response with the HTTP status code "400 Bad Request". + +## PermissionDenied + +**Signature:** `PermissionDenied(detail=None)` + +Raised when an incoming request fails the permission checks. + +By default this exception results in a response with the HTTP status code "403 Forbidden". + +## MethodNotAllowed + +**Signature:** `MethodNotAllowed(method, detail=None)` + +Raised when an incoming request occurs that does not map to a handler method on the view. + +By default this exception results in a response with the HTTP status code "405 Method Not Allowed". + +## UnsupportedMediaType + +**Signature:** `UnsupportedMediaType(media_type, detail=None)` + +Raised if there are no parsers that can handle the content type of the request data when accessing `request.DATA` or `request.FILES`. + +By default this exception results in a response with the HTTP status code "415 Unsupported Media Type". + +## Throttled + +**Signature:** `Throttled(wait=None, detail=None)` + +Raised when an incoming request fails the throttling checks. + +By default this exception results in a response with the HTTP status code "429 Too Many Requests". + +[cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html \ No newline at end of file diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md new file mode 100644 index 00000000..7d72d9f8 --- /dev/null +++ b/docs/api-guide/format-suffixes.md @@ -0,0 +1,11 @@ + + +# Format suffixes + +> Section 6.2.1 does not say that content negotiation should be +used all the time. +> +> — Roy Fielding, [REST discuss mailing list][cite] + +[cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857 + diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md new file mode 100644 index 00000000..202875a4 --- /dev/null +++ b/docs/api-guide/generic-views.md @@ -0,0 +1,10 @@ + + + +# Generic views + +> Django’s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself. +> +> — [Django Documentation][cite] + +[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 2e15107c..be22eefe 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -1,3 +1,87 @@ -# Permissions \ No newline at end of file +# Permissions + +> Authentication or identification by itself is not usually sufficient to gain access to information or code. For that, the entity requesting access must have authorization. +> +> — [Apple Developer Documentation][cite] + +Together with [authentication] and [throttling], permissions determine wheter a request should be granted or denied access. + +Permission checks are always run at the very start of the view, before any other code is allowed to proceed. Permission checks will typically use the authentication information in the `request.user` and `request.auth` properties to determine if the incoming request should be permitted. + +## How permissions are determined + +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. + +## Object level permissions + +REST framework permissions also support object-level permissioning. Object level permissions are used to determine if a user should be allowed to act on a particular object, which will typically be a model instance. + +Object level permissions are run by REST framework's generic views when `.get_object()` is called. As with view level permissions, an `exceptions.PermissionDenied` exception will be raised if the user is not allowed to act on the given object. + +## Setting the permission policy + +The default permission policy may be set globally, using the `DEFAULT_PERMISSIONS` setting. For example. + + API_SETTINGS = { + 'DEFAULT_PERMISSIONS': ( + 'djangorestframework.permissions.IsAuthenticated', + ) + } + +You can also set the authentication policy on a per-view basis, using the `APIView` class based views. + + class ExampleView(APIView): + permission_classes = (IsAuthenticated,) + + def get(self, request, format=None): + content = { + 'status': 'request was permitted' + } + return Response(content) + +Or, if you're using the `@api_view` decorator with function based views. + + @api_view('GET') + @permission_classes(IsAuthenticated) + def example_view(request, format=None): + content = { + 'status': 'request was permitted' + } + return Response(content) + +## IsAuthenticated + +The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise. + +This permission is suitable if you want your API to only be accessible to registered users. + +## IsAdminUser + +The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff`is `True` in which case permission will be allowed. + +This permission is suitable is you want your API to only be accessible to a subset of trusted administrators. + +## IsAuthenticatedOrReadOnly + +The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthorised users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`. + +This permission is suitable if you want to your API to allow read permissions to anonymous users, and only allow write permissions to authenticated users. + +## DjangoModelPermissions + +This permission class ties into Django's standard `django.contrib.auth` model permissions. When applied to a view that has a `.model` property, permission will only be granted if the user + +## Custom permissions + +To implement a custom permission, override `BasePermission` and implement the `.check_permission(self, request, obj=None)` method. + +The method should return `True` if the request should be granted access, and `False` otherwise. + + +[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html +[authentication]: authentication.md +[throttling]: throttling.md \ No newline at end of file diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 6e42b68e..f3cb0c64 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -1,12 +1,12 @@ -# Returning URIs from your Web APIs +# Returning URLs > The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. > > — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite] -As a rule, it's probably better practice to return absolute URIs from you web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. +As a rule, it's probably better practice to return absolute URIs from you Web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. The advantages of doing so are: diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index ae8dce76..2513928c 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -2,9 +2,13 @@ # Settings -Configuration for REST framework is all namespaced inside the `API_SETTINGS` setting. +> Namespaces are one honking great idea - let's do more of those! +> +> — [The Zen of Python][cite] -For example your project's `settings.py` file might look like this: +Configuration for REST framework is all namespaced inside a single Django setting, named `API_SETTINGS`. + +For example your project's `settings.py` file might include something like this: API_SETTINGS = { 'DEFAULT_RENDERERS': ( @@ -133,3 +137,5 @@ The name of a URL parameter that may be used to override the HTTP `Accept` heade If the value of this setting is `None` then URL accept overloading will be disabled. Default: `'_accept'` + +[cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index e3a66c83..ae8f7b98 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -1,3 +1,15 @@ # Throttling + +> HTTP/1.1 420 Enhance Your Calm +> +> [Twitter API rate limiting response][cite] + +[cite]: https://dev.twitter.com/docs/error-codes-responses + +## PerUserThrottle + +## PerViewThrottle + +## Custom throttles \ No newline at end of file -- cgit v1.2.3 From dac4cb9e8bf107f407ed8754bbef0ce97e79beb2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Sep 2012 13:11:26 +0100 Subject: GitHub link in toolbar --- docs/api-guide/exceptions.md | 6 +++--- docs/api-guide/permissions.md | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index c8ccb08b..c22d6d8b 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -8,7 +8,7 @@ ## Exception handling in REST framework views -REST framework's views handle various exceptions, and deal with returning appropriate error responses for you. +REST framework's views handle various exceptions, and deal with returning appropriate error responses. The handled exceptions are: @@ -16,9 +16,9 @@ The handled exceptions are: * Django's `Http404` exception. * Django's `PermissionDenied` exception. -In each case, REST framework will return a response, rendering it to an appropriate content-type. +In each case, REST framework will return a response with an appropriate status code and content-type. The body of the response will include any additional details regarding the nature of the error. -By default all error messages will include a key `details` in the body of the response, but other keys may also be included. +By default all error responses will include a key `details` in the body of the response, but other keys may also be included. For example, the following request: diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index be22eefe..e0f3583f 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -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. Before running the main body of the view, each permission in the list is checked. +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. @@ -73,7 +73,18 @@ This permission is suitable if you want to your API to allow read permissions to ## DjangoModelPermissions -This permission class ties into Django's standard `django.contrib.auth` model permissions. When applied to a view that has a `.model` property, permission will only be granted if the user +This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user has the relevant model permissions assigned. + +* `POST` requests require the user to have the `add` permission on the model. +* `PUT` and `PATCH` requests require the user to have the `change` permission on the model. +* `DELETE` requests require the user to have the `delete` permission on the model. + +The default behaviour can also be overridden to support custom model permissions. For example, you might want to include a `view` model permission for `GET` requests. + +To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details. + +The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] should work just fine with `DjangoModelPermissions` without any custom configuration required. + ## Custom permissions @@ -84,4 +95,6 @@ The method should return `True` if the request should be granted access, and `Fa [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md -[throttling]: throttling.md \ No newline at end of file +[throttling]: throttling.md +[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions +[guardian]: https://github.com/lukaszb/django-guardian \ No newline at end of file -- cgit v1.2.3 From 003a65f0e094e59b5462fcd0607bf290d80cc5aa Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Sep 2012 20:39:22 +0100 Subject: Tweaks to Token auth --- docs/api-guide/authentication.md | 27 +++++++++++++++++---------- docs/api-guide/permissions.md | 3 +-- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index f2878f19..777106e8 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -60,33 +60,40 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) -## UserBasicAuthentication +## BasicAuthentication -This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. User basic authentication is generally only appropriate for testing. +This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. Basic authentication is generally only appropriate for testing. -**Note:** If you run `UserBasicAuthentication` in production your API should be `https` only. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. - -If successfully authenticated, `UserBasicAuthentication` provides the following credentials. +If successfully authenticated, `BasicAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be `None`. +**Note:** If you use `BasicAuthentication` in production you must ensure that your API is only available over `https` only. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage. + ## TokenAuthentication -This policy uses simple token-based HTTP Authentication. Token basic authentication is appropriate for client-server setups, such as native desktop and mobile clients. +This policy uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients. + +To use the `TokenAuthentication` policy, include `djangorestframework.authtoken` in your `INSTALLED_APPS` setting. + +You'll also need to create tokens for your users. + + from djangorestframework.authtoken.models import Token -The token key should be passed in as a string to the "Authorization" HTTP header. For example: + token = Token.objects.create(user=...) + print token.key - curl http://my.api.org/ -X POST -H "Authorization: 0123456789abcdef0123456789abcdef" +For clients to authenticate, the token key should be included in the `Authorization` HTTP header. The key should be prefixed by the string literal "Token", with whitespace seperating the two strings. For example: -**Note:** If you run `TokenAuthentication` in production your API should be `https` only. + Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. -To use the `TokenAuthentication` policy, you must have a token model. Django REST Framework comes with a minimal default token model. To use it, include `djangorestframework.tokenauth` in your installed applications and sync your database. To use your own token model, subclass the `djangorestframework.tokenauth.TokenAuthentication` class and specify a `model` attribute that references your custom token model. The token model must provide `user`, `key`, and `revoked` attributes. Refer to the `djangorestframework.tokenauth.models.BasicToken` model as an example. +**Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only. ## OAuthAuthentication diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index e0f3583f..bd107462 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -83,8 +83,7 @@ The default behaviour can also be overridden to support custom model permissions To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details. -The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] should work just fine with `DjangoModelPermissions` without any custom configuration required. - +The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required. ## Custom permissions -- cgit v1.2.3 From b16c45aa6dfb5937d01f0c89273cd24f5e729f60 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Sep 2012 09:39:16 +0100 Subject: Tweak throttling/permissions/auth docs --- docs/api-guide/authentication.md | 2 +- docs/api-guide/permissions.md | 3 +- docs/api-guide/throttling.md | 67 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 5 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 777106e8..79950946 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -18,7 +18,7 @@ The `request.auth` property is used for any additional authentication informatio ## How authentication is determined -Authentication is always set as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set `request.user` and `request.auth` using the return value of the first class that successfully authenticates. +The authentication policy is always defined as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set `request.user` and `request.auth` using the return value of the first class that successfully authenticates. If no class authenticates, `request.user` will be set to an instance of `django.contrib.auth.models.AnonymousUser`, and `request.auth` will be set to `None`. diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index bd107462..8d5e5140 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -12,8 +12,9 @@ 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. Before running the main body of the view each permission in the list is checked. +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. ## Object level permissions diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index ae8f7b98..b958d2b5 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -8,8 +8,69 @@ [cite]: https://dev.twitter.com/docs/error-codes-responses -## PerUserThrottle +Throttling is similar to [permissions], in that it determines if a request should be authorized. Throttles indicate a temporary state, and are used to control the rate of requests that clients can make to an API. -## PerViewThrottle +As with permissions, multiple throttles may be used. Your API might have a restrictive throttle for unauthenticated requests, and a less restrictive throttle for authenticated requests. -## Custom throttles \ No newline at end of file +Another scenario where you might want to use multiple throttles would be if you need to impose different constraints on different parts of the API, due ato some services being particularly resource-intensive. + +Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth. + +## How throttling is determined + +As with permissions and authentication, throttling in REST framework is always defined as a list of classes. + +Before running the main body of the view each throttle in the list is checked. +If any throttle check fails an `exceptions.Throttled` exception will be raised, and the main body of the view will not run. + +## Setting the throttling policy + +The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` setting. For example. + + API_SETTINGS = { + 'DEFAULT_THROTTLES': ( + 'djangorestframework.throttles.AnonThrottle', + 'djangorestframework.throttles.UserThrottle', + ) + 'DEFAULT_THROTTLE_RATES': { + 'anon': '100/day', + 'user': '1000/day' + } + } + +You can also set the throttling policy on a per-view basis, using the `APIView` class based views. + + class ExampleView(APIView): + throttle_classes = (UserThrottle,) + + def get(self, request, format=None): + content = { + 'status': 'request was permitted' + } + return Response(content) + +Or, if you're using the `@api_view` decorator with function based views. + + @api_view('GET') + @throttle_classes(UserThrottle) + def example_view(request, format=None): + content = { + 'status': 'request was permitted' + } + return Response(content) + +## AnonThrottle + +The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to identify + +`AnonThrottle` is suitable if you want to restrict the rate of requests from unknown sources. + +## UserThrottle + +`UserThrottle` is suitable if you want a simple restriction + +## ScopedThrottle + +## Custom throttles + +[permissions]: permissions.md \ No newline at end of file -- cgit v1.2.3 From 6c109ac60f891955df367c61d4d7094039098076 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Sep 2012 18:32:56 +0100 Subject: Improve throttles and docs --- docs/api-guide/permissions.md | 2 +- docs/api-guide/throttling.md | 69 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 9 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 8d5e5140..fafef305 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -88,7 +88,7 @@ The `DjangoModelPermissions` class also supports object-level permissions. Thir ## Custom permissions -To implement a custom permission, override `BasePermission` and implement the `.check_permission(self, request, obj=None)` method. +To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, obj=None)` method. The method should return `True` if the request should be granted access, and `False` otherwise. diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index b958d2b5..acad82cd 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -12,9 +12,11 @@ Throttling is similar to [permissions], in that it determines if a request shoul As with permissions, multiple throttles may be used. Your API might have a restrictive throttle for unauthenticated requests, and a less restrictive throttle for authenticated requests. -Another scenario where you might want to use multiple throttles would be if you need to impose different constraints on different parts of the API, due ato some services being particularly resource-intensive. +Another scenario where you might want to use multiple throttles would be if you need to impose different constraints on different parts of the API, due to some services being particularly resource-intensive. -Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth. +Multiple throttles can also be used if you want to impose both burst throttling rates, and sustained throttling rates. For example, you might want to limit a user to a maximum of 60 requests per minute, and 1000 requests per day. + +Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth, and a paid data service might want to throttle against a certain number of a records being accessed. ## How throttling is determined @@ -25,7 +27,7 @@ If any throttle check fails an `exceptions.Throttled` exception will be raised, ## Setting the throttling policy -The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` setting. For example. +The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` and `DEFAULT_THROTTLE_RATES` settings. For example. API_SETTINGS = { 'DEFAULT_THROTTLES': ( @@ -38,6 +40,8 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` } } +The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `minute`, `hour` or `day` as the throttle period. + You can also set the throttling policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): @@ -59,18 +63,67 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) -## AnonThrottle +## AnonRateThrottle + +The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against. -The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to identify +The allowed request rate is determined from one of the following (in order of preference). + +* The `rate` property on the class, which may be provided by overriding `AnonThrottle` and setting the property. +* The `DEFAULT_THROTTLE_RATES['anon']` setting. `AnonThrottle` is suitable if you want to restrict the rate of requests from unknown sources. -## UserThrottle +## UserRateThrottle + +The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticted requests will fall back to using the IP address of the incoming request is used to generate a unique key to throttle against. + +The allowed request rate is determined from one of the following (in order of preference). + +* The `rate` property on the class, which may be provided by overriding `UserThrottle` and setting the property. +* The `DEFAULT_THROTTLE_RATES['user']` setting. + +`UserThrottle` is suitable if you want a simple global rate restriction per-user. + +## ScopedRateThrottle + +The `ScopedThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unqiue user id or IP address. + +The allowed request rate is determined by the `DEFAULT_THROTTLE_RATES` setting using a key from the request "scope". + +For example, given the following views... -`UserThrottle` is suitable if you want a simple restriction + class ContactListView(APIView): + throttle_scope = 'contacts' + ... + + class ContactDetailView(ApiView): + throttle_scope = 'contacts' + ... -## ScopedThrottle + + class UploadView(APIView): + throttle_scope = 'uploads' + ... + +...and the following settings. + + API_SETTINGS = { + 'DEFAULT_THROTTLES': ( + 'djangorestframework.throttles.ScopedRateThrottle', + ) + 'DEFAULT_THROTTLE_RATES': { + 'contacts': '1000/day', + 'uploads': '20/day' + } + } + +User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day. ## Custom throttles +To implement a custom throttle, override `BaseThrottle` and implement `.allow_request(request)`. The method should return `True` if the request should be allowed, and `False` otherwise. + +Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.check_throttle()` has previously returned `False`. + [permissions]: permissions.md \ No newline at end of file -- cgit v1.2.3 From d8b5d6603ed5095d8fa62147196e0c5b1e642325 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Sep 2012 18:35:06 +0100 Subject: Tweak copy --- docs/api-guide/throttling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index acad82cd..0856183b 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -122,7 +122,7 @@ User requests to either `ContactListView` or `ContactDetailView` would be restri ## Custom throttles -To implement a custom throttle, override `BaseThrottle` and implement `.allow_request(request)`. The method should return `True` if the request should be allowed, and `False` otherwise. +To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request)`. The method should return `True` if the request should be allowed, and `False` otherwise. Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.check_throttle()` has previously returned `False`. -- cgit v1.2.3 From 886f8b47510c830483b5adae1855593cdc3df2dc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 14 Sep 2012 08:54:58 +0100 Subject: Tweak throttles and improve docs --- docs/api-guide/throttling.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 0856183b..d1e34dcd 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -83,6 +83,29 @@ The allowed request rate is determined from one of the following (in order of pr * The `rate` property on the class, which may be provided by overriding `UserThrottle` and setting the property. * The `DEFAULT_THROTTLE_RATES['user']` setting. +An API may have multiple `UserRateThrottles` in place at the same time. To do so, override `UserRateThrottle` and set a unique "scope" for each class. + +For example, multiple user throttle rates could be implemented by using the following classes... + + class BurstRateThrottle(UserRateThrottle): + scope = 'burst' + + class SustainedRateThrottle(UserRateThrottle): + scope = 'sustained' + +...and the following settings. + + API_SETTINGS = { + 'DEFAULT_THROTTLES': ( + 'example.throttles.BurstRateThrottle', + 'example.throttles.SustainedRateThrottle', + ) + 'DEFAULT_THROTTLE_RATES': { + 'burst': '60/min', + 'sustained': '1000/day' + } + } + `UserThrottle` is suitable if you want a simple global rate restriction per-user. ## ScopedRateThrottle -- cgit v1.2.3 From eb761be9d058dbfb9214f200b941496524dc0ded Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 14 Sep 2012 12:43:14 +0100 Subject: Flesh out resources/routers part of tutorial --- docs/api-guide/throttling.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index d1e34dcd..10997801 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -76,7 +76,7 @@ The allowed request rate is determined from one of the following (in order of pr ## UserRateThrottle -The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticted requests will fall back to using the IP address of the incoming request is used to generate a unique key to throttle against. +The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticted requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). @@ -106,7 +106,7 @@ For example, multiple user throttle rates could be implemented by using the foll } } -`UserThrottle` is suitable if you want a simple global rate restriction per-user. +`UserThrottle` is suitable if you want simple global rate restrictions per-user. ## ScopedRateThrottle @@ -124,7 +124,6 @@ For example, given the following views... throttle_scope = 'contacts' ... - class UploadView(APIView): throttle_scope = 'uploads' ... -- cgit v1.2.3 From a96211d3d1ba246512af5e32c31726a666c467ac Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 16 Sep 2012 21:48:55 +0100 Subject: Simplify negotiation. Drop MSIE hacks. Etc. --- docs/api-guide/content-negotiation.md | 2 ++ 1 file changed, 2 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index 01895a4b..ad98de3b 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -1,3 +1,5 @@ + + # Content negotiation > HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available. -- cgit v1.2.3 From 4b691c402707775c3048a90531024f3bc5be6f91 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Sep 2012 13:06:27 +0100 Subject: Change package name: djangorestframework -> rest_framework --- docs/api-guide/authentication.md | 14 +++++++------- docs/api-guide/permissions.md | 6 +++--- docs/api-guide/requests.md | 6 +++--- docs/api-guide/reverse.md | 6 +++--- docs/api-guide/settings.md | 28 ++++++++++++++-------------- docs/api-guide/status-codes.md | 2 +- docs/api-guide/throttling.md | 14 +++++++------- 7 files changed, 38 insertions(+), 38 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 79950946..f24c6a81 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -28,10 +28,10 @@ The value of `request.user` and `request.auth` for unauthenticated requests can The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION` setting. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION': ( - 'djangorestframework.authentication.UserBasicAuthentication', - 'djangorestframework.authentication.SessionAuthentication', + 'rest_framework.authentication.UserBasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', ) } @@ -75,11 +75,11 @@ If successfully authenticated, `BasicAuthentication` provides the following cred This policy uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients. -To use the `TokenAuthentication` policy, include `djangorestframework.authtoken` in your `INSTALLED_APPS` setting. +To use the `TokenAuthentication` policy, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting. You'll also need to create tokens for your users. - from djangorestframework.authtoken.models import Token + from rest_framework.authtoken.models import Token token = Token.objects.create(user=...) print token.key @@ -91,7 +91,7 @@ For clients to authenticate, the token key should be included in the `Authorizat If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.tokenauth.models.BasicToken` instance. +* `request.auth` will be a `rest_framework.tokenauth.models.BasicToken` instance. **Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only. @@ -102,7 +102,7 @@ This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAut If successfully authenticated, `OAuthAuthentication` provides the following credentials. * `request.user` will be a `django.contrib.auth.models.User` instance. -* `request.auth` will be a `djangorestframework.models.OAuthToken` instance. +* `request.auth` will be a `rest_framework.models.OAuthToken` instance. ## SessionAuthentication diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index fafef305..e0ceb1ea 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -27,9 +27,9 @@ Object level permissions are run by REST framework's generic views when `.get_ob The default permission policy may be set globally, using the `DEFAULT_PERMISSIONS` setting. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_PERMISSIONS': ( - 'djangorestframework.permissions.IsAuthenticated', + 'rest_framework.permissions.IsAuthenticated', ) } @@ -97,4 +97,4 @@ The method should return `True` if the request should be granted access, and `Fa [authentication]: authentication.md [throttling]: throttling.md [contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions -[guardian]: https://github.com/lukaszb/django-guardian \ No newline at end of file +[guardian]: https://github.com/lukaszb/django-guardian diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 6746bb20..b223da80 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -49,7 +49,7 @@ This allows you to support file uploads from multiple content-types. For exampl `request.parsers` may no longer be altered once `request.DATA`, `request.FILES` or `request.POST` have been accessed. -If you're using the `djangorestframework.views.View` class... **[TODO]** +If you're using the `rest_framework.views.View` class... **[TODO]** ## .stream @@ -63,6 +63,6 @@ You will not typically need to access `request.stream`, unless you're writing a `request.authentication` may no longer be altered once `request.user` or `request.auth` have been accessed. -If you're using the `djangorestframework.views.View` class... **[TODO]** +If you're using the `rest_framework.views.View` class... **[TODO]** -[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion \ No newline at end of file +[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index f3cb0c64..3fa654c0 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -23,8 +23,8 @@ There's no requirement for you to use them, but if you do then the self-describi Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. - from djangorestframework.utils import reverse - from djangorestframework.views import APIView + from rest_framework.utils import reverse + from rest_framework.views import APIView class MyView(APIView): def get(self, request): @@ -40,4 +40,4 @@ Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy] [cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 [reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy \ No newline at end of file +[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 2513928c..0f66e85e 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -6,16 +6,16 @@ > > — [The Zen of Python][cite] -Configuration for REST framework is all namespaced inside a single Django setting, named `API_SETTINGS`. +Configuration for REST framework is all namespaced inside a single Django setting, named `REST_FRAMEWORK`. For example your project's `settings.py` file might include something like this: - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_RENDERERS': ( - 'djangorestframework.renderers.YAMLRenderer', + 'rest_framework.renderers.YAMLRenderer', ) 'DEFAULT_PARSERS': ( - 'djangorestframework.parsers.YAMLParser', + 'rest_framework.parsers.YAMLParser', ) } @@ -24,7 +24,7 @@ For example your project's `settings.py` file might include something like this: If you need to access the values of REST framework's API settings in your project, you should use the `api_settings` object. For example. - from djangorestframework.settings import api_settings + from rest_framework.settings import api_settings print api_settings.DEFAULT_AUTHENTICATION @@ -37,9 +37,9 @@ A list or tuple of renderer classes, that determines the default set of renderer Default: ( - 'djangorestframework.renderers.JSONRenderer', - 'djangorestframework.renderers.DocumentingHTMLRenderer' - 'djangorestframework.renderers.TemplateHTMLRenderer' + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.DocumentingHTMLRenderer' + 'rest_framework.renderers.TemplateHTMLRenderer' ) ## DEFAULT_PARSERS @@ -49,8 +49,8 @@ A list or tuple of parser classes, that determines the default set of parsers us Default: ( - 'djangorestframework.parsers.JSONParser', - 'djangorestframework.parsers.FormParser' + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser' ) ## DEFAULT_AUTHENTICATION @@ -60,8 +60,8 @@ A list or tuple of authentication classes, that determines the default set of au Default: ( - 'djangorestframework.authentication.SessionAuthentication', - 'djangorestframework.authentication.UserBasicAuthentication' + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.UserBasicAuthentication' ) ## DEFAULT_PERMISSIONS @@ -80,13 +80,13 @@ Default: `()` **TODO** -Default: `djangorestframework.serializers.ModelSerializer` +Default: `rest_framework.serializers.ModelSerializer` ## DEFAULT_PAGINATION_SERIALIZER **TODO** -Default: `djangorestframework.pagination.PaginationSerializer` +Default: `rest_framework.pagination.PaginationSerializer` ## FORMAT_SUFFIX_KWARG diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 6693c79f..401f45ce 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -8,7 +8,7 @@ Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable. - from djangorestframework import status + from rest_framework import status def empty_view(self): content = {'please move along': 'nothing to see here'} diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 10997801..7861e9ba 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -29,10 +29,10 @@ If any throttle check fails an `exceptions.Throttled` exception will be raised, The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` and `DEFAULT_THROTTLE_RATES` settings. For example. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( - 'djangorestframework.throttles.AnonThrottle', - 'djangorestframework.throttles.UserThrottle', + 'rest_framework.throttles.AnonThrottle', + 'rest_framework.throttles.UserThrottle', ) 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', @@ -95,7 +95,7 @@ For example, multiple user throttle rates could be implemented by using the foll ...and the following settings. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( 'example.throttles.BurstRateThrottle', 'example.throttles.SustainedRateThrottle', @@ -130,9 +130,9 @@ For example, given the following views... ...and the following settings. - API_SETTINGS = { + REST_FRAMEWORK = { 'DEFAULT_THROTTLES': ( - 'djangorestframework.throttles.ScopedRateThrottle', + 'rest_framework.throttles.ScopedRateThrottle', ) 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', @@ -148,4 +148,4 @@ To create a custom throttle, override `BaseThrottle` and implement `.allow_reque Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.check_throttle()` has previously returned `False`. -[permissions]: permissions.md \ No newline at end of file +[permissions]: permissions.md -- cgit v1.2.3 From 0cc7030aab9f2b97ce6b5db55d6d1a8a32d50231 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 26 Sep 2012 21:47:19 +0100 Subject: Fix @api_view decorator tests --- docs/api-guide/parsers.md | 21 ++++++++- docs/api-guide/renderers.md | 19 +++++++- docs/api-guide/requests.md | 2 +- docs/api-guide/views.md | 110 ++++++++++++++++++++++++++++++++++++-------- 4 files changed, 130 insertions(+), 22 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 5e2344a3..b9bb4900 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -2,4 +2,23 @@ # Parsers -## .parse(request) +> Machine interacting web services tend to use more +structured formats for sending data than form-encoded, since they're +sending more complex data than simple forms +> +> — Malcom Tredinnick, [Django developers group][cite] + + +## JSONParser + +## YAMLParser + +## XMLParser + +## FormParser + +## MultiPartParser + +## Custom parsers + +[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 1cd6d1a0..d644599e 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -2,5 +2,22 @@ # Renderers -## .render(response) +> Before a TemplateResponse instance can be returned to the client, it must be rendered. The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. +> +> — [Django documentation][cite] +## JSONRenderer + +## JSONPRenderer + +## YAMLRenderer + +## XMLRenderer + +## DocumentingHTMLRenderer + +## TemplatedHTMLRenderer + +## Custom renderers + +[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process \ No newline at end of file diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index b223da80..36513cd9 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -4,7 +4,7 @@ > If you're doing REST-based web service stuff ... you should ignore request.POST. > -> — Malcom Tredinnick, [Django developers group][cite] +> — Malcom Tredinnick, [Django developers group][cite] REST framework's `Request` class extends the standard `HttpRequest`, adding support for parsing multiple content types, allowing browser-based `PUT`, `DELETE` and other methods, and adding flexible per-request authentication. diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 04647f91..e7f12b45 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,4 +1,4 @@ - + # Views @@ -6,36 +6,108 @@ > > — [Reinout van Rees][cite] -REST framework provides a simple `APIView` class, built on Django's `django.generics.views.View`. The `APIView` class ensures five main things: +REST framework provides an `APIView` class, which subclasses Django's `View` class. -1. Any requests inside the view will become `Request` instances. -2. `Request` instances will have their `renderers` and `authentication` attributes automatically set. -3. `Response` instances will have their `parsers` and `serializer` attributes automatically set. -4. `APIException` exceptions will be caught and return appropriate responses. -5. Any permissions provided will be checked prior to passing the request to a handler method. +`APIView` classes are different from regular `View` classes in the following ways: -Additionally there are a some minor extras, such as providing a default `options` handler, setting some common headers on the response prior to return, and providing the useful `initial()` and `final()` hooks. +* Requests passed to the handler methods will be REST framework's `Request` instances, not Django's `HttpRequest` instances. +* Handler methods may return REST framework's `Response`, instead of Django's `HttpResponse`. The view will manage content negotiation and setting the correct renderer on the response. +* Any `APIException` exceptions will be caught and mediated into appropriate responses. +* Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method. -## APIView +Using the `APIView` class is pretty much the same as using a regular `View` class, as usual, the incoming request is dispatched to an appropriate handler method such as `.get()` or `.post()`. Additionally, a number of attributes may be set on the class that control various aspects of the API policy. -## Method handlers +For example: -Describe that APIView handles regular .get(), .post(), .put(), .delete() etc... + class ListUsers(APIView): + """ + View to list all users in the system. + + * Requires token authentication. + * Only admin users are able to access this view. + """ + authentication_classes = (authentication.TokenAuthentication,) + permission_classes = (permissions.IsAdmin,) -## .initial(request, *args, **kwargs) + def get(self, request, format=None): + """ + Return a list of all users. + """ + users = [user.username for user in User.objects.all()] + return Response(users) -## .final(request, response, *args, **kwargs) +## API policy attributes -## .parsers +The following attributes control the pluggable aspects of API views. -## .renderers +### .renderer_classes -## .serializer +### .parser_classes -## .authentication +### .authentication_classes -## .permissions +### .throttle_classes -## .headers +### .permission_classes + +### .content_negotiation_class + +## API policy instantiation methods + +The following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods. + +### .get_renderers(self) + +### .get_parsers(self) + +### .get_authenticators(self) + +### .get_throttles(self) + +### .get_permissions(self) + +### .get_content_negotiator(self) + +## API policy implementation methods + +The following methods are called before dispatching to the handler method. + +### .check_permissions(...) + +### .check_throttles(...) + +### .perform_content_negotiation(...) + +## Dispatch methods + +The following methods are called directly by the view's `.dispatch()` method. +These perform any actions that need to occur before or after calling the handler methods such as `.get()`, `.post()`, `put()` and `.delete()`. + +### .initial(self, request, *args, **kwargs) + +Performs any actions that need to occur before the handler method gets called. +This method is used to enforce permissions and throttling, and perform content negotiation. + +You won't typically need to override this method. + +### .handle_exception(self, exc) + +Any exception thrown by the handler method will be passed to this method, which either returns a `Response` instance, or re-raises the exception. + +The default implementation handles any subclass of `rest_framework.exceptions.APIException`, as well as Django's `Http404` and `PermissionDenied` exceptions, and returns an appropriate error response. + +If you need to customize the error responses your API returns you should subclass this method. + +### .initialize_request(self, request, *args, **kwargs) + +Ensures that the request object that is passed to the handler method is an instance of `Request`, rather than the usual Django `HttpRequest`. + +You won't typically need to override this method. + +### .finalize_response(self, request, response, *args, **kwargs) + +Ensures that any `Response` object returned from the handler method will be rendered into the correct content type, as determined by the content negotation. + +You won't typically need to override this method. [cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html \ No newline at end of file -- cgit v1.2.3 From ee36e4ab0c0508a590c6b73a23ec82b7f1e49bd0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 27 Sep 2012 21:51:46 +0100 Subject: Only display forms when user has permissions. #159 --- docs/api-guide/permissions.md | 1 - 1 file changed, 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index e0ceb1ea..9a3c39ab 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -92,7 +92,6 @@ To implement a custom permission, override `BasePermission` and implement the `. The method should return `True` if the request should be granted access, and `False` otherwise. - [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md -- cgit v1.2.3 From 43d3634e892e303ca377265d3176e8313f19563f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 30 Sep 2012 15:55:24 +0100 Subject: Docs tweaking --- docs/api-guide/authentication.md | 8 ++++---- docs/api-guide/responses.md | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index f24c6a81..c6995360 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -39,6 +39,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi class ExampleView(APIView): authentication_classes = (SessionAuthentication, UserBasicAuthentication) + permission_classes = (IsAuthenticated,) def get(self, request, format=None): content = { @@ -49,10 +50,9 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. - @api_view( - allowed=('GET',), - authentication_classes=(SessionAuthentication, UserBasicAuthentication) - ) + @api_view('GET'), + @authentication_classes(SessionAuthentication, UserBasicAuthentication) + @permissions_classes(IsAuthenticated) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 6c279f17..e9ebcf81 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -8,18 +8,30 @@ REST framework supports HTTP content negotiation by providing a `Response` class which allows you to return content that can be rendered into multiple content types, depending on the client request. -The `Response` class subclasses Django's `TemplateResponse`. `Response` objects are initialised with content, which should consist of native python primatives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content. +The `Response` class subclasses Django's `SimpleTemplateResponse`. `Response` objects are initialised with data, which should consist of native python primatives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content. -There's no requirement for you to use the `Response` class, you can also return regular `HttpResponse` objects from your views if you want, but it does provide a better interface for returning Web API responses. +There's no requirement for you to use the `Response` class, you can also return regular `HttpResponse` objects from your views if you want, but it provides a nicer interface for returning Web API responses. -## Response(content, headers=None, renderers=None, view=None, format=None, status=None) +Unless you want to heavily customize REST framework for some reason, you should always use an `APIView` class or `@api_view` function for views that return `Response` objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view. +## Response(data, status=None, headers=None) -## .renderers +Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any python primatives. -## .view +The renderers used by the `Response` class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primative datatypes before creating the `Response` object. -## .format +You can use REST framework's `Serializer` classes to perform this data serialization, or use your own custom serialization. +## .data + +The unrendered content of a `Request` object can be accessed using the `.data` attribute. + +## .content + +To access the rendered content of a `Response` object, you must first call `.render()`. You'll typically only need to do this in cases such as unit testing responses - when you return a `Response` from a view Django's response cycle will handle calling `.render()` for you. + +## .renderer + +When you return a `Response` instance, the `APIView` class or `@api_view` decorator will select the appropriate renderer, and set the `.renderer` attribute on the `Response`, before returning it from the view. [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ \ No newline at end of file -- cgit v1.2.3 From b16fb5777168246b1e217640b818a82eb6e2141b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Oct 2012 15:49:19 +0100 Subject: Expand pagination support, add docs --- docs/api-guide/pagination.md | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 docs/api-guide/pagination.md (limited to 'docs/api-guide') diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md new file mode 100644 index 00000000..0f0a32b5 --- /dev/null +++ b/docs/api-guide/pagination.md @@ -0,0 +1,98 @@ + + +# Pagination + +> Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links. +> +> — [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. + +## Examples + +Let's start by taking a look at an example from the Django documentation. + + from django.core.paginator import Paginator + objects = ['john', 'paul', 'george', 'ringo'] + paginator = Paginator(objects, 2) + page = paginator.page(1) + page.object_list + # ['john', 'paul'] + +At this point we've got a page object. If we wanted to return this page object as a JSON response, we'd need to provide the client with context such as next and previous links, so that it would be able to page through the remaining results. + + from rest_framework.pagination import PaginationSerializer + serializer = PaginationSerializer(instance=page) + serializer.data + # {'count': 4, 'next': '?page=2', 'previous': None, 'results': [u'john', u'paul']} + +The `context` argument of the `PaginationSerializer` class may optionally include the request. If the request is included in the context then the next and previous links returned by the serializer will use absolute URLs instead of relative URLs. + + 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']} + +We could now return that data in a `Response` object, and it would be rendered into the correct media type. + +Our first example worked because we were using primative objects. If we wanted to paginate a queryset or other complex data, we'd need to specify a serializer to use to serialize the result set itself with. + +We can do this using the `object_serializer_class` attribute on the inner `Meta` class of the pagination serializer. For example. + + class UserSerializer(serializers.ModelSerializer): + """ + Serializes user querysets. + """ + class Meta: + model = User + fields = ('username', 'email') + + class PaginatedUserSerializer(pagination.PaginationSerializer): + """ + Serializes page objects of user querysets. + """ + class Meta: + object_serializer_class = UserSerializer + + queryset = User.objects.all() + paginator = Paginator(queryset, 20) + page = paginator.page(1) + serializer = PaginatedUserSerializer(instance=page) + serializer.data + # {'count': 1, 'next': None, 'previous': None, 'results': [{'username': u'admin', 'email': u'admin@example.com'}]} + +## Pagination in the generic views + +The generic class based views `ListAPIView` and `ListCreateAPIView` provide pagination of the returned querysets by default. You can customise this behaviour by altering the pagination style, by modifying the default number of results, or by turning pagination off completely. + +## Setting the default pagination style + +The default pagination style may be set globally, using the `PAGINATION_SERIALIZER` and `PAGINATE_BY` settings. For example. + + REST_FRAMEWORK = { + 'PAGINATION_SERIALIZER': ( + 'example_app.pagination.CustomPaginationSerializer', + ), + 'PAGINATE_BY': 10 + } + +You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view. + + class PaginatedListView(ListAPIView): + model = ExampleModel + pagination_serializer_class = CustomPaginationSerializer + paginate_by = 10 + +## Creating custom pagination serializers + +Override `pagination.BasePaginationSerializer`, and set the fields that you want the serializer to return. + +For example. + + class CustomPaginationSerializer(pagination.BasePaginationSerializer): + next = pagination.NextURLField() + total_results = serializers.Field(source='paginator.count') + + +[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ + -- cgit v1.2.3 From 8d1d99018725469061d5696a5552e7ebdb5cccc9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Oct 2012 16:17:01 +0100 Subject: Pagination docs --- docs/api-guide/pagination.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 0f0a32b5..6211a0ac 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -8,7 +8,7 @@ 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. -## Examples +## Paginating basic data Let's start by taking a look at an example from the Django documentation. @@ -35,6 +35,8 @@ The `context` argument of the `PaginationSerializer` class may optionally includ We could now return that data in a `Response` object, and it would be rendered into the correct media type. +## Paginating QuerySets + Our first example worked because we were using primative objects. If we wanted to paginate a queryset or other complex data, we'd need to specify a serializer to use to serialize the result set itself with. We can do this using the `object_serializer_class` attribute on the inner `Meta` class of the pagination serializer. For example. @@ -83,16 +85,20 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie pagination_serializer_class = CustomPaginationSerializer paginate_by = 10 +For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. + ## Creating custom pagination serializers -Override `pagination.BasePaginationSerializer`, and set the fields that you want the serializer to return. +To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return. + +For example, to nest a pair of links labelled 'prev' and 'next' you might use something like this. -For example. + class LinksSerializer(serializers.Serializer): + next = pagination.NextURLField(source='*') + prev = pagination.PreviousURLField(source='*') class CustomPaginationSerializer(pagination.BasePaginationSerializer): - next = pagination.NextURLField() + links = LinksSerializer(source='*') # Takes the page object as the source total_results = serializers.Field(source='paginator.count') - [cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ - -- cgit v1.2.3 From dae6d093988ef2830e715d17ad6a0b9f715bfeba Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Oct 2012 16:27:22 +0100 Subject: Add example of using paginator in a view --- docs/api-guide/pagination.md | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 6211a0ac..8ef7c4cc 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -56,19 +56,32 @@ We can do this using the `object_serializer_class` attribute on the inner `Meta` class Meta: object_serializer_class = UserSerializer - queryset = User.objects.all() - paginator = Paginator(queryset, 20) - page = paginator.page(1) - serializer = PaginatedUserSerializer(instance=page) - serializer.data - # {'count': 1, 'next': None, 'previous': None, 'results': [{'username': u'admin', 'email': u'admin@example.com'}]} +We could now use our pagination serializer in a view like this. + + @api_view('GET') + def user_list(request): + queryset = User.objects.all() + paginator = Paginator(queryset, 20) + + page = request.QUERY_PARAMS.get('page') + try: + users = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + users = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + users = paginator.page(paginator.num_pages) + + serializer_context = {'request': request} + serializer = PaginatedUserSerializer(instance=users, + context=serializer_context) + return Response(serializer.data) ## Pagination in the generic views The generic class based views `ListAPIView` and `ListCreateAPIView` provide pagination of the returned querysets by default. You can customise this behaviour by altering the pagination style, by modifying the default number of results, or by turning pagination off completely. -## Setting the default pagination style - The default pagination style may be set globally, using the `PAGINATION_SERIALIZER` and `PAGINATE_BY` settings. For example. REST_FRAMEWORK = { -- cgit v1.2.3 From a8f6ac3f3abdcab298f73f591188012a703a65e2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 10:39:28 +0100 Subject: Renderer documentation --- docs/api-guide/renderers.md | 132 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index d644599e..134c3749 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -6,18 +6,146 @@ > > — [Django documentation][cite] +REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexiblity to design your own media types. + +## How the renderer is determined + +The set of valid renderers for a view is always defined as a list of classes. When a view is entered REST framework will perform content negotiation on the incoming request, and determine the most appropriate renderer to satisfy the request. + +The basic process of content negotiation involves examining the request's `Accept` header, to determine which media types it expects in the response. Optionally, format suffixes on the URL may be used to explicitly request a particular representation. For example the URL `http://example.com/api/users_count.json` might be an endpoint that always returns JSON data. + +For more information see the documentation on [content negotation][conneg]. + +## Setting the renderers + +The default set of renderers may be set globally, using the `DEFAULT_RENDERERS` setting. For example, the following settings would use `YAML` as the main media type and also include the self describing API. + + REST_FRAMEWORK = { + 'DEFAULT_RENDERERS': ( + 'rest_framework.renderers.YAMLRenderer', + 'rest_framework.renderers.DocumentingHTMLRenderer', + ) + } + +You can also set the renderers used for an individual view, using the `APIView` class based views. + + class UserCountView(APIView): + """ + A view that returns the count of active users, in JSON or JSONp. + """ + renderer_classes = (JSONRenderer, JSONPRenderer) + + def get(self, request, format=None): + user_count = User.objects.filter(active=True).count() + content = {'user_count': user_count} + return Response(content) + +Or, if you're using the `@api_view` decorator with function based views. + + @api_view('GET'), + @renderer_classes(JSONRenderer, JSONPRenderer) + def user_count_view(request, format=None): + """ + A view that returns the count of active users, in JSON or JSONp. + """ + user_count = User.objects.filter(active=True).count() + content = {'user_count': user_count} + return Response(content) + +## Ordering of renderer classes + +It's important when specifying the renderer classes for your API to think about what priority you want to assign to each media type. If a client underspecifies the representations it can accept, such as sending an `Accept: */*` header, or not including an `Accept` header at all, then REST framework will select the first renderer in the list to use for the response. + +For example if your API serves JSON responses and the HTML browseable API, you might want to make `JSONRenderer` your default renderer, in order to send `JSON` responses to clients that do not specify an `Accept` header. + +If your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making `TemplateHTMLRenderer` your default renderer, in order to play nicely with older browsers that send [broken accept headers][browser-accept-headers]. + ## JSONRenderer +**.media_type:** `application/json` + +**.format:** `'.json'` + ## JSONPRenderer +**.media_type:** `application/javascript` + +**.format:** `'.jsonp'` + ## YAMLRenderer +**.media_type:** `application/yaml` + +**.format:** `'.yaml'` + ## XMLRenderer +**.media_type:** `application/xml` + +**.format:** `'.xml'` + ## DocumentingHTMLRenderer -## TemplatedHTMLRenderer +**.media_type:** `text/html` + +**.format:** `'.api'` + +## TemplateHTMLRenderer + +**.media_type:** `text/html` + +**.format:** `'.html'` ## Custom renderers -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process \ No newline at end of file +To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method. + +## Advanced renderer usage + +You can do some pretty flexible things using REST framework's renderers. Some examples... + +* 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. + +In some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access `request.accepted_renderer` to determine the negotiated renderer that will be used for the response. + +For example: + + @api_view(('GET',)) + @renderer_classes((TemplateHTMLRenderer, JSONRenderer)) + @template_name('list_users.html') + def list_users(request): + """ + A view that can return JSON or HTML representations + of the users in the system. + """ + queryset = Users.objects.filter(active=True) + + if request.accepted_renderer.format == 'html': + # TemplateHTMLRenderer takes a context dict, + # and does not require serialization. + data = {'users': queryset} + else: + # JSONRenderer requires serialized data as normal. + serializer = UserSerializer(instance=queryset) + data = serializer.data + + return Response(data) + +## Designing your media types + +For the purposes of many Web APIs, simple `JSON` responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and [HATEOAS] you'll neeed to consider the design and usage of your media types in more detail. + +In [the words of Roy Fielding][quote], "A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.". + +For good examples of custom media types, see GitHub's use of a custom [application/vnd.github+json] media type, and Mike Amundsen's IANA approved [application/vnd.collection+json] JSON-based hypermedia. + +[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 +[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas +[quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven +[application/vnd.github+json]: http://developer.github.com/v3/media/ +[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ \ No newline at end of file -- cgit v1.2.3 From 2284e592de6315264098e77f72b816984a50d025 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 10:40:04 +0100 Subject: Clean up reverse docs slightly --- docs/api-guide/reverse.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 3fa654c0..07094e6f 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -19,7 +19,9 @@ REST framework provides two utility functions to make it more simple to return a There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier. -## reverse(viewname, request, *args, **kwargs) +## reverse + +**Signature:** `reverse(viewname, request, *args, **kwargs)` Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. @@ -34,7 +36,9 @@ Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except t } return Response(content) -## reverse_lazy(viewname, request, *args, **kwargs) +## reverse_lazy + +**Signature:** `reverse_lazy(viewname, request, *args, **kwargs)` Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. -- cgit v1.2.3 From ae8a8270042820f92b9d43597bde50d72c300513 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 10:40:43 +0100 Subject: Make 'results_field' attribute of BasePaginationSerializer public. --- docs/api-guide/pagination.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 8ef7c4cc..50be59bf 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -104,7 +104,9 @@ For more complex requirements such as serialization that differs depending on th To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return. -For example, to nest a pair of links labelled 'prev' and 'next' you might use something like this. +You can also override the name used for the object list field, by setting the `results_field` attribute, which defaults to `'results'`. + +For example, to nest a pair of links labelled 'prev' and 'next', and set the name for the results field to 'objects', you might use something like this. class LinksSerializer(serializers.Serializer): next = pagination.NextURLField(source='*') @@ -114,4 +116,6 @@ For example, to nest a pair of links labelled 'prev' and 'next' you might use so links = LinksSerializer(source='*') # Takes the page object as the source total_results = serializers.Field(source='paginator.count') + results_field = 'objects' + [cite]: https://docs.djangoproject.com/en/dev/topics/pagination/ -- cgit v1.2.3 From 34637bf8578bdfff2d60f36e6e5ac6314a330eaf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 11:03:51 +0100 Subject: Make example more realistic and less of a toy --- docs/api-guide/reverse.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 07094e6f..8485087e 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -25,16 +25,18 @@ There's no requirement for you to use them, but if you do then the self-describi Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. + import datetime from rest_framework.utils import reverse from rest_framework.views import APIView - class MyView(APIView): + class APIRootView(APIView): def get(self, request): - content = { + year = datetime.datetime.now().year + data = { ... - 'url': reverse('year-summary', request, args=[1945]) + 'year-summary-url': reverse('year-summary', request, args=[year]) } - return Response(content) + return Response(data) ## reverse_lazy -- cgit v1.2.3 From b526b82abf4be55faa7610bdeeefa340f84ad2d1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 11:04:06 +0100 Subject: Placeholder for FBV docs --- docs/api-guide/views.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index e7f12b45..37d2285b 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -1,6 +1,6 @@ -# Views +# Class Based Views > Django's class based views are a welcome departure from the old-style views. > @@ -110,4 +110,15 @@ Ensures that any `Response` object returned from the handler method will be rend You won't typically need to override this method. -[cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html \ No newline at end of file +# Function Based Views + +> Saying [that Class based views] is always the superior solution is a mistake. +> +> — [Nick Coghlan][cite2] + +REST framework also gives you to work with regular function based views... + +**[TODO]** + +[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 \ No newline at end of file -- cgit v1.2.3 From 8855a462c606c4cb08a86269c1f46dc2b30fc1ac Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 11:48:25 +0100 Subject: Clean up docs slightly --- docs/api-guide/generic-views.md | 88 +++++++++++++++++++++++++++++++++++++++++ docs/api-guide/views.md | 2 + 2 files changed, 90 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 202875a4..88f245c7 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -7,4 +7,92 @@ > > — [Django Documentation][cite] +One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. + +## Example + +... + +--- + +# API Reference + +## ListAPIView + +Used for read-write endpoints to represent a collection of model instances. + +Provides a `get` method handler. + +## ListCreateAPIView + +Used for read-write endpoints to represent a collection of model instances. + +Provides `get` and `post` method handlers. + +## RetrieveAPIView + +Used for read-only endpoints to represent a single model instance. + +Provides a `get` method handler. + +## RetrieveUpdateDestroyAPIView + +Used for read-write endpoints to represent a single model instance. + +Provides `get`, `put` and `delete` method handlers. + +--- + +# Base views + +## BaseAPIView + +Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets. + +## MultipleObjectBaseAPIView + +Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin]. + +**See also:** ccbv.co.uk documentation for [MultipleObjectMixin][multiple-object-mixin-classy]. + +## SingleObjectBaseAPIView + +Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin]. + +**See also:** ccbv.co.uk documentation for [SingleObjectMixin][single-object-mixin-classy]. + +--- + +# Mixins + +The mixin classes provide the actions that are used + +## ListModelMixin + +Provides a `.list(request, *args, **kwargs)` method, that implements listing a queryset. + +## CreateModelMixin + +Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. + +## RetrieveModelMixin + +Provides a `.retrieve(request, *args, **kwargs)` method, that implements returning an existing model instance in a response. + +## UpdateModelMixin + +Provides a `.update(request, *args, **kwargs)` method, that implements updating and saving an existing model instance. + +## DestroyModelMixin + +Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance. + +## MetadataMixin + +Provides a `.metadata(request, *args, **kwargs)` method, that returns a response containing metadata about the view. + [cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views +[MultipleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/ +[SingleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/ +[multiple-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.list/MultipleObjectMixin/ +[single-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.detail/SingleObjectMixin/ diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 37d2285b..a615026f 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -110,6 +110,8 @@ Ensures that any `Response` object returned from the handler method will be rend You won't typically need to override this method. +--- + # Function Based Views > Saying [that Class based views] is always the superior solution is a mistake. -- cgit v1.2.3 From e7685f3eb5c7d7e8fb1678d673f03688012b00cb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 15:24:42 +0100 Subject: URL overrides in settings fixed up slightly --- docs/api-guide/settings.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 0f66e85e..43be0d47 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -136,6 +136,10 @@ The name of a URL parameter that may be used to override the HTTP `Accept` heade If the value of this setting is `None` then URL accept overloading will be disabled. -Default: `'_accept'` +Default: `'accept'` + +## URL_FORMAT_OVERRIDE + +Default: `'format'` [cite]: http://www.python.org/dev/peps/pep-0020/ -- cgit v1.2.3 From ab173fd8f9070ccdb70f86f400d2ffa780977ce4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 15:37:13 +0100 Subject: Fix bug where pk could be set in post data --- docs/api-guide/serializers.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 38a1e560..4ddc6e0a 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -230,6 +230,9 @@ The `nested` option may also be set by passing it to the `serialize()` method. class Meta: model = Account + def get_pk_field(self, model_field): + return Field(readonly=True) + def get_nested_field(self, model_field): return ModelSerializer() -- cgit v1.2.3 From b89125ef53a2f9e246afd5eda5c8f404a714da76 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Oct 2012 21:26:15 +0100 Subject: Update view docs slightly --- docs/api-guide/generic-views.md | 43 +++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 88f245c7..b2284ae5 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -9,9 +9,38 @@ One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. -## Example +The generic views provided by REST framework allow you to quickly build API views that map closely to your database models. -... +If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views. + +## Examples + +Typically when using the generic views, you'll override the view, and set several class attributes. + + class UserList(generics.ListCreateAPIView): + serializer = UserSerializer + model = User + permissions = (IsAdminUser,) + paginate_by = 100 + +For more complex cases you might also want to override various methods on the view class. For example. + + class UserList(generics.ListCreateAPIView): + serializer = UserSerializer + model = User + permissions = (IsAdminUser,) + + def get_paginate_by(self): + """ + Use smaller pagination for HTML representations. + """ + if self.request.accepted_media_type == 'text/html': + return 10 + return 100 + +For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something the following entry. + + url(r'^/users/', ListCreateAPIView.as_view(model=User) name='user-list') --- @@ -19,7 +48,7 @@ One of the key benefits of class based views is the way they allow you to compos ## ListAPIView -Used for read-write endpoints to represent a collection of model instances. +Used for read-only endpoints to represent a collection of model instances. Provides a `get` method handler. @@ -45,6 +74,8 @@ Provides `get`, `put` and `delete` method handlers. # Base views +Each of the generic views provided is built by combining one of the base views below, with one or more mixin classes. + ## BaseAPIView Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets. @@ -65,7 +96,7 @@ Provides a base view for acting on a single object, by combining REST framework' # Mixins -The mixin classes provide the actions that are used +The mixin classes provide the actions that are used to provide the basic view behaviour. Note that the mixin classes provide action methods rather than defining the handler methods such as `.get()` and `.post()` directly. This allows for more flexible composition of behaviour. ## ListModelMixin @@ -87,10 +118,6 @@ Provides a `.update(request, *args, **kwargs)` method, that implements updating Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance. -## MetadataMixin - -Provides a `.metadata(request, *args, **kwargs)` method, that returns a response containing metadata about the view. - [cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views [MultipleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/ [SingleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/ -- cgit v1.2.3 From bcd2caf5598a71cb468d86b6f286e180d1bf0a19 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 4 Oct 2012 09:18:46 +0100 Subject: Abstract out the app_label on test models --- docs/api-guide/fields.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/api-guide/fields.md (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md new file mode 100644 index 00000000..009d2a79 --- /dev/null +++ b/docs/api-guide/fields.md @@ -0,0 +1,43 @@ + + +# Serializer fields + +> Flat is better than nested. +> +> — [The Zen of Python][cite] + +# Generic Fields + +## Field + +## ModelField + +# Typed Fields + +## BooleanField + +## CharField + +## EmailField + +## DateField + +## DateTimeField + +## IntegerField + +## FloatField + +# Relational Fields + +Relational fields are used to represent model relationships. + +## PrimaryKeyRelatedField + +## ManyPrimaryKeyRelatedField + +## HyperlinkedRelatedField + +## ManyHyperlinkedRelatedField + +[cite]: http://www.python.org/dev/peps/pep-0020/ -- cgit v1.2.3 From d07dc77e91c1f99b47915b3cef30b565f2618e82 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 10:23:47 +0100 Subject: Accepted media type uses most specific of client/renderer media types. --- docs/api-guide/generic-views.md | 29 +++++++++++++++++++++++------ docs/api-guide/renderers.md | 14 +++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index b2284ae5..571cc66f 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -18,24 +18,25 @@ If the generic views don't suit the needs of your API, you can drop down to usin Typically when using the generic views, you'll override the view, and set several class attributes. class UserList(generics.ListCreateAPIView): - serializer = UserSerializer model = User - permissions = (IsAdminUser,) + serializer_class = UserSerializer + permission_classes = (IsAdminUser,) paginate_by = 100 For more complex cases you might also want to override various methods on the view class. For example. class UserList(generics.ListCreateAPIView): - serializer = UserSerializer model = User - permissions = (IsAdminUser,) + serializer_class = UserSerializer + permission_classes = (IsAdminUser,) def get_paginate_by(self): """ Use smaller pagination for HTML representations. """ - if self.request.accepted_media_type == 'text/html': - return 10 + page_size_param = self.request.QUERY_PARAMS.get('page_size') + if page_size_param: + return int(page_size_param) return 100 For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something the following entry. @@ -52,24 +53,32 @@ Used for read-only endpoints to represent a collection of model instances. Provides a `get` method handler. +Extends: [MultipleObjectBaseAPIView], [ListModelMixin] + ## ListCreateAPIView Used for read-write endpoints to represent a collection of model instances. Provides `get` and `post` method handlers. +Extends: [MultipleObjectBaseAPIView], [ListModelMixin], [CreateModelMixin] + ## RetrieveAPIView Used for read-only endpoints to represent a single model instance. Provides a `get` method handler. +Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin] + ## RetrieveUpdateDestroyAPIView Used for read-write endpoints to represent a single model instance. Provides `get`, `put` and `delete` method handlers. +Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin], [UpdateModelMixin], [DestroyModelMixin] + --- # Base views @@ -123,3 +132,11 @@ Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion [SingleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/ [multiple-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.list/MultipleObjectMixin/ [single-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.detail/SingleObjectMixin/ + +[SingleObjectBaseAPIView]: #singleobjectbaseapiview +[MultipleObjectBaseAPIView]: #multipleobjectbaseapiview +[ListModelMixin]: #listmodelmixin +[CreateModelMixin]: #createmodelmixin +[RetrieveModelMixin]: #retrievemodelmixin +[UpdateModelMixin]: #updatemodelmixin +[DestroyModelMixin]: #destroymodelmixin \ No newline at end of file diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 134c3749..e1c83477 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -115,7 +115,6 @@ For example: @api_view(('GET',)) @renderer_classes((TemplateHTMLRenderer, JSONRenderer)) - @template_name('list_users.html') def list_users(request): """ A view that can return JSON or HTML representations @@ -123,15 +122,16 @@ For example: """ queryset = Users.objects.filter(active=True) - if request.accepted_renderer.format == 'html': + if request.accepted_media_type == 'text/html': # TemplateHTMLRenderer takes a context dict, - # and does not require serialization. + # and additionally requiresa 'template_name'. + # It does not require serialization. data = {'users': queryset} - else: - # JSONRenderer requires serialized data as normal. - serializer = UserSerializer(instance=queryset) - data = serializer.data + return Response(data, template='list_users.html') + # JSONRenderer requires serialized data as normal. + serializer = UserSerializer(instance=queryset) + data = serializer.data return Response(data) ## Designing your media types -- cgit v1.2.3 From 26c7d6df6c0a12a2e19e07951b93de80bbfdf91c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 12:13:44 +0100 Subject: HTMLTemplateRenderer working --- docs/api-guide/renderers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index e1c83477..ef0ed46f 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -127,7 +127,7 @@ For example: # and additionally requiresa 'template_name'. # It does not require serialization. data = {'users': queryset} - return Response(data, template='list_users.html') + return Response(data, template_name='list_users.html') # JSONRenderer requires serialized data as normal. serializer = UserSerializer(instance=queryset) -- cgit v1.2.3 From 2575ea92aad3608142cfdd3ede5ee1b53e2064ba Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 13:04:34 +0100 Subject: Docs for template responses --- docs/api-guide/renderers.md | 19 ++++++++++++++- docs/api-guide/responses.md | 56 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 6 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index ef0ed46f..d2d8985d 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -86,11 +86,28 @@ If your API includes views that can serve both regular webpages and API response ## DocumentingHTMLRenderer +Renders data into HTML for the browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. + **.media_type:** `text/html` **.format:** `'.api'` -## TemplateHTMLRenderer +## HTMLTemplateRenderer + +Renders data to HTML, using Django's standard template rendering. +Unlike other renderers, the data passed to the `Response` does not need to be serialized. Also, unlike other renderers, you may want to include a `template_name` argument when creating the `Response`. + +The HTMLTemplateRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. + +The template name is determined by (in order of preference): + +1. An explicit `.template_name` attribute set on the response. +2. An explicit `.template_name` attribute set on this class. +3. The return result of calling `view.get_template_names()`. + +You can use `HTMLTemplateRenderer` 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 `HTMLTemplateRenderer` along with other renderer classes, you should consider listing `HTMLTemplateRenderer` 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. **.media_type:** `text/html` diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index e9ebcf81..d8f8e97c 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -14,7 +14,11 @@ There's no requirement for you to use the `Response` class, you can also return Unless you want to heavily customize REST framework for some reason, you should always use an `APIView` class or `@api_view` function for views that return `Response` objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view. -## Response(data, status=None, headers=None) +--- + +# Methods + +## Response(data, status=None, template_name=None, headers=None) Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any python primatives. @@ -22,16 +26,58 @@ The renderers used by the `Response` class cannot natively handle complex dataty You can use REST framework's `Serializer` classes to perform this data serialization, or use your own custom serialization. +Arguments: + +* `data`: The serialized data for the response. +* `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes]. +* `template_name`: A template name to use if `HTMLTemplateRenderer` is selected. +* `headers`: A dictionary of HTTP headers to use in the response. + +## .render() + +This methd 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)` 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. + +## Standard HTTPResponse methods + +The `Response` class extends `SimpleTemplateResponse`, and all the usual methods are also available on the response. For example you can set headers on the response in the standard way: + + response = Response() + response['Cache-Control'] = 'no-cache' + +--- + +# Attributes + ## .data The unrendered content of a `Request` object can be accessed using the `.data` attribute. +## .status_code + +The numeric status code of the HTTP response. + ## .content -To access the rendered content of a `Response` object, you must first call `.render()`. You'll typically only need to do this in cases such as unit testing responses - when you return a `Response` from a view Django's response cycle will handle calling `.render()` for you. +The rendered content of the response. `.render()` must have been called before `.content` can be accessed. + +## .template_name + +The `template_name`, if supplied. Only required if `HTMLTemplateRenderer` or some other custom template renderer is the accepted renderer for the reponse. + +## .accepted_renderer + +The renderer instance that will be used to render the response. + +Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view. + +## .accepted_media_type + +The media type that was selected by the content negotiation stage. -## .renderer +Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view. -When you return a `Response` instance, the `APIView` class or `@api_view` decorator will select the appropriate renderer, and set the `.renderer` attribute on the `Response`, before returning it from the view. -[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ \ No newline at end of file +[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ +[statuscodes]: status-codes.md \ No newline at end of file -- cgit v1.2.3 From 1a09983dfc2d7ac03f2722e054e04c300bf77e65 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 13:51:09 +0100 Subject: Tweak doc headings --- docs/api-guide/renderers.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index d2d8985d..f27eb360 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -60,6 +60,8 @@ For example if your API serves JSON responses and the HTML browseable API, you m If your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making `TemplateHTMLRenderer` your default renderer, in order to play nicely with older browsers that send [broken accept headers][browser-accept-headers]. +# API Reference + ## JSONRenderer **.media_type:** `application/json` @@ -117,7 +119,7 @@ If you're building websites that use `HTMLTemplateRenderer` along with other ren To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method. -## Advanced renderer usage +# Advanced renderer usage You can do some pretty flexible things using REST framework's renderers. Some examples... -- cgit v1.2.3 From 6a15556384c48984868493b6b38a7afa61d77a3a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 14:00:02 +0100 Subject: Docs tweaks --- docs/api-guide/responses.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index d8f8e97c..724428f8 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -18,7 +18,9 @@ Unless you want to heavily customize REST framework for some reason, you should # Methods -## Response(data, status=None, template_name=None, headers=None) +## Response() + +**Signature:** `Response(data, status=None, template_name=None, headers=None)` Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any python primatives. @@ -35,11 +37,13 @@ Arguments: ## .render() +**Signature:** `.render()` + This methd 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)` 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. -## Standard HTTPResponse methods +## Standard response methods The `Response` class extends `SimpleTemplateResponse`, and all the usual methods are also available on the response. For example you can set headers on the response in the standard way: -- cgit v1.2.3 From 3e862c77379b2f84356e2e8f0be20b7aca5b9e89 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 14:22:02 +0100 Subject: Tweak view slightly --- docs/api-guide/authentication.md | 6 +++--- docs/api-guide/renderers.md | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index c6995360..ae21c66e 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -50,9 +50,9 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. - @api_view('GET'), - @authentication_classes(SessionAuthentication, UserBasicAuthentication) - @permissions_classes(IsAuthenticated) + @api_view(('GET',)), + @authentication_classes((SessionAuthentication, UserBasicAuthentication)) + @permissions_classes((IsAuthenticated,)) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index f27eb360..2b1f423d 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -60,6 +60,8 @@ For example if your API serves JSON responses and the HTML browseable API, you m If your API includes views that can serve both regular webpages and API responses depending on the request, then you might consider making `TemplateHTMLRenderer` your default renderer, in order to play nicely with older browsers that send [broken accept headers][browser-accept-headers]. +--- + # API Reference ## JSONRenderer @@ -119,6 +121,8 @@ If you're building websites that use `HTMLTemplateRenderer` along with other ren To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method. +--- + # Advanced renderer usage You can do some pretty flexible things using REST framework's renderers. Some examples... @@ -128,6 +132,8 @@ You can do some pretty flexible things using REST framework's renderers. Some e * 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. +## Varying behaviour by media type + In some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access `request.accepted_renderer` to determine the negotiated renderer that will be used for the response. For example: -- cgit v1.2.3 From 84958d131a29d80acea94dec5260b484556e73d0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 15:22:30 +0100 Subject: Doc style tweaks --- docs/api-guide/responses.md | 4 ++-- docs/api-guide/views.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 724428f8..b5ad843b 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -56,7 +56,7 @@ The `Response` class extends `SimpleTemplateResponse`, and all the usual methods ## .data -The unrendered content of a `Request` object can be accessed using the `.data` attribute. +The unrendered content of a `Request` object. ## .status_code @@ -64,7 +64,7 @@ The numeric status code of the HTTP response. ## .content -The rendered content of the response. `.render()` must have been called before `.content` can be accessed. +The rendered content of the response. The `.render()` method must have been called before `.content` can be accessed. ## .template_name diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index a615026f..cbfa2e28 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -83,7 +83,7 @@ The following methods are called before dispatching to the handler method. The following methods are called directly by the view's `.dispatch()` method. These perform any actions that need to occur before or after calling the handler methods such as `.get()`, `.post()`, `put()` and `.delete()`. -### .initial(self, request, *args, **kwargs) +### .initial(self, request, \*args, **kwargs) Performs any actions that need to occur before the handler method gets called. This method is used to enforce permissions and throttling, and perform content negotiation. @@ -98,13 +98,13 @@ The default implementation handles any subclass of `rest_framework.exceptions.AP If you need to customize the error responses your API returns you should subclass this method. -### .initialize_request(self, request, *args, **kwargs) +### .initialize_request(self, request, \*args, **kwargs) Ensures that the request object that is passed to the handler method is an instance of `Request`, rather than the usual Django `HttpRequest`. You won't typically need to override this method. -### .finalize_response(self, request, response, *args, **kwargs) +### .finalize_response(self, request, response, \*args, **kwargs) Ensures that any `Response` object returned from the handler method will be rendered into the correct content type, as determined by the content negotation. -- cgit v1.2.3 From cc21948a69f5cea90ca02042549af29c20e0268a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 17:02:33 +0100 Subject: Fix django 1.3 bug --- docs/api-guide/fields.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 009d2a79..5f3aba9a 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -6,14 +6,30 @@ > > — [The Zen of Python][cite] +**Note:** The serializer fields are declared in fields.py, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.`. + +Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects. + # Generic Fields ## Field +A generic, read-only field. You can use this field for any attribute that does not need to support write operations. + +## WritableField + +A field that supports both read and + ## ModelField +A generic field that can be tied to any arbitrary model field. The `ModelField` class delegates the task of serialization/deserialization to it's associated model field. This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field. + +**Signature:** `ModelField(model_field=)` + # Typed Fields +These fields represent basic datatypes, and support both reading and writing values. + ## BooleanField ## CharField @@ -30,7 +46,7 @@ # Relational Fields -Relational fields are used to represent model relationships. +Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`. ## PrimaryKeyRelatedField @@ -40,4 +56,6 @@ Relational fields are used to represent model relationships. ## ManyHyperlinkedRelatedField +## HyperLinkedIdentityField + [cite]: http://www.python.org/dev/peps/pep-0020/ -- cgit v1.2.3 From 9bc4aead653517bda75ff42d011e2e026051dec9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Oct 2012 19:26:57 +0100 Subject: Tweak fields docs --- docs/api-guide/fields.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 5f3aba9a..56c84981 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -6,9 +6,13 @@ > > — [The Zen of Python][cite] +Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects. + +--- + **Note:** The serializer fields are declared in fields.py, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.`. -Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects. +--- # Generic Fields -- cgit v1.2.3 From f79ed6175df62f68dba9179f792f95b6ac6dca14 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 8 Oct 2012 14:13:15 +0100 Subject: Add RetrieveDestroyAPIView and remove Metadata mixin --- docs/api-guide/generic-views.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 571cc66f..8bf7a7e2 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -47,9 +47,11 @@ For very simple cases you might want to pass through any class attributes using # API Reference +The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior. + ## ListAPIView -Used for read-only endpoints to represent a collection of model instances. +Used for **read-only** endpoints to represent a **collection of model instances**. Provides a `get` method handler. @@ -57,7 +59,7 @@ Extends: [MultipleObjectBaseAPIView], [ListModelMixin] ## ListCreateAPIView -Used for read-write endpoints to represent a collection of model instances. +Used for **read-write** endpoints to represent a **collection of model instances**. Provides `get` and `post` method handlers. @@ -65,15 +67,23 @@ Extends: [MultipleObjectBaseAPIView], [ListModelMixin], [CreateModelMixin] ## RetrieveAPIView -Used for read-only endpoints to represent a single model instance. +Used for **read-only** endpoints to represent a **single model instance**. Provides a `get` method handler. Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin] +## RetrieveDestroyAPIView + +Used for **read or delete** endpoints to represent a **single model instance**. + +Provides `get` and `delete` method handlers. + +Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin], [DestroyModelMixin] + ## RetrieveUpdateDestroyAPIView -Used for read-write endpoints to represent a single model instance. +Used for **read-write** endpoints to represent a **single model instance**. Provides `get`, `put` and `delete` method handlers. @@ -111,28 +121,39 @@ The mixin classes provide the actions that are used to provide the basic view be Provides a `.list(request, *args, **kwargs)` method, that implements listing a queryset. +Should be mixed in with [MultipleObjectBaseAPIView]. + ## CreateModelMixin Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. +Should be mixed in with any [BaseAPIView]. + ## RetrieveModelMixin Provides a `.retrieve(request, *args, **kwargs)` method, that implements returning an existing model instance in a response. +Should be mixed in with [SingleObjectBaseAPIView]. + ## UpdateModelMixin Provides a `.update(request, *args, **kwargs)` method, that implements updating and saving an existing model instance. +Should be mixed in with [SingleObjectBaseAPIView]. + ## DestroyModelMixin Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance. +Should be mixed in with [SingleObjectBaseAPIView]. + [cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views [MultipleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/ [SingleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/ [multiple-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.list/MultipleObjectMixin/ [single-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.detail/SingleObjectMixin/ +[BaseAPIView]: #baseapiview [SingleObjectBaseAPIView]: #singleobjectbaseapiview [MultipleObjectBaseAPIView]: #multipleobjectbaseapiview [ListModelMixin]: #listmodelmixin -- cgit v1.2.3 From b581ffe323d88b6740abfed0fd552cc436fd2dcc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 8 Oct 2012 15:46:52 +0100 Subject: Docs tweaks --- docs/api-guide/fields.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 56c84981..e9833a97 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -30,6 +30,8 @@ A generic field that can be tied to any arbitrary model field. The `ModelField` **Signature:** `ModelField(model_field=)` +--- + # Typed Fields These fields represent basic datatypes, and support both reading and writing values. @@ -48,6 +50,8 @@ These fields represent basic datatypes, and support both reading and writing val ## FloatField +--- + # Relational Fields Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`. -- cgit v1.2.3 From 97a7f27c8219181e40dddcaf820545e08283de93 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Oct 2012 15:58:48 +0100 Subject: Rename HTMLTemplateRenderer -> HTMLRenderer, DocuemntingHTMLRenderer -> BrowseableAPIRenderer --- docs/api-guide/fields.md | 115 +++++++++++++++++++++++++++++++++++++++++++- docs/api-guide/renderers.md | 58 ++++++++++++++++------ docs/api-guide/responses.md | 6 +-- docs/api-guide/settings.md | 2 +- 4 files changed, 160 insertions(+), 21 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index e9833a97..df54ea02 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -16,13 +16,48 @@ Serializer fields handle converting between primative values and internal dataty # Generic Fields +These generic fields are used for representing arbitrary model fields or the output of model methods. + ## Field -A generic, read-only field. You can use this field for any attribute that does not need to support write operations. +A generic, **read-only** field. You can use this field for any attribute that does not need to support write operations. + +For example, using the following model. + + class Account(models.Model): + owner = models.ForeignKey('auth.user') + name = models.CharField(max_length=100) + created = models.DateTimeField(auto_now_add=True) + payment_expiry = models.DateTimeField() + + def has_expired(self): + now = datetime.datetime.now() + return now > self.payment_expiry + +A serializer definition that looked like this: + + class AccountSerializer(serializers.HyperlinkedModelSerializer): + expired = Field(source='has_expired') + + class Meta: + fields = ('url', 'owner', 'name', 'expired') + +Would produced output similar to: + + { + 'url': 'http://example.com/api/accounts/3/', + 'owner': 'http://example.com/api/users/12/', + 'name': 'FooCorp business account', + 'expired': True + } + +Be default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when neccesary. + +You can customize this behaviour by overriding the `.to_native(self, value)` method. ## WritableField -A field that supports both read and +A field that supports both read and write operations. By itself `WriteableField` does not perform any translation of input values into a given type. You won't typically use this field directly, but you may want to override it and implement the `.to_native(self, value)` and `.from_native(self, value)` methods. ## ModelField @@ -56,10 +91,86 @@ These fields represent basic datatypes, and support both reading and writing val Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`. +## RelatedField + +This field can be applied to any of the following: + +* A `ForeignKey` field. +* A `OneToOneField` field. +* A reverse OneToOne relationship +* Any other "to-one" relationship. + +By default `RelatedField` will represent the target of the field using it's `__unicode__` method. + +You can customise this behaviour by subclassing `ManyRelatedField`, and overriding the `.to_native(self, value)` method. + +## ManyRelatedField + +This field can be applied to any of the following: + +* A `ManyToManyField` field. +* A reverse ManyToMany relationship. +* A reverse ForeignKey relationship +* Any other "to-many" relationship. + +By default `ManyRelatedField` will represent the targets of the field using their `__unicode__` method. + +For example, given the following models: + + class TaggedItem(models.Model): + """ + Tags arbitrary model instances using a generic relation. + + See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ + """ + tag = models.SlugField() + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + + def __unicode__(self): + return self.tag + + + class Bookmark(models.Model): + """ + A bookmark consists of a URL, and 0 or more descriptive tags. + """ + url = models.URLField() + tags = GenericRelation(TaggedItem) + +And a model serializer defined like this: + + class BookmarkSerializer(serializers.ModelSerializer): + tags = serializers.ManyRelatedField(source='tags') + + class Meta: + model = Bookmark + exclude = ('id',) + +The an example output format for a Bookmark instance would be: + + { + 'tags': [u'django', u'python'], + 'url': u'https://www.djangoproject.com/' + } + ## PrimaryKeyRelatedField +As with `RelatedField` field can be applied to any "to-one" relationship, such as a `ForeignKey` field. + +`PrimaryKeyRelatedField` will represent the target of the field using it's primary key. + +Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. + ## ManyPrimaryKeyRelatedField +As with `RelatedField` field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. + +`PrimaryKeyRelatedField` will represent the target of the field using their primary key. + +Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. + ## HyperlinkedRelatedField ## ManyHyperlinkedRelatedField diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 2b1f423d..5d93ea38 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -23,7 +23,7 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERERS` REST_FRAMEWORK = { 'DEFAULT_RENDERERS': ( 'rest_framework.renderers.YAMLRenderer', - 'rest_framework.renderers.DocumentingHTMLRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer', ) } @@ -88,20 +88,12 @@ If your API includes views that can serve both regular webpages and API response **.format:** `'.xml'` -## DocumentingHTMLRenderer - -Renders data into HTML for the browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. - -**.media_type:** `text/html` - -**.format:** `'.api'` - -## HTMLTemplateRenderer +## HTMLRenderer Renders data to HTML, using Django's standard template rendering. Unlike other renderers, the data passed to the `Response` does not need to be serialized. Also, unlike other renderers, you may want to include a `template_name` argument when creating the `Response`. -The HTMLTemplateRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. +The HTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. The template name is determined by (in order of preference): @@ -109,18 +101,54 @@ The template name is determined by (in order of preference): 2. An explicit `.template_name` attribute set on this class. 3. The return result of calling `view.get_template_names()`. -You can use `HTMLTemplateRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. +An example of a view that uses `HTMLRenderer`: -If you're building websites that use `HTMLTemplateRenderer` along with other renderer classes, you should consider listing `HTMLTemplateRenderer` 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. + class UserInstance(generics.RetrieveUserAPIView): + """ + A view that returns a templated HTML representations of a given user. + """ + model = Users + renderer_classes = (HTMLRenderer,) + + def get(self, request, \*args, **kwargs) + self.object = self.get_object() + return Response(self.object, template_name='user_detail.html') + +You can use `HTMLRenderer` 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 `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` 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. **.media_type:** `text/html` **.format:** `'.html'` +## BrowsableAPIRenderer + +Renders data into HTML for the Browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. + +**.media_type:** `text/html` + +**.format:** `'.api'` + ## Custom renderers To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method. +For example: + + from django.utils.encoding import smart_unicode + from rest_framework import renderers + + + class PlainText(renderers.BaseRenderer): + media_type = 'text/plain' + format = 'txt' + + def render(self, data, media_type): + if isinstance(data, basestring): + return data + return smart_unicode(data) + --- # Advanced renderer usage @@ -139,7 +167,7 @@ In some cases you might want your view to use different serialization styles dep For example: @api_view(('GET',)) - @renderer_classes((TemplateHTMLRenderer, JSONRenderer)) + @renderer_classes((HTMLRenderer, JSONRenderer)) def list_users(request): """ A view that can return JSON or HTML representations @@ -173,4 +201,4 @@ For good examples of custom media types, see GitHub's use of a custom [applicati [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ -[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ \ No newline at end of file +[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index b5ad843b..e0198324 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -32,7 +32,7 @@ Arguments: * `data`: The serialized data for the response. * `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes]. -* `template_name`: A template name to use if `HTMLTemplateRenderer` is selected. +* `template_name`: A template name to use if `HTMLRenderer` is selected. * `headers`: A dictionary of HTTP headers to use in the response. ## .render() @@ -68,7 +68,7 @@ The rendered content of the response. The `.render()` method must have been cal ## .template_name -The `template_name`, if supplied. Only required if `HTMLTemplateRenderer` or some other custom template renderer is the accepted renderer for the reponse. +The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the reponse. ## .accepted_renderer @@ -84,4 +84,4 @@ Set automatically by the `APIView` or `@api_view` immediately before the respons [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ -[statuscodes]: status-codes.md \ No newline at end of file +[statuscodes]: status-codes.md diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 43be0d47..f473128e 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -38,7 +38,7 @@ Default: ( 'rest_framework.renderers.JSONRenderer', - 'rest_framework.renderers.DocumentingHTMLRenderer' + 'rest_framework.renderers.BrowsableAPIRenderer' 'rest_framework.renderers.TemplateHTMLRenderer' ) -- cgit v1.2.3 From 52a2ff8f77dc22a65e6f5780de8183f261472342 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Oct 2012 16:44:49 +0100 Subject: Docs tweaks --- docs/api-guide/renderers.md | 2 +- docs/api-guide/reverse.md | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 5d93ea38..1b266f7e 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -110,7 +110,7 @@ An example of a view that uses `HTMLRenderer`: model = Users renderer_classes = (HTMLRenderer,) - def get(self, request, \*args, **kwargs) + def get(self, request, *args, **kwargs) self.object = self.get_object() return Response(self.object, template_name='user_detail.html') diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 8485087e..82cadffc 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -21,10 +21,12 @@ There's no requirement for you to use them, but if you do then the self-describi ## reverse -**Signature:** `reverse(viewname, request, *args, **kwargs)` +**Signature:** `reverse(viewname, *args, **kwargs)` Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. +You should **include the request as a keyword argument** to the function, for example: + import datetime from rest_framework.utils import reverse from rest_framework.views import APIView @@ -34,16 +36,20 @@ Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except t year = datetime.datetime.now().year data = { ... - 'year-summary-url': reverse('year-summary', request, args=[year]) + 'year-summary-url': reverse('year-summary', args=[year], request=request) } return Response(data) ## reverse_lazy -**Signature:** `reverse_lazy(viewname, request, *args, **kwargs)` +**Signature:** `reverse_lazy(viewname, *args, **kwargs)` Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. +As with the `reverse` function, you should **include the request as a keyword argument** to the function, for example: + + api_root = reverse_lazy('api-root', request=request) + [cite]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 [reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse [reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy -- cgit v1.2.3 From b0c370dd2b42db9074c2580ca4a48d7dda088abf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Oct 2012 17:36:03 +0100 Subject: Fixed couple of incorrect imports in the docs --- docs/api-guide/reverse.md | 2 +- docs/api-guide/serializers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 82cadffc..12346eb4 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -28,7 +28,7 @@ Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except t You should **include the request as a keyword argument** to the function, for example: import datetime - from rest_framework.utils import reverse + from rest_framework.reverse import reverse from rest_framework.views import APIView class APIRootView(APIView): diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 4ddc6e0a..fff03241 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -94,7 +94,7 @@ The `Serializer` class is itself a type of `Field`, and can be used to represent class CommentSerializer(serializers.Serializer): - user = serializers.UserSerializer() + user = UserSerializer() title = serializers.CharField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() -- cgit v1.2.3 From 9bbc1cc403e3cf171710ae02255e2b7e6185f823 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Oct 2012 17:49:04 +0100 Subject: Add flag in get_related_field --- docs/api-guide/serializers.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index fff03241..47958fe3 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -120,7 +120,7 @@ Let's look at an example of serializing a class that represents an RGB color val assert(red < 256 and green < 256 and blue < 256) self.red, self.green, self.blue = red, green, blue - class ColourField(Field): + class ColourField(serializers.WritableField): """ Color objects are serialized into "rgb(#, #, #)" notation. """ @@ -138,7 +138,7 @@ By default field values are treated as mapping to an attribute on the object. I As an example, let's create a field that can be used represent the class name of the object being serialized: - class ClassNameField(Field): + class ClassNameField(serializers.WritableField): def field_to_native(self, obj, field_name): """ Serialize the object's class name, not an attribute of the object. @@ -158,7 +158,7 @@ As an example, let's create a field that can be used represent the class name of Often you'll want serializer classes that map closely to model definitions. The `ModelSerializer` class lets you automatically create a Serializer class with fields that corrospond to the Model fields. - class AccountSerializer(ModelSerializer): + class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account @@ -168,7 +168,7 @@ The `ModelSerializer` class lets you automatically create a Serializer class wit 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. - class AccountSerializer(ModelSerializer): + class AccountSerializer(serializers.ModelSerializer): url = CharField(source='get_absolute_url', readonly=True) group = NaturalKeyField() @@ -183,7 +183,7 @@ When serializing model instances, there are a number of different ways you might Alternative representations include serializing using natural keys, serializing complete nested representations, or serializing using a custom representation, such as a URL that uniquely identifies the model instances. -The `PrimaryKeyField` and `NaturalKeyField` fields provide alternative flat representations. +The `PrimaryKeyRelatedField` and `HyperlinkedRelatedField` fields provide alternative flat representations. The `ModelSerializer` class can itself be used as a field, in order to serialize relationships using nested representations. @@ -197,20 +197,16 @@ If you only want a subset of the default fields to be used in a model serializer For example: - class AccountSerializer(ModelSerializer): + class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ('id',) -The `fields` and `exclude` options may also be set by passing them to the `serialize()` method. - -**[TODO: Possibly only allow .serialize(fields=…) in FixtureSerializer for backwards compatability, but remove for ModelSerializer]** - ## Specifiying nested serialization The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `nested` option: - class AccountSerializer(ModelSerializer): + class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ('id',) @@ -220,27 +216,28 @@ The `nested` option may be set to either `True`, `False`, or an integer value. When serializing objects using a nested representation any occurances of recursion will be recognised, and will fall back to using a flat representation. -The `nested` option may also be set by passing it to the `serialize()` method. +## Customising the default fields used by a ModelSerializer -**[TODO: Possibly only allow .serialize(nested=…) in FixtureSerializer]** -## Customising the default fields used by a ModelSerializer - class AccountSerializer(ModelSerializer): + class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account def get_pk_field(self, model_field): - return Field(readonly=True) + return serializers.Field(readonly=True) def get_nested_field(self, model_field): - return ModelSerializer() + return serializers.ModelSerializer() - def get_related_field(self, model_field): - return NaturalKeyField() + def get_related_field(self, model_field, to_many=False): + queryset = model_field.rel.to._default_manager + if to_many: + return return serializers.ManyRelatedField(queryset=queryset) + return serializers.RelatedField(queryset=queryset) def get_field(self, model_field): - return Field() + return serializers.ModelField(model_field=model_field) [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion -- cgit v1.2.3 From ccd2b0117d9c26199b1862a302b1eb06dd2f07b2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 10 Oct 2012 10:02:37 +0100 Subject: Permissions and throttles no longer have a view attribute on self. Explicitly passed to .has_permissions(request, view, obj=None) / .allow_request(request, view) --- docs/api-guide/permissions.md | 2 +- docs/api-guide/throttling.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 9a3c39ab..b6912d88 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -88,7 +88,7 @@ The `DjangoModelPermissions` class also supports object-level permissions. Thir ## Custom permissions -To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, obj=None)` method. +To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method. The method should return `True` if the request should be granted access, and `False` otherwise. diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 7861e9ba..0e228905 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -144,8 +144,8 @@ User requests to either `ContactListView` or `ContactDetailView` would be restri ## Custom throttles -To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request)`. The method should return `True` if the request should be allowed, and `False` otherwise. +To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise. -Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.check_throttle()` has previously returned `False`. +Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.allow_request()` has previously returned `False`. [permissions]: permissions.md -- cgit v1.2.3 From 648d2be29b0738999742f4d844caab7b7652d1ad Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 10 Oct 2012 12:15:18 +0100 Subject: Make sure JSON output in Browseable API is nicely indented --- docs/api-guide/renderers.md | 19 +++++++++++++++++-- docs/api-guide/responses.md | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 1b266f7e..b2ebd0c7 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -132,7 +132,7 @@ Renders data into HTML for the Browseable API. This renderer will determine whi ## Custom renderers -To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method. +To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method. For example: @@ -144,11 +144,26 @@ For example: media_type = 'text/plain' format = 'txt' - def render(self, data, media_type): + def render(self, data, media_type=None, renderer_context=None): if isinstance(data, basestring): return data return smart_unicode(data) +The arguments passed to the `.render()` method are: + +#### `data` + +The request data, as set by the `Response()` instantiation. + +#### `media_type=None` + +Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`. + +#### `renderer_context=None` + +Optional. If provided, this is a dictionary of contextual information provided by the view. +By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`. + --- # Advanced renderer usage diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index e0198324..b0de6824 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -82,6 +82,11 @@ The media type that was selected by the content negotiation stage. Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view. +## .renderer_context + +A dictionary of additional context information that will be passed to the renderer's `.render()` method. + +Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view. [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/ [statuscodes]: status-codes.md -- cgit v1.2.3 From 7608cf1193fd555f31eb9a3d98c6f258720f8022 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 13 Oct 2012 15:07:43 +0100 Subject: Improve documentation for Requests --- docs/api-guide/requests.md | 101 +++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 26 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 36513cd9..919e410a 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -6,63 +6,112 @@ > > — Malcom Tredinnick, [Django developers group][cite] -REST framework's `Request` class extends the standard `HttpRequest`, adding support for parsing multiple content types, allowing browser-based `PUT`, `DELETE` and other methods, and adding flexible per-request authentication. +REST framework's `Request` class extends the standard `HttpRequest`, adding support for REST framework's flexible request parsing and request authentication. -## .method +--- -`request.method` returns the uppercased string representation of the request's HTTP method. +# Request parsing -Browser-based `PUT`, `DELETE` and other requests are supported, and can be made by using a hidden form field named `_method` in a regular `POST` form. +REST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data. +## .DATA +`request.DATA` returns the parsed content of the request body. This is similar to the standard `request.POST` attribute except that: -## .content_type +* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests. +* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data. -`request.content`, returns a string object representing the mimetype of the HTTP request's body, if one exists. +For more details see the [parsers documentation]. +## .FILES +`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing that is used for `request.DATA`. -## .DATA +For more details see the [parsers documentation]. -`request.DATA` returns the parsed content of the request body. This is similar to the standard `HttpRequest.POST` attribute except that: +## .QUERY_PARAMS -1. It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests. -2. It supports parsing multiple content types, rather than just form data. For example you can handle incoming json data in the same way that you handle incoming form data. +`request.QUERY_PARAMS` is a more correcly named synonym for `request.GET`. -## .FILES +For clarity inside your code, we recommend using `request.QUERY_PARAMS` instead of the usual `request.GET`, as *any* HTTP method type may include query parameters. -`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing that is used for `request.DATA`. +## .parsers -This allows you to support file uploads from multiple content-types. For example you can write a parser that supports `POST`ing the raw content of a file, instead of using form-encoded file uploads. +The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSERS` setting. + +You won't typically need to access this property. + +--- + +**Note:** If a client sends malformed content, then accessing `request.DATA` or `request.FILES` may raise a `ParseError`. By default REST framework's `APIView` class or `@api_view` decorator will catch the error and return a `400 Bad Request` response. + +--- + +# Authentication + +REST framework provides flexbile, per-request authentication, that gives you the abilty to: + +* Use different authentication policies for different parts of your API. +* Support the use of multiple authentication policies. +* Provide both user and token information associated with the incoming request. ## .user -`request.user` returns a `django.contrib.auth.models.User` instance. +`request.user` typically returns an instance of `django.contrib.auth.models.User`, although the behavior depends on the authentication policy being used. + +If the request is unauthenticated the default value of `request.user` is an instance of `django.contrib.auth.models.AnonymousUser`. + +For more details see the [authentication documentation]. ## .auth -`request.auth` returns any additional authentication context that may not be contained in `request.user`. The exact behavior of `request.auth` depends on what authentication has been set in `request.authentication`. For many types of authentication this will simply be `None`, but it may also be an object representing a permission scope, an expiry time, or any other information that might be contained in a token-based authentication scheme. +`request.auth` returns any additional authentication context. The exact behavior of `request.auth` depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against. -## .parsers +If the request is unauthenticated, or if no additional context is present, the default value of `request.auth` is `None`. -`request.parsers` should be set to a list of `Parser` instances that can be used to parse the content of the request body. +For more details see the [authentication documentation]. -`request.parsers` may no longer be altered once `request.DATA`, `request.FILES` or `request.POST` have been accessed. +## .authenticators -If you're using the `rest_framework.views.View` class... **[TODO]** +The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Authentication` instances, based on the `authentication_classes` set on the view or based on the `DEFAULT_AUTHENTICATORS` setting. -## .stream +You won't typically need to access this property. -`request.stream` returns a stream representing the content of the request body. +--- + +# Browser enhancments + +REST framework supports a few browser enhancments such as broser-based `PUT` and `DELETE` forms. + +## .method + +`request.method` returns the **uppercased** string representation of the request's HTTP method. + +Browser-based `PUT` and `DELETE` forms are transparently supported. -You will not typically need to access `request.stream`, unless you're writing a `Parser` class. +For more information see the [browser enhancements documentation]. -## .authentication +## .content_type + +`request.content_type`, returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided. + +You won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior. + +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]. + +## .stream + +`request.stream` returns a stream representing the content of the request body. -`request.authentication` should be set to a list of `Authentication` instances that can be used to authenticate the request. +You won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior. -`request.authentication` may no longer be altered once `request.user` or `request.auth` have been accessed. +If you do need to access the raw content directly, you should use the `.stream` property in preference to using `request.content`, as it provides transparent support for browser-based non-form content. -If you're using the `rest_framework.views.View` class... **[TODO]** +For more information see the [browser enhancements documentation]. [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion +[parsers documentation]: parsers.md +[authentication documentation]: authentication.md +[browser enhancements documentation]: ../topics/browser-enhancements.md \ No newline at end of file -- cgit v1.2.3 From fe5db419491fb400d4f7211f57225719087d7c36 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 13 Oct 2012 15:12:44 +0100 Subject: Fix typo --- docs/api-guide/requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 919e410a..a371143a 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -79,7 +79,7 @@ You won't typically need to access this property. --- -# Browser enhancments +# Browser enhancements REST framework supports a few browser enhancments such as broser-based `PUT` and `DELETE` forms. -- cgit v1.2.3 From 27b8904ffeb354422a688f40fbe0dfb398669327 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 13 Oct 2012 23:28:05 +0200 Subject: Fix typo --- docs/api-guide/requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index a371143a..9711d989 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -81,7 +81,7 @@ You won't typically need to access this property. # Browser enhancements -REST framework supports a few browser enhancments such as broser-based `PUT` and `DELETE` forms. +REST framework supports a few browser enhancments such as browser-based `PUT` and `DELETE` forms. ## .method -- cgit v1.2.3 From 7a89d7a770d310f8b72178c561fcd04e6c15e5c4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 14 Oct 2012 20:46:38 +0100 Subject: Work on docs --- docs/api-guide/requests.md | 9 +++++++++ docs/api-guide/responses.md | 34 ++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index a371143a..91563e3b 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -111,6 +111,15 @@ If you do need to access the raw content directly, you should use the `.stream` For more information see the [browser enhancements documentation]. +--- + +# Standard HttpRequest attributes + +As REST framework's `Request` extends Django's `HttpRequest`, all the other standard attributes and methods are also available. For example the `request.META` dictionary is available as normal. + +Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition. + + [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion [parsers documentation]: parsers.md [authentication documentation]: authentication.md diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index b0de6824..395decda 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -16,7 +16,7 @@ Unless you want to heavily customize REST framework for some reason, you should --- -# Methods +# Creating responses ## Response() @@ -35,21 +35,6 @@ Arguments: * `template_name`: A template name to use if `HTMLRenderer` is selected. * `headers`: A dictionary of HTTP headers to use in the response. -## .render() - -**Signature:** `.render()` - -This methd 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)` 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. - -## Standard response methods - -The `Response` class extends `SimpleTemplateResponse`, and all the usual methods are also available on the response. For example you can set headers on the response in the standard way: - - response = Response() - response['Cache-Control'] = 'no-cache' - --- # Attributes @@ -88,5 +73,22 @@ A dictionary of additional context information that will be passed to the render Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view. +--- + +# Standard HttpResponse attributes + +The `Response` class extends `SimpleTemplateResponse`, and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way: + + response = Response() + response['Cache-Control'] = 'no-cache' + +## .render() + +**Signature:** `.render()` + +As with any other `TemplateResponse`, this methd 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 -- cgit v1.2.3 From 551c86c43a71f7dee5cce68c5142714301f6196f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 14 Oct 2012 22:43:07 +0100 Subject: Documentation for parsers --- docs/api-guide/parsers.md | 129 +++++++++++++++++++++++++++++++++++++++++++- docs/api-guide/renderers.md | 57 +++++++++++++------- docs/api-guide/requests.md | 2 + 3 files changed, 169 insertions(+), 19 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index b9bb4900..4b769672 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -8,17 +8,144 @@ sending more complex data than simple forms > > — Malcom Tredinnick, [Django developers group][cite] +REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexiblity to design the media types that your API accepts. + +## How the parser is determined + +The set of valid parsers for a view is always defined as a list of classes. When either `request.DATA` or `request.FILES` is accessed, REST framework will examine the `Content-Type` header on the incoming request, and determine which parser to use to parse the request content. + +## Setting the parsers + +The default set of parsers may be set globally, using the `DEFAULT_PARSERS` setting. For example, the following settings would allow requests with `YAML` content. + + REST_FRAMEWORK = { + 'DEFAULT_PARSERS': ( + 'rest_framework.parsers.YAMLParser', + ) + } + +You can also set the renderers used for an individual view, using the `APIView` class based views. + + class ExampleView(APIView): + """ + A view that can accept POST requests with YAML content. + """ + parser_classes = (YAMLParser,) + + def post(self, request, format=None): + return Response({'received data': request.DATA}) + +Or, if you're using the `@api_view` decorator with function based views. + + @api_view(('POST',)), + @parser_classes((YAMLParser,)) + def example_view(request, format=None): + """ + A view that can accept POST requests with YAML content. + """ + return Response({'received data': request.DATA}) + +--- + +# API Reference ## JSONParser +Parses `JSON` request content. + +**.media_type**: `application/json` + ## YAMLParser +Parses `YAML` request content. + +**.media_type**: `application/yaml` + ## XMLParser +Parses REST framework's default style of `XML` request content. + +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. + +If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. + +**.media_type**: `application/xml` + ## FormParser +Parses HTML form content. `request.DATA` will be populated with a `QueryDict` of data, `request.FILES` will be populated with an empty `QueryDict` of data. + +You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data. + +**.media_type**: `application/x-www-form-urlencoded` + ## MultiPartParser -## Custom parsers +Parses multipart HTML form content, which supports file uploads. Both `request.DATA` and `request.FILES` will be populated with a `QueryDict`. + +You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data. + +**.media_type**: `multipart/form-data` + +--- + +# Custom parsers + +To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse_stream(self, stream, parser_context)` method. + +The method should return the data that will be used to populate the `request.DATA` property. + +For example: + + class PlainTextParser(BaseParser): + """ + Plain text parser. + """ + + media_type = 'text/plain' + + def parse_stream(self, stream, parser_context=None): + """ + Simply return a string representing the body of the request. + """ + return stream.read() + +The arguments passed to `.parse_stream()` are: + +### stream + +A stream-like object representing the body of the request. + +### parser_context + +If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. + +## Uploading file content + +If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property. + +For example: + + class SimpleFileUploadParser(BaseParser): + """ + A naive raw file upload parser. + """ + + def parse_stream(self, stream, parser_context): + content = stream.read() + name = 'example.dat' + content_type = 'application/octet-stream' + size = len(content) + charset = 'utf-8' + + # Write a temporary file based on the request content + temp = tempfile.NamedTemporaryFile(delete=False) + temp.write(content) + uploaded = UploadedFile(temp, name, content_type, size, charset) + + # Return the uploaded file + data = {} + files = {name: uploaded} + return DataAndFiles(data, files) [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b2ebd0c7..024a4ee2 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -42,8 +42,8 @@ You can also set the renderers used for an individual view, using the `APIView` Or, if you're using the `@api_view` decorator with function based views. - @api_view('GET'), - @renderer_classes(JSONRenderer, JSONPRenderer) + @api_view(('GET',)), + @renderer_classes((JSONRenderer, JSONPRenderer)) def user_count_view(request, format=None): """ A view that returns the count of active users, in JSON or JSONp. @@ -66,27 +66,45 @@ If your API includes views that can serve both regular webpages and API response ## JSONRenderer -**.media_type:** `application/json` +Renders the request data into `JSON`. -**.format:** `'.json'` +The client may additionally include an `'indent'` media type parameter, in which case the returned `JSON` will be indented. For example `Accept: application/json; indent=4`. + +**.media_type**: `application/json` + +**.format**: `'.json'` ## JSONPRenderer -**.media_type:** `application/javascript` +Renders the request data into `JSONP`. The `JSONP` media type provides a mechanism of allowing cross-domain AJAX requests, by wrapping a `JSON` response in a javascript callback. + +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'`. -**.format:** `'.jsonp'` +**Note**: If you require cross-domain AJAX requests, you may also want to consider using [CORS] as an alternative to `JSONP`. + +**.media_type**: `application/javascript` + +**.format**: `'.jsonp'` ## YAMLRenderer -**.media_type:** `application/yaml` +Renders the request data into `YAML`. + +**.media_type**: `application/yaml` -**.format:** `'.yaml'` +**.format**: `'.yaml'` ## XMLRenderer -**.media_type:** `application/xml` +Renders REST framework's default style of `XML` response content. -**.format:** `'.xml'` +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. + +If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. + +**.media_type**: `application/xml` + +**.format**: `'.xml'` ## HTMLRenderer @@ -118,19 +136,21 @@ You can use `HTMLRenderer` either to return regular HTML pages using REST framew If you're building websites that use `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` 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. -**.media_type:** `text/html` +**.media_type**: `text/html` -**.format:** `'.html'` +**.format**: `'.html'` ## BrowsableAPIRenderer Renders data into HTML for the Browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. -**.media_type:** `text/html` +**.media_type**: `text/html` -**.format:** `'.api'` +**.format**: `'.api'` + +--- -## Custom renderers +# Custom renderers To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method. @@ -151,15 +171,15 @@ For example: The arguments passed to the `.render()` method are: -#### `data` +### `data` The request data, as set by the `Response()` instantiation. -#### `media_type=None` +### `media_type=None` Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`. -#### `renderer_context=None` +### `renderer_context=None` Optional. If provided, this is a dictionary of contextual information provided by the view. By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`. @@ -213,6 +233,7 @@ For good examples of custom media types, see GitHub's use of a custom [applicati [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 +[CORS]: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 91563e3b..23d84a53 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -45,6 +45,8 @@ You won't typically need to access this property. **Note:** If a client sends malformed content, then accessing `request.DATA` or `request.FILES` may raise a `ParseError`. By default REST framework's `APIView` class or `@api_view` decorator will catch the error and return a `400 Bad Request` response. +If a client sends a request with a content-type that cannot be parsed then a `UnsupportedMediaType` exception will be raised, which by default will be caught and return a `415 Unsupported Media Type` response. + --- # Authentication -- cgit v1.2.3 From c94272650915eef368cdc5d157644884c3eecccb Mon Sep 17 00:00:00 2001 From: Jens Alm Date: Mon, 15 Oct 2012 13:46:44 +0200 Subject: Added docs, integer fields and refactored models.TextField to use CharField I realized that per the django forms, there is no need for a separate TextField, an unlimited CharField is perfectly good. Also added default field for the different IntegerField types --- docs/api-guide/fields.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index df54ea02..dc9ab045 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -73,18 +73,35 @@ These fields represent basic datatypes, and support both reading and writing val ## BooleanField +A Boolean representation, corresponds to `django.db.models.fields.BooleanField`. + ## CharField +A text representation, optionally validates the text to be shorter than `max_length` and longer than `min_length`, corresponds to `django.db.models.fields.CharField` +or `django.db.models.fields.TextField`. + +**Signature:** `CharField([max_length=[, min_length=]])` + ## EmailField +A text representation, validates the text to be a valid e-mail adress. Corresponds to `django.db.models.fields.EmailField` + ## DateField +A date representation. Corresponds to `django.db.models.fields.DateField` + ## DateTimeField +A date and time representation. Corresponds to `django.db.models.fields.DateTimeField` + ## IntegerField +An integer representation. Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` and `django.db.models.fields.PositiveSmallIntegerField` + ## FloatField +A floating point representation. Corresponds to `django.db.models.fields.FloatField`. + --- # Relational Fields -- cgit v1.2.3 From 9c1fba3483b7e81da0744464dcf23a5f12711de2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 15 Oct 2012 13:27:50 +0100 Subject: Tweak parsers to take parser_context --- docs/api-guide/authentication.md | 4 +++- docs/api-guide/exceptions.md | 2 +- docs/api-guide/pagination.md | 2 +- docs/api-guide/parsers.md | 26 ++++++++++++++------------ docs/api-guide/permissions.md | 4 +++- docs/api-guide/renderers.md | 34 ++++++++++++++++++---------------- docs/api-guide/throttling.md | 4 +++- 7 files changed, 43 insertions(+), 33 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index ae21c66e..71f48163 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -60,6 +60,8 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) +# API Reference + ## BasicAuthentication This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. Basic authentication is generally only appropriate for testing. @@ -113,7 +115,7 @@ If successfully authenticated, `SessionAuthentication` provides the following cr * `request.user` will be a `django.contrib.auth.models.User` instance. * `request.auth` will be `None`. -## Custom authentication policies +# Custom authentication To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise. diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index c22d6d8b..c3bdb7b9 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -37,7 +37,7 @@ Might recieve an error response indicating that the `DELETE` method is not allow **Signature:** `APIException(detail=None)` -The base class for all exceptions raised inside REST framework. +The **base class** for all exceptions raised inside REST framework. To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class. diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 50be59bf..e416de02 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -100,7 +100,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. -## Creating custom pagination serializers +## Custom pagination serializers To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return. diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 4b769672..4f145ba3 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -65,7 +65,7 @@ Parses `YAML` request content. Parses REST framework's default style of `XML` request content. -Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`. If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. @@ -95,7 +95,19 @@ To implement a custom parser, you should override `BaseParser`, set the `.media_ The method should return the data that will be used to populate the `request.DATA` property. -For example: +The arguments passed to `.parse_stream()` are: + +### stream + +A stream-like object representing the body of the request. + +### parser_context + +If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. + +## Example + +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): """ @@ -110,16 +122,6 @@ For example: """ return stream.read() -The arguments passed to `.parse_stream()` are: - -### stream - -A stream-like object representing the body of the request. - -### parser_context - -If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. - ## Uploading file content If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property. diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index b6912d88..eb290849 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -54,6 +54,8 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) +# API Reference + ## IsAuthenticated The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise. @@ -86,7 +88,7 @@ To use custom model permissions, override `DjangoModelPermissions` and set the ` The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required. -## Custom permissions +# Custom permissions To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method. diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 024a4ee2..c8addb32 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -98,7 +98,7 @@ Renders the request data into `YAML`. Renders REST framework's default style of `XML` response content. -Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, `SOAP`, and `XHTML`. +Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`. If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. @@ -154,21 +154,6 @@ Renders data into HTML for the Browseable API. This renderer will determine whi To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type=None, renderer_context=None)` method. -For example: - - from django.utils.encoding import smart_unicode - from rest_framework import renderers - - - class PlainText(renderers.BaseRenderer): - media_type = 'text/plain' - format = 'txt' - - def render(self, data, media_type=None, renderer_context=None): - if isinstance(data, basestring): - return data - return smart_unicode(data) - The arguments passed to the `.render()` method are: ### `data` @@ -184,6 +169,23 @@ Optional. If provided, this is the accepted media type, as determined by the con Optional. If provided, this is a dictionary of contextual information provided by the view. By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`. +## Example + +The following is an example plaintext renderer that will return a response with the `data` parameter as the content of the response. + + from django.utils.encoding import smart_unicode + from rest_framework import renderers + + + class PlainText(renderers.BaseRenderer): + media_type = 'text/plain' + format = 'txt' + + def render(self, data, media_type=None, renderer_context=None): + if isinstance(data, basestring): + return data + return smart_unicode(data) + --- # Advanced renderer usage diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 0e228905..3fb95ae3 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -63,6 +63,8 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) +# API Reference + ## AnonRateThrottle The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against. @@ -142,7 +144,7 @@ For example, given the following views... User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day. -## Custom throttles +# Custom throttles To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise. -- cgit v1.2.3 From cab4a2a5ad17545ac435785bf55b0b3a6c8f932c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 15:41:57 +0100 Subject: Split up doc sections more cleanly --- docs/api-guide/exceptions.md | 4 ++++ docs/api-guide/permissions.md | 5 +++++ docs/api-guide/settings.md | 4 ++++ docs/api-guide/throttling.md | 4 ++++ docs/api-guide/views.md | 4 ++-- 5 files changed, 19 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index c3bdb7b9..33cf1ca8 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -33,6 +33,10 @@ Might recieve an error response indicating that the `DELETE` method is not allow {"detail": "Method 'DELETE' not allowed."} +--- + +# API Reference + ## APIException **Signature:** `APIException(detail=None)` diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index eb290849..b25b52be 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -54,6 +54,8 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) +--- + # API Reference ## IsAuthenticated @@ -88,12 +90,15 @@ To use custom model permissions, override `DjangoModelPermissions` and set the ` The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required. +--- + # Custom permissions To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method. The method should return `True` if the request should be granted access, and `False` otherwise. + [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md [throttling]: throttling.md diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index f473128e..84acd797 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -30,6 +30,10 @@ you should use the `api_settings` object. For example. The `api_settings` object will check for any user-defined settings, and otherwise fallback to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal. +--- + +# API Reference + ## DEFAULT_RENDERERS A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 3fb95ae3..22e34187 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -63,6 +63,8 @@ Or, if you're using the `@api_view` decorator with function based views. } return Response(content) +--- + # API Reference ## AnonRateThrottle @@ -144,6 +146,8 @@ For example, given the following views... User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day. +--- + # Custom throttles To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise. diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index cbfa2e28..77349252 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -33,8 +33,8 @@ For example: """ Return a list of all users. """ - users = [user.username for user in User.objects.all()] - return Response(users) + usernames = [user.username for user in User.objects.all()] + return Response(usernames) ## API policy attributes -- cgit v1.2.3 From 99d48f90030d174ef80498b48f56af6489865f0d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:07:56 +0100 Subject: Drop .parse_string_or_stream() - keep API minimal. --- docs/api-guide/parsers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 4f145ba3..a950c0e0 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -91,11 +91,11 @@ You will typically want to use both `FormParser` and `MultiPartParser` together # Custom parsers -To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse_stream(self, stream, parser_context)` method. +To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse(self, stream, parser_context)` method. The method should return the data that will be used to populate the `request.DATA` property. -The arguments passed to `.parse_stream()` are: +The arguments passed to `.parse()` are: ### stream @@ -116,7 +116,7 @@ The following is an example plaintext parser that will populate the `request.DAT media_type = 'text/plain' - def parse_stream(self, stream, parser_context=None): + def parse(self, stream, parser_context=None): """ Simply return a string representing the body of the request. """ @@ -124,7 +124,7 @@ The following is an example plaintext parser that will populate the `request.DAT ## Uploading file content -If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse_stream()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property. +If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property. For example: @@ -133,7 +133,7 @@ For example: A naive raw file upload parser. """ - def parse_stream(self, stream, parser_context): + def parse(self, stream, parser_context): content = stream.read() name = 'example.dat' content_type = 'application/octet-stream' -- cgit v1.2.3 From 4231995fbd80e45991975ab81d9e570a9f4b72d0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:19:59 +0100 Subject: parser_context includes `view`, `request`, `args`, `kwargs`. (Not `meta` and `upload_handlers`) Consistency with renderer API. --- docs/api-guide/parsers.md | 4 +++- docs/api-guide/renderers.md | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index a950c0e0..70abad9b 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -103,7 +103,9 @@ A stream-like object representing the body of the request. ### parser_context -If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. By default it includes the keys `'upload_handlers'` and `'meta'`, which contain the values of the `request.upload_handlers` and `request.meta` properties. +Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. + +By default this will include the following keys: `view`, `request`, `args`, `kwargs`. ## Example diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index c8addb32..24cca181 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -162,11 +162,14 @@ The request data, as set by the `Response()` instantiation. ### `media_type=None` -Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`. +Optional. If provided, this is the accepted media type, as determined by the content negotiation stage. + +Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`. ### `renderer_context=None` Optional. If provided, this is a dictionary of contextual information provided by the view. + By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`. ## Example -- cgit v1.2.3 From fb56f215ae50da0aebe99e05036ece259fd3e6f1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:39:07 +0100 Subject: Added `media_type` to `.parse()` - Consistency with renderer API. --- docs/api-guide/parsers.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 70abad9b..18a5872c 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -101,6 +101,12 @@ The arguments passed to `.parse()` are: A stream-like object representing the body of the request. +### media_type + +Optional. If provided, this is the media type of the incoming request. + +Depending on the request's `Content-Type:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"text/plain; charset=utf-8"`. + ### parser_context Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content. @@ -118,7 +124,7 @@ The following is an example plaintext parser that will populate the `request.DAT media_type = 'text/plain' - def parse(self, stream, parser_context=None): + def parse(self, stream, media_type=None, parser_context=None): """ Simply return a string representing the body of the request. """ @@ -135,7 +141,7 @@ For example: A naive raw file upload parser. """ - def parse(self, stream, parser_context): + def parse(self, stream, media_type=None, parser_context=None): content = stream.read() name = 'example.dat' content_type = 'application/octet-stream' -- cgit v1.2.3 From e126b615420fed12af58675cb4bb52e749b006bd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 22:58:18 +0100 Subject: Negotiation API finalized. .select_renderers and .select_parsers --- docs/api-guide/content-negotiation.md | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index ad98de3b..b95091c5 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -7,3 +7,9 @@ > — [RFC 2616][cite], Fielding et al. [cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html + +**TODO**: Describe content negotiation style used by REST framework. + +## Custom content negotiation + +It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme, override `BaseContentNegotiation`, and implement the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` \ No newline at end of file -- cgit v1.2.3 From fed235dd0135c3eb98bb218a51f01ace5ddd3782 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 17 Oct 2012 23:09:11 +0100 Subject: Make settings consistent with corrosponding view attributes --- docs/api-guide/authentication.md | 4 ++-- docs/api-guide/parsers.md | 4 ++-- docs/api-guide/permissions.md | 4 ++-- docs/api-guide/renderers.md | 4 ++-- docs/api-guide/requests.md | 4 ++-- docs/api-guide/settings.md | 20 ++++++++++---------- docs/api-guide/throttling.md | 8 ++++---- 7 files changed, 24 insertions(+), 24 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 71f48163..5e5ee4ed 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -26,10 +26,10 @@ The value of `request.user` and `request.auth` for unauthenticated requests can ## Setting the authentication policy -The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION` setting. For example. +The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION_CLASSES` setting. For example. REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION': ( + 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.UserBasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 18a5872c..f7a62d3d 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -16,10 +16,10 @@ The set of valid parsers for a view is always defined as a list of classes. Whe ## Setting the parsers -The default set of parsers may be set globally, using the `DEFAULT_PARSERS` setting. For example, the following settings would allow requests with `YAML` content. +The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSES` setting. For example, the following settings would allow requests with `YAML` content. REST_FRAMEWORK = { - 'DEFAULT_PARSERS': ( + 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.YAMLParser', ) } diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index b25b52be..249f3938 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -25,10 +25,10 @@ Object level permissions are run by REST framework's generic views when `.get_ob ## Setting the permission policy -The default permission policy may be set globally, using the `DEFAULT_PERMISSIONS` setting. For example. +The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example. REST_FRAMEWORK = { - 'DEFAULT_PERMISSIONS': ( + 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) } diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 24cca181..b3b8d5bc 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -18,10 +18,10 @@ For more information see the documentation on [content negotation][conneg]. ## Setting the renderers -The default set of renderers may be set globally, using the `DEFAULT_RENDERERS` setting. For example, the following settings would use `YAML` as the main media type and also include the self describing API. +The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CLASSES` setting. For example, the following settings would use `YAML` as the main media type and also include the self describing API. REST_FRAMEWORK = { - 'DEFAULT_RENDERERS': ( + 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.YAMLRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ) diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 439c97bc..2770c6dd 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -37,7 +37,7 @@ For clarity inside your code, we recommend using `request.QUERY_PARAMS` instead ## .parsers -The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSERS` setting. +The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSER_CLASSES` setting. You won't typically need to access this property. @@ -125,4 +125,4 @@ Note that due to implementation reasons the `Request` class does not inherit fro [cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion [parsers documentation]: parsers.md [authentication documentation]: authentication.md -[browser enhancements documentation]: ../topics/browser-enhancements.md \ No newline at end of file +[browser enhancements documentation]: ../topics/browser-enhancements.md diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 84acd797..21efc853 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -11,10 +11,10 @@ Configuration for REST framework is all namespaced inside a single Django settin For example your project's `settings.py` file might include something like this: REST_FRAMEWORK = { - 'DEFAULT_RENDERERS': ( + 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.YAMLRenderer', ) - 'DEFAULT_PARSERS': ( + 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.YAMLParser', ) } @@ -26,7 +26,7 @@ you should use the `api_settings` object. For example. from rest_framework.settings import api_settings - print api_settings.DEFAULT_AUTHENTICATION + print api_settings.DEFAULT_AUTHENTICATION_CLASSES The `api_settings` object will check for any user-defined settings, and otherwise fallback to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal. @@ -34,7 +34,7 @@ The `api_settings` object will check for any user-defined settings, and otherwis # API Reference -## DEFAULT_RENDERERS +## DEFAULT_RENDERER_CLASSES A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object. @@ -46,7 +46,7 @@ Default: 'rest_framework.renderers.TemplateHTMLRenderer' ) -## DEFAULT_PARSERS +## DEFAULT_PARSER_CLASSES A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.DATA` property. @@ -57,7 +57,7 @@ Default: 'rest_framework.parsers.FormParser' ) -## DEFAULT_AUTHENTICATION +## DEFAULT_AUTHENTICATION_CLASSES A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the `request.user` or `request.auth` properties. @@ -68,25 +68,25 @@ Default: 'rest_framework.authentication.UserBasicAuthentication' ) -## DEFAULT_PERMISSIONS +## DEFAULT_PERMISSION_CLASSES A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. Default: `()` -## DEFAULT_THROTTLES +## DEFAULT_THROTTLE_CLASSES A list or tuple of throttle classes, that determines the default set of throttles checked at the start of a view. Default: `()` -## DEFAULT_MODEL_SERIALIZER +## DEFAULT_MODEL_SERIALIZER_CLASS **TODO** Default: `rest_framework.serializers.ModelSerializer` -## DEFAULT_PAGINATION_SERIALIZER +## DEFAULT_PAGINATION_SERIALIZER_CLASS **TODO** diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 22e34187..435b20ce 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -27,10 +27,10 @@ If any throttle check fails an `exceptions.Throttled` exception will be raised, ## Setting the throttling policy -The default throttling policy may be set globally, using the `DEFAULT_THROTTLES` and `DEFAULT_THROTTLE_RATES` settings. For example. +The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_CLASSES` and `DEFAULT_THROTTLE_RATES` settings. For example. REST_FRAMEWORK = { - 'DEFAULT_THROTTLES': ( + 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttles.AnonThrottle', 'rest_framework.throttles.UserThrottle', ) @@ -100,7 +100,7 @@ For example, multiple user throttle rates could be implemented by using the foll ...and the following settings. REST_FRAMEWORK = { - 'DEFAULT_THROTTLES': ( + 'DEFAULT_THROTTLE_CLASSES': ( 'example.throttles.BurstRateThrottle', 'example.throttles.SustainedRateThrottle', ) @@ -135,7 +135,7 @@ For example, given the following views... ...and the following settings. REST_FRAMEWORK = { - 'DEFAULT_THROTTLES': ( + 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttles.ScopedRateThrottle', ) 'DEFAULT_THROTTLE_RATES': { -- cgit v1.2.3 From e8f542aac88677cd95c473d56511cadbc0c67813 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 18 Oct 2012 09:19:38 +0100 Subject: Minor docs fix --- docs/api-guide/parsers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index 18a5872c..0985b2ca 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -91,7 +91,7 @@ You will typically want to use both `FormParser` and `MultiPartParser` together # Custom parsers -To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse(self, stream, parser_context)` method. +To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse(self, stream, media_type, parser_context)` method. The method should return the data that will be used to populate the `request.DATA` property. @@ -103,7 +103,7 @@ A stream-like object representing the body of the request. ### media_type -Optional. If provided, this is the media type of the incoming request. +Optional. If provided, this is the media type of the incoming request content. Depending on the request's `Content-Type:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"text/plain; charset=utf-8"`. -- cgit v1.2.3 From efabd2bb1b762fbdee2b48fa3a6ccb8f23c7e8dc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 19 Oct 2012 19:59:46 +0100 Subject: docs, docs, docs, docs, docs, docs, docs --- docs/api-guide/content-negotiation.md | 57 +++++++++++++++++++++++++++++++++-- docs/api-guide/format-suffixes.md | 52 +++++++++++++++++++++++++++++++- docs/api-guide/pagination.md | 6 +++- 3 files changed, 110 insertions(+), 5 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index b95091c5..78dc0747 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -8,8 +8,59 @@ [cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html -**TODO**: Describe content negotiation style used by REST framework. +Content negotiation is the process of selecting one of multiple possible representations to return to a client, based on client or server preferences. -## Custom content negotiation +## Determining the accepted renderer -It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme, override `BaseContentNegotiation`, and implement the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` \ No newline at end of file +REST framework uses a simple style of content negotition to determine which media type should be returned to a client, based on the available renderers, the priorities of each of those renderers, and the client's `Accept:` header. The style used is partly client-driven, and partly server-driven. + +1. More specific media types are given preference to less specific media types. +2. If multiple media types have the same specificity, then preference is given to based on the ordering of the renderers configured for the given view. + +For example, given the following `Accept` header: + + application/json; indent=4, application/json, application/yaml, text/html, */* + +The priorities for each of the given media types would be: + +* `application/json; indent=4` +* `application/json`, `application/yaml` and `text/html` +* `*/*` + +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] + +--- + +**Note**: "q" values are not taken into account by REST framework when determining preference. The use of "q" values negatively impacts caching, and in the author's opinion they are an unnecessary and overcomplicated approach to content negotiation. + +This is a valid approach as the HTTP spec deliberately underspecifies how a server should weight server-based preferences against client-based preferences. + +--- + +# Custom content negotiation + +It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override `BaseContentNegotiation`. + +REST framework's content negotiation classes handle selection of both the approprate parser for the requesr, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods. + +## Example + +The following is a custom content negotiation class which ignores the client +request when selecting the appropriate parser or renderer. + + class IgnoreClientContentNegotiation(BaseContentNegotiation): + def select_parser(self, request, parsers): + """ + 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. + """ + return renderers[0] + +[accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html \ No newline at end of file diff --git a/docs/api-guide/format-suffixes.md b/docs/api-guide/format-suffixes.md index 7d72d9f8..6d5feba4 100644 --- a/docs/api-guide/format-suffixes.md +++ b/docs/api-guide/format-suffixes.md @@ -7,5 +7,55 @@ used all the time. > > — Roy Fielding, [REST discuss mailing list][cite] -[cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857 +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. + +## format_suffix_patterns + +**Signature**: format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None) + +Returns a URL pattern list which includes format suffix patterns appended to each of the URL patterns provided. + +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. + +Example: + + from rest_framework.urlpatterns import format_suffix_patterns + + urlpatterns = patterns('blog.views', + url(r'^/$', 'api_root'), + url(r'^comment/$', 'comment_root'), + url(r'^comment/(?P[0-9]+)/$', 'comment_instance') + ) + + 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. + @api_view(('GET',)) + def api_root(request, format=None): + # do stuff... + +The name of the kwarg used may be modified by using the `FORMAT_SUFFIX_KWARG` setting. + +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: + +“That's why I always prefer extensions. Neither choice has anything to do with REST.” — Roy Fielding, [REST discuss mailing list][cite2] + +The quote does not mention Accept headers, but it does make it clear that format suffixes should be considered an acceptable pattern. + +[cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857 +[cite2]: http://tech.groups.yahoo.com/group/rest-discuss/message/14844 \ No newline at end of file diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index e416de02..597baba4 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -100,12 +100,16 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods. -## Custom pagination serializers +--- + +# Custom pagination serializers To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return. You can also override the name used for the object list field, by setting the `results_field` attribute, which defaults to `'results'`. +## Example + For example, to nest a pair of links labelled 'prev' and 'next', and set the name for the results field to 'objects', you might use something like this. class LinksSerializer(serializers.Serializer): -- cgit v1.2.3 From 71a93930fd4df7a1f5f92c67633b813a26a5e938 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sun, 21 Oct 2012 16:34:07 +0200 Subject: Fixing spelling errors. --- docs/api-guide/authentication.md | 2 +- docs/api-guide/content-negotiation.md | 8 ++++---- docs/api-guide/exceptions.md | 4 ++-- docs/api-guide/fields.md | 8 ++++---- docs/api-guide/parsers.md | 4 ++-- docs/api-guide/permissions.md | 2 +- docs/api-guide/renderers.md | 4 ++-- docs/api-guide/requests.md | 12 ++++++------ docs/api-guide/responses.md | 2 +- docs/api-guide/reverse.md | 2 +- docs/api-guide/serializers.md | 4 ++-- docs/api-guide/throttling.md | 6 +++--- 12 files changed, 29 insertions(+), 29 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 5e5ee4ed..7bad4867 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -86,7 +86,7 @@ You'll also need to create tokens for your users. token = Token.objects.create(user=...) print token.key -For clients to authenticate, the token key should be included in the `Authorization` HTTP header. The key should be prefixed by the string literal "Token", with whitespace seperating the two strings. For example: +For clients to authenticate, the token key should be included in the `Authorization` HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example: Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md index 78dc0747..10288c94 100644 --- a/docs/api-guide/content-negotiation.md +++ b/docs/api-guide/content-negotiation.md @@ -12,7 +12,7 @@ Content negotiation is the process of selecting one of multiple possible represe ## Determining the accepted renderer -REST framework uses a simple style of content negotition to determine which media type should be returned to a client, based on the available renderers, the priorities of each of those renderers, and the client's `Accept:` header. The style used is partly client-driven, and partly server-driven. +REST framework uses a simple style of content negotiation to determine which media type should be returned to a client, based on the available renderers, the priorities of each of those renderers, and the client's `Accept:` header. The style used is partly client-driven, and partly server-driven. 1. More specific media types are given preference to less specific media types. 2. If multiple media types have the same specificity, then preference is given to based on the ordering of the renderers configured for the given view. @@ -41,9 +41,9 @@ This is a valid approach as the HTTP spec deliberately underspecifies how a serv # Custom content negotiation -It's unlikley that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override `BaseContentNegotiation`. +It's unlikely that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override `BaseContentNegotiation`. -REST framework's content negotiation classes handle selection of both the approprate parser for the requesr, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods. +REST framework's content negotiation classes handle selection of both the appropriate parser for the request, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods. ## Example @@ -63,4 +63,4 @@ request when selecting the appropriate parser or renderer. """ return renderers[0] -[accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html \ No newline at end of file +[accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index 33cf1ca8..ba57fde8 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -25,7 +25,7 @@ For example, the following request: DELETE http://api.example.com/foo/bar HTTP/1.1 Accept: application/json -Might recieve an error response indicating that the `DELETE` method is not allowed on that resource: +Might receive an error response indicating that the `DELETE` method is not allowed on that resource: HTTP/1.1 405 Method Not Allowed Content-Type: application/json; charset=utf-8 @@ -85,4 +85,4 @@ Raised when an incoming request fails the throttling checks. By default this exception results in a response with the HTTP status code "429 Too Many Requests". -[cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html \ No newline at end of file +[cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index dc9ab045..b3cf186c 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -42,7 +42,7 @@ A serializer definition that looked like this: class Meta: fields = ('url', 'owner', 'name', 'expired') -Would produced output similar to: +Would produce output similar to: { 'url': 'http://example.com/api/accounts/3/', @@ -51,7 +51,7 @@ Would produced output similar to: 'expired': True } -Be default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when neccesary. +By default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when necessary. You can customize this behaviour by overriding the `.to_native(self, value)` method. @@ -84,7 +84,7 @@ or `django.db.models.fields.TextField`. ## EmailField -A text representation, validates the text to be a valid e-mail adress. Corresponds to `django.db.models.fields.EmailField` +A text representation, validates the text to be a valid e-mail address. Corresponds to `django.db.models.fields.EmailField` ## DateField @@ -165,7 +165,7 @@ And a model serializer defined like this: model = Bookmark exclude = ('id',) -The an example output format for a Bookmark instance would be: +Then an example output format for a Bookmark instance would be: { 'tags': [u'django', u'python'], diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index c35dfd05..ac904720 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -8,7 +8,7 @@ sending more complex data than simple forms > > — Malcom Tredinnick, [Django developers group][cite] -REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexiblity to design the media types that your API accepts. +REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts. ## How the parser is determined @@ -65,7 +65,7 @@ Parses `YAML` request content. Parses REST framework's default style of `XML` request content. -Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`. +Note that the `XML` markup language is typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`. If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type. diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 249f3938..0b7b32e9 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -6,7 +6,7 @@ > > — [Apple Developer Documentation][cite] -Together with [authentication] and [throttling], permissions determine wheter a request should be granted or denied access. +Together with [authentication] and [throttling], permissions determine whether a request should be granted or denied access. Permission checks are always run at the very start of the view, before any other code is allowed to proceed. Permission checks will typically use the authentication information in the `request.user` and `request.auth` properties to determine if the incoming request should be permitted. diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b3b8d5bc..b6db376c 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -6,7 +6,7 @@ > > — [Django documentation][cite] -REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexiblity to design your own media types. +REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types. There is also support for defining your own custom renderers, which gives you the flexibility to design your own media types. ## How the renderer is determined @@ -229,7 +229,7 @@ For example: ## Designing your media types -For the purposes of many Web APIs, simple `JSON` responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and [HATEOAS] you'll neeed to consider the design and usage of your media types in more detail. +For the purposes of many Web APIs, simple `JSON` responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and [HATEOAS] you'll need to consider the design and usage of your media types in more detail. In [the words of Roy Fielding][quote], "A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.". diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md index 2770c6dd..72932f5d 100644 --- a/docs/api-guide/requests.md +++ b/docs/api-guide/requests.md @@ -25,19 +25,19 @@ For more details see the [parsers documentation]. ## .FILES -`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing that is used for `request.DATA`. +`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing is used for `request.DATA`. For more details see the [parsers documentation]. ## .QUERY_PARAMS -`request.QUERY_PARAMS` is a more correcly named synonym for `request.GET`. +`request.QUERY_PARAMS` is a more correctly named synonym for `request.GET`. For clarity inside your code, we recommend using `request.QUERY_PARAMS` instead of the usual `request.GET`, as *any* HTTP method type may include query parameters. ## .parsers -The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSER_CLASSES` setting. +The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSER_CLASSES` setting. You won't typically need to access this property. @@ -51,7 +51,7 @@ If a client sends a request with a content-type that cannot be parsed then a `Un # Authentication -REST framework provides flexbile, per-request authentication, that gives you the abilty to: +REST framework provides flexible, per-request authentication, that gives you the ability to: * Use different authentication policies for different parts of your API. * Support the use of multiple authentication policies. @@ -75,7 +75,7 @@ For more details see the [authentication documentation]. ## .authenticators -The `APIView` class or `@api_view` decorator will ensure that this property is automatically to a list of `Authentication` instances, based on the `authentication_classes` set on the view or based on the `DEFAULT_AUTHENTICATORS` setting. +The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Authentication` instances, based on the `authentication_classes` set on the view or based on the `DEFAULT_AUTHENTICATORS` setting. You won't typically need to access this property. @@ -83,7 +83,7 @@ You won't typically need to access this property. # Browser enhancements -REST framework supports a few browser enhancments such as browser-based `PUT` and `DELETE` forms. +REST framework supports a few browser enhancements such as browser-based `PUT` and `DELETE` forms. ## .method diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md index 395decda..794f9377 100644 --- a/docs/api-guide/responses.md +++ b/docs/api-guide/responses.md @@ -86,7 +86,7 @@ The `Response` class extends `SimpleTemplateResponse`, and all the usual attribu **Signature:** `.render()` -As with any other `TemplateResponse`, this methd is called to render the serialized data of the response into the final response content. When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance. +As with any other `TemplateResponse`, this method is called to render the serialized data of the response into the final response content. When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance. You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle. diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 12346eb4..19930dc3 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -6,7 +6,7 @@ > > — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite] -As a rule, it's probably better practice to return absolute URIs from you Web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. +As a rule, it's probably better practice to return absolute URIs from your Web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`. The advantages of doing so are: diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 47958fe3..c10a3f44 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -175,7 +175,7 @@ You can add extra fields to a `ModelSerializer` or override the default fields b class Meta: model = Account -Extra fields can corrospond to any property or callable on the model. +Extra fields can correspond to any property or callable on the model. ## Relational fields @@ -187,7 +187,7 @@ The `PrimaryKeyRelatedField` and `HyperlinkedRelatedField` fields provide altern The `ModelSerializer` class can itself be used as a field, in order to serialize relationships using nested representations. -The `RelatedField` class may be subclassed to create a custom represenation of a relationship. The subclass should override `.to_native()`, and optionally `.from_native()` if deserialization is supported. +The `RelatedField` class may be subclassed to create a custom representation of a relationship. The subclass should override `.to_native()`, and optionally `.from_native()` if deserialization is supported. All the relational fields may be used for any relationship or reverse relationship on a model. diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 435b20ce..d54433b1 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -80,7 +80,7 @@ The allowed request rate is determined from one of the following (in order of pr ## UserRateThrottle -The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticted requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. +The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against. The allowed request rate is determined from one of the following (in order of preference). @@ -114,7 +114,7 @@ For example, multiple user throttle rates could be implemented by using the foll ## ScopedRateThrottle -The `ScopedThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unqiue user id or IP address. +The `ScopedThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unique user id or IP address. The allowed request rate is determined by the `DEFAULT_THROTTLE_RATES` setting using a key from the request "scope". @@ -152,6 +152,6 @@ User requests to either `ContactListView` or `ContactDetailView` would be restri To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise. -Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recomended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.allow_request()` has previously returned `False`. +Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recommended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.allow_request()` has previously returned `False`. [permissions]: permissions.md -- cgit v1.2.3 From 65d4970bf71d31669f10dc0cecd4a2a00acd7393 Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Sun, 21 Oct 2012 16:34:50 +0200 Subject: Changed IsAdmin -> IsAdminUser in example --- docs/api-guide/views.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 77349252..e3fbadb2 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -27,7 +27,7 @@ For example: * Only admin users are able to access this view. """ authentication_classes = (authentication.TokenAuthentication,) - permission_classes = (permissions.IsAdmin,) + permission_classes = (permissions.IsAdminUser,) def get(self, request, format=None): """ @@ -123,4 +123,4 @@ REST framework also gives you to work with regular function based views... **[TODO]** [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 \ No newline at end of file +[cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html -- cgit v1.2.3 From 13d0a829390105aa53602be7dc713092ead5a66c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 21 Oct 2012 17:40:49 +0100 Subject: Minor docs tweaks --- docs/api-guide/fields.md | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index dc9ab045..234c65ad 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -73,34 +73,52 @@ These fields represent basic datatypes, and support both reading and writing val ## BooleanField -A Boolean representation, corresponds to `django.db.models.fields.BooleanField`. +A Boolean representation. + +Corresponds to `django.db.models.fields.BooleanField`. ## CharField -A text representation, optionally validates the text to be shorter than `max_length` and longer than `min_length`, corresponds to `django.db.models.fields.CharField` +A text representation, optionally validates the text to be shorter than `max_length` and longer than `min_length`. + +Corresponds to `django.db.models.fields.CharField` or `django.db.models.fields.TextField`. -**Signature:** `CharField([max_length=[, min_length=]])` +**Signature:** `CharField(max_length=None, min_length=None)` + +## ChoiceField + +A field that can accept on of a limited set of choices. ## EmailField -A text representation, validates the text to be a valid e-mail adress. Corresponds to `django.db.models.fields.EmailField` +A text representation, validates the text to be a valid e-mail adress. + +Corresponds to `django.db.models.fields.EmailField` ## DateField -A date representation. Corresponds to `django.db.models.fields.DateField` +A date representation. + +Corresponds to `django.db.models.fields.DateField` ## DateTimeField -A date and time representation. Corresponds to `django.db.models.fields.DateTimeField` +A date and time representation. + +Corresponds to `django.db.models.fields.DateTimeField` ## IntegerField -An integer representation. Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` and `django.db.models.fields.PositiveSmallIntegerField` +An integer representation. + +Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` and `django.db.models.fields.PositiveSmallIntegerField` ## FloatField -A floating point representation. Corresponds to `django.db.models.fields.FloatField`. +A floating point representation. + +Corresponds to `django.db.models.fields.FloatField`. --- -- cgit v1.2.3 From aba0172f5c988af145113678fe3d4f411111d4ff Mon Sep 17 00:00:00 2001 From: Marko Tibold Date: Mon, 22 Oct 2012 21:31:15 +0300 Subject: Update docs/api-guide/fields.md Fix typo.--- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 189ed76f..7e117df7 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -88,7 +88,7 @@ or `django.db.models.fields.TextField`. ## ChoiceField -A field that can accept on of a limited set of choices. +A field that can accept a value out of a limited set of choices. ## EmailField -- cgit v1.2.3 From 51fae73f3d565e2702c72ff9841cc072d6490804 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Wed, 24 Oct 2012 09:28:10 +0100 Subject: Implement per-field validation on Serializers --- docs/api-guide/serializers.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index c10a3f44..e1e12e74 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -78,6 +78,23 @@ When deserializing data, you always need to call `is_valid()` before attempting **TODO: Describe validation in more depth** +## Custom field validation + +Like Django forms, you can specify custom field-level validation by adding `clean_()` methods to your `Serializer` subclass. This method takes a dictionary of deserialized data as a first argument, and the field name in that data as a second argument (which will be either the name of the field or the value of the `source` argument, if one was provided.) It should either return the data dictionary or raise a `ValidationError`. For example: + + class BlogPostSerializer(Serializer): + title = serializers.CharField(max_length=100) + content = serializers.CharField() + + def clean_title(self, data, source): + """ + Check that the blog post is about Django + """ + value = data[source] + if "Django" not in value: + raise ValidationError("Blog post is not about Django") + return data + ## Dealing with nested objects The previous example is fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, -- cgit v1.2.3 From 388a807f64f60d84556288e2ade4f0fe57a8e66b Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Wed, 24 Oct 2012 11:27:01 +0100 Subject: Switch from clean_ to validate_, clarify documentation --- docs/api-guide/serializers.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index e1e12e74..9011d31f 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -80,19 +80,21 @@ When deserializing data, you always need to call `is_valid()` before attempting ## Custom field validation -Like Django forms, you can specify custom field-level validation by adding `clean_()` methods to your `Serializer` subclass. This method takes a dictionary of deserialized data as a first argument, and the field name in that data as a second argument (which will be either the name of the field or the value of the `source` argument, if one was provided.) It should either return the data dictionary or raise a `ValidationError`. For example: +You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized data as a first argument, and the field name in that data as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the data dictionary or raise a `ValidationError`. For example: - class BlogPostSerializer(Serializer): + from rest_framework import serializers + + class BlogPostSerializer(serializers.Serializer): title = serializers.CharField(max_length=100) content = serializers.CharField() - def clean_title(self, data, source): + def validate_title(self, data, source): """ Check that the blog post is about Django """ value = data[source] if "Django" not in value: - raise ValidationError("Blog post is not about Django") + raise serializers.ValidationError("Blog post is not about Django") return data ## Dealing with nested objects -- cgit v1.2.3 From ac2d39892d6b3fbbe5cd53b9ef83367249ba4880 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Wed, 24 Oct 2012 11:39:17 +0100 Subject: Add cross-field validate method --- docs/api-guide/serializers.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 9011d31f..40f8a170 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -76,9 +76,7 @@ Deserialization is similar. First we parse a stream into python native datatype When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. -**TODO: Describe validation in more depth** - -## Custom field validation +### Field-level validation You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized data as a first argument, and the field name in that data as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the data dictionary or raise a `ValidationError`. For example: @@ -97,6 +95,10 @@ You can specify custom field-level validation by adding `validate_()` raise serializers.ValidationError("Blog post is not about Django") return data +### Final cross-field validation + +To do any other validation that requires access to multiple fields, add a method called `validate` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. + ## Dealing with nested objects The previous example is fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, -- cgit v1.2.3 From d60d598e0255fb3d55a1213d1025447d83523658 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Wed, 24 Oct 2012 11:43:30 +0100 Subject: Clean up internal names and documentation --- docs/api-guide/serializers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 40f8a170..50505d30 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -78,7 +78,7 @@ When deserializing data, you always need to call `is_valid()` before attempting ### Field-level validation -You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized data as a first argument, and the field name in that data as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the data dictionary or raise a `ValidationError`. For example: +You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the attrs dictionary or raise a `ValidationError`. For example: from rest_framework import serializers @@ -86,14 +86,14 @@ You can specify custom field-level validation by adding `validate_()` title = serializers.CharField(max_length=100) content = serializers.CharField() - def validate_title(self, data, source): + def validate_title(self, attrs, source): """ Check that the blog post is about Django """ - value = data[source] + value = attrs[source] if "Django" not in value: raise serializers.ValidationError("Blog post is not about Django") - return data + return attrs ### Final cross-field validation -- cgit v1.2.3 From d97c71212423dd79abafc5aab21afc4eccfc4529 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 24 Oct 2012 20:49:09 +0200 Subject: Fix typo reported by @diviei --- docs/api-guide/serializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 50505d30..057827d3 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -254,7 +254,7 @@ When serializing objects using a nested representation any occurances of recursi def get_related_field(self, model_field, to_many=False): queryset = model_field.rel.to._default_manager if to_many: - return return serializers.ManyRelatedField(queryset=queryset) + return serializers.ManyRelatedField(queryset=queryset) return serializers.RelatedField(queryset=queryset) def get_field(self, model_field): -- cgit v1.2.3 From 1ceca69e5fa64344f1a039526fb653bf6bbd8a9d Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Thu, 25 Oct 2012 13:50:48 +0100 Subject: Update generic view documentation --- docs/api-guide/generic-views.md | 62 ++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 19 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 8bf7a7e2..7ca0f905 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -49,21 +49,21 @@ For very simple cases you might want to pass through any class attributes using The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior. -## ListAPIView +## CreateAPIView -Used for **read-only** endpoints to represent a **collection of model instances**. +Used for **create-only** endpoints. -Provides a `get` method handler. +Provides `post` method handlers. -Extends: [MultipleObjectBaseAPIView], [ListModelMixin] +Extends: [GenericAPIView], [CreateModelMixin] -## ListCreateAPIView +## ListAPIView -Used for **read-write** endpoints to represent a **collection of model instances**. +Used for **read-only** endpoints to represent a **collection of model instances**. -Provides `get` and `post` method handlers. +Provides a `get` method handler. -Extends: [MultipleObjectBaseAPIView], [ListModelMixin], [CreateModelMixin] +Extends: [MultipleObjectAPIView], [ListModelMixin] ## RetrieveAPIView @@ -71,7 +71,31 @@ Used for **read-only** endpoints to represent a **single model instance**. Provides a `get` method handler. -Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin] +Extends: [SingleObjectAPIView], [RetrieveModelMixin] + +## DestroyAPIView + +Used for **delete-only** endpoints for a **single model instance**. + +Provides a `delete` method handler. + +Extends: [SingleObjectAPIView], [DestroyModelMixin] + +## UpdateAPIView + +Used for **update-only** endpoints for a **single model instance**. + +Provides a `put` method handler. + +Extends: [SingleObjectAPIView], [UpdateModelMixin] + +## ListCreateAPIView + +Used for **read-write** endpoints to represent a **collection of model instances**. + +Provides `get` and `post` method handlers. + +Extends: [MultipleObjectAPIView], [ListModelMixin], [CreateModelMixin] ## RetrieveDestroyAPIView @@ -79,15 +103,15 @@ Used for **read or delete** endpoints to represent a **single model instance**. Provides `get` and `delete` method handlers. -Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin], [DestroyModelMixin] +Extends: [SingleObjectAPIView], [RetrieveModelMixin], [DestroyModelMixin] ## RetrieveUpdateDestroyAPIView -Used for **read-write** endpoints to represent a **single model instance**. +Used for **read-write-delete** endpoints to represent a **single model instance**. Provides `get`, `put` and `delete` method handlers. -Extends: [SingleObjectBaseAPIView], [RetrieveModelMixin], [UpdateModelMixin], [DestroyModelMixin] +Extends: [SingleObjectAPIView], [RetrieveModelMixin], [UpdateModelMixin], [DestroyModelMixin] --- @@ -121,7 +145,7 @@ The mixin classes provide the actions that are used to provide the basic view be Provides a `.list(request, *args, **kwargs)` method, that implements listing a queryset. -Should be mixed in with [MultipleObjectBaseAPIView]. +Should be mixed in with [MultipleObjectAPIView]. ## CreateModelMixin @@ -133,19 +157,19 @@ Should be mixed in with any [BaseAPIView]. Provides a `.retrieve(request, *args, **kwargs)` method, that implements returning an existing model instance in a response. -Should be mixed in with [SingleObjectBaseAPIView]. +Should be mixed in with [SingleObjectAPIView]. ## UpdateModelMixin Provides a `.update(request, *args, **kwargs)` method, that implements updating and saving an existing model instance. -Should be mixed in with [SingleObjectBaseAPIView]. +Should be mixed in with [SingleObjectAPIView]. ## DestroyModelMixin Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance. -Should be mixed in with [SingleObjectBaseAPIView]. +Should be mixed in with [SingleObjectAPIView]. [cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views [MultipleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/ @@ -153,9 +177,9 @@ Should be mixed in with [SingleObjectBaseAPIView]. [multiple-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.list/MultipleObjectMixin/ [single-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.detail/SingleObjectMixin/ -[BaseAPIView]: #baseapiview -[SingleObjectBaseAPIView]: #singleobjectbaseapiview -[MultipleObjectBaseAPIView]: #multipleobjectbaseapiview +[GenericAPIView]: #genericapiview +[SingleObjectAPIView]: #singleobjectapiview +[MultipleObjectAPIView]: #multipleobjectapiview [ListModelMixin]: #listmodelmixin [CreateModelMixin]: #createmodelmixin [RetrieveModelMixin]: #retrievemodelmixin -- cgit v1.2.3 From 41d27b1a307132fea42fb104a8d7d266ea9e90d1 Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Thu, 25 Oct 2012 14:22:36 +0100 Subject: Fix section headings in generic views docs --- docs/api-guide/generic-views.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 7ca0f905..97b4441f 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -119,17 +119,17 @@ Extends: [SingleObjectAPIView], [RetrieveModelMixin], [UpdateModelMixin], [Destr Each of the generic views provided is built by combining one of the base views below, with one or more mixin classes. -## BaseAPIView +## GenericAPIView Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets. -## MultipleObjectBaseAPIView +## MultipleObjectAPIView Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin]. **See also:** ccbv.co.uk documentation for [MultipleObjectMixin][multiple-object-mixin-classy]. -## SingleObjectBaseAPIView +## SingleObjectAPIView Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin]. @@ -151,7 +151,7 @@ Should be mixed in with [MultipleObjectAPIView]. Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance. -Should be mixed in with any [BaseAPIView]. +Should be mixed in with any [GenericAPIView]. ## RetrieveModelMixin -- cgit v1.2.3 From d4063eb02e31401252ca15b3aae50ed951d7869f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 26 Oct 2012 12:46:31 +0100 Subject: Fix incorrect method signature in docs --- docs/api-guide/generic-views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 97b4441f..360ef1a2 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -30,7 +30,7 @@ For more complex cases you might also want to override various methods on the vi serializer_class = UserSerializer permission_classes = (IsAdminUser,) - def get_paginate_by(self): + def get_paginate_by(self, queryset): """ Use smaller pagination for HTML representations. """ -- cgit v1.2.3 From c221bc6f6f239d958e89523c00686da595c68578 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 26 Oct 2012 17:30:45 +0200 Subject: Use context dict in HTMLRenderer --- docs/api-guide/renderers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b6db376c..b5e2fe8f 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -130,7 +130,7 @@ An example of a view that uses `HTMLRenderer`: def get(self, request, *args, **kwargs) self.object = self.get_object() - return Response(self.object, template_name='user_detail.html') + return Response({'user': self.object}, template_name='user_detail.html') You can use `HTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. -- cgit v1.2.3 From 5180b725655e84712b103fbc280353d25048068b Mon Sep 17 00:00:00 2001 From: Jamie Matthews Date: Sat, 27 Oct 2012 13:53:07 +0100 Subject: Documentation for function-based view decorators --- docs/api-guide/views.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index e3fbadb2..c51860b5 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -118,9 +118,48 @@ You won't typically need to override this method. > > — [Nick Coghlan][cite2] -REST framework also gives you to work with regular function based views... +REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of `Request` (rather than the usual Django `HttpRequest`) and allows them to return a `Response` (instead of a Django `HttpResponse`), and allow you to configure how the request is processed. + +### api_view(http_method_names) + +The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: + + from rest_framework.decorators import api_view + + @api_view(['GET']) + def hello_world(request): + return Response({"message": "Hello, world!"}) + + +This view will use the default renderers, parsers, authentication classes etc specified in the [settings](settings). + +To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `api_view` decorator. For example, to create a view that uses a [throttle](throttling) to ensure it can only be called once per day by a particular user, use the `throttle_classes` decorator, passing a list of throttle classes: + + from rest_framework.decorators import api_view, throttle_classes + from rest_framework.throttling import UserRateThrottle + + class OncePerDayUserThrottle(UserRateThrottle): + rate = '1/day' + + @api_view(['GET']) + @throttle_classes([OncePerDayUserThrottle]) + def view(request): + return Response({"message": "Hello for today! See you tomorrow!"}) + +These decorators correspond to the attributes set on `APIView` subclasses, described above. + +### renderer_classes(renderer_classes) + +### parser_classes(parser_classes) + +### authentication_classes(authentication_classes) + +### throttle_classes(throttle_classes) + +### permission_classes(permission_classes) -**[TODO]** [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 -- cgit v1.2.3 From ec1429ffc8079e2f0fcc5af05882360689fca5bf Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 27 Oct 2012 17:27:12 +0200 Subject: Tweaks --- docs/api-guide/views.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index c51860b5..9e661532 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -148,15 +148,15 @@ To override the default settings, REST framework provides a set of additional de These decorators correspond to the attributes set on `APIView` subclasses, described above. -### renderer_classes(renderer_classes) +### @renderer_classes() -### parser_classes(parser_classes) +### @parser_classes() -### authentication_classes(authentication_classes) +### @authentication_classes() -### throttle_classes(throttle_classes) +### @throttle_classes() -### permission_classes(permission_classes) +### @permission_classes() [cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html -- cgit v1.2.3 From cef379db065711bd2f1b0805d28a56f7a80cef37 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 27 Oct 2012 18:39:17 +0100 Subject: 2.0 Announcement --- docs/api-guide/views.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 9e661532..96ce3be7 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -120,7 +120,9 @@ You won't typically need to override this method. REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of `Request` (rather than the usual Django `HttpRequest`) and allows them to return a `Response` (instead of a Django `HttpResponse`), and allow you to configure how the request is processed. -### api_view(http_method_names) +## @api_view() + +**Signature:** `@api_view(http_method_names) The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: @@ -133,7 +135,9 @@ The core of this functionality is the `api_view` decorator, which takes a list o This view will use the default renderers, parsers, authentication classes etc specified in the [settings](settings). -To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `api_view` decorator. For example, to create a view that uses a [throttle](throttling) to ensure it can only be called once per day by a particular user, use the `throttle_classes` decorator, passing a list of throttle classes: +## API policy decorators + +To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle](throttling) to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes: from rest_framework.decorators import api_view, throttle_classes from rest_framework.throttling import UserRateThrottle @@ -148,16 +152,15 @@ To override the default settings, REST framework provides a set of additional de These decorators correspond to the attributes set on `APIView` subclasses, described above. -### @renderer_classes() - -### @parser_classes() - -### @authentication_classes() - -### @throttle_classes() +The available decorators are: -### @permission_classes() +* `@renderer_classes(...)` +* `@parser_classes(...)` +* `@authentication_classes(...)` +* `@throttle_classes(...)` +* `@permission_classes(...)` +Each of these decorators takes a single argument which must be a list or tuple of classes. [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 -- cgit v1.2.3 From d995742afc09ff8d387751a6fe47b9686845740b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 27 Oct 2012 20:04:33 +0100 Subject: Add AllowAny permission --- docs/api-guide/permissions.md | 12 ++++++++++++ docs/api-guide/settings.md | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 0b7b32e9..d43b7bed 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -33,6 +33,12 @@ The default permission policy may be set globally, using the `DEFAULT_PERMISSION ) } +If not specified, this setting defaults to allowing unrestricted access: + + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.AllowAny', + ) + You can also set the authentication policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): @@ -58,6 +64,12 @@ Or, if you're using the `@api_view` decorator with function based views. # API Reference +## AllowAny + +The `AllowAny` permission class will allow unrestricted access, **regardless of if the request was authenticated or unauthenticated**. + +This permission is not strictly required, since you can achieve the same result by using an empty list or tuple for the permissions setting, but you may find it useful to specify this class because it makes the intention explicit. + ## IsAuthenticated The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 21efc853..3556a5b1 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -72,7 +72,11 @@ Default: A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view. -Default: `()` +Default: + + ( + 'rest_framework.permissions.AllowAny', + ) ## DEFAULT_THROTTLE_CLASSES -- cgit v1.2.3 From 12c363c1fe237d0357e6020b44890926856b9191 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Oct 2012 18:12:56 +0000 Subject: TemplateHTMLRenderer, StaticHTMLRenderer --- docs/api-guide/renderers.md | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index b5e2fe8f..5efb3610 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -106,12 +106,12 @@ If you are considering using `XML` for your API, you may want to consider implem **.format**: `'.xml'` -## HTMLRenderer +## TemplateHTMLRenderer Renders data to HTML, using Django's standard template rendering. Unlike other renderers, the data passed to the `Response` does not need to be serialized. Also, unlike other renderers, you may want to include a `template_name` argument when creating the `Response`. -The HTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. +The TemplateHTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context. The template name is determined by (in order of preference): @@ -119,27 +119,49 @@ The template name is determined by (in order of preference): 2. An explicit `.template_name` attribute set on this class. 3. The return result of calling `view.get_template_names()`. -An example of a view that uses `HTMLRenderer`: +An example of a view that uses `TemplateHTMLRenderer`: class UserInstance(generics.RetrieveUserAPIView): """ A view that returns a templated HTML representations of a given user. """ model = Users - renderer_classes = (HTMLRenderer,) + renderer_classes = (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 `HTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. +You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. -If you're building websites that use `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. +If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers. **.media_type**: `text/html` **.format**: `'.html'` +See also: `StaticHTMLRenderer` + +## StaticHTMLRenderer + +A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned. + +An example of a view that uses `TemplateHTMLRenderer`: + + @api_view(('GET',)) + @renderer_classes((StaticHTMLRenderer,)) + def simple_html_view(request): + data = '

Hello, world

' + return Response(data) + +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. + +**.media_type**: `text/html` + +**.format**: `'.html'` + +See also: `TemplateHTMLRenderer` + ## BrowsableAPIRenderer Renders data into HTML for the Browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page. @@ -207,7 +229,7 @@ In some cases you might want your view to use different serialization styles dep For example: @api_view(('GET',)) - @renderer_classes((HTMLRenderer, JSONRenderer)) + @renderer_classes((TemplateHTMLRenderer, JSONRenderer)) def list_users(request): """ A view that can return JSON or HTML representations @@ -215,9 +237,9 @@ For example: """ queryset = Users.objects.filter(active=True) - if request.accepted_media_type == 'text/html': + if request.accepted_renderer.format == 'html': # TemplateHTMLRenderer takes a context dict, - # and additionally requiresa 'template_name'. + # and additionally requires a 'template_name'. # It does not require serialization. data = {'users': queryset} return Response(data, template_name='list_users.html') -- cgit v1.2.3 From 3906ff0df5d1972a7226a4fdd0eebce9a026befa Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Oct 2012 20:18:02 +0000 Subject: Improve fields docs --- docs/api-guide/fields.md | 69 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 7e117df7..bf80945d 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -12,6 +12,51 @@ Serializer fields handle converting between primative values and internal dataty **Note:** The serializer fields are declared in fields.py, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.`. +--- + +## Core arguments + +Each serializer field class constructor takes at least these arguments. Some Field classes take additional, field-specific arguments, but the following should always be accepted: + +### `source` + +The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `Field(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `Field(source='user.email')`. + +The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations. (See the implementation of the `PaginationSerializer` class for an example.) + +Defaults to the name of the field. + +### `readonly` + +Set this to `True` to ensure that the field is used when serializing a representation, but is not used when updating an instance dureing deserialization. + +Defaults to `False` + +### `required` + +Normally an error will be raised if a field is not supplied during deserialization. +Set to false if this field is not required to be present during deserialization. + +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 behaviour is to not populate the attribute at all. + +### `validators` + +A list of Django validators that should be used to validate deserialized values. + +### `error_messages` + +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. + + --- # Generic Fields @@ -192,7 +237,7 @@ Then an example output format for a Bookmark instance would be: ## PrimaryKeyRelatedField -As with `RelatedField` field can be applied to any "to-one" relationship, such as a `ForeignKey` field. +This field can be applied to any "to-one" relationship, such as a `ForeignKey` field. `PrimaryKeyRelatedField` will represent the target of the field using it's primary key. @@ -200,16 +245,34 @@ Be default, `PrimaryKeyRelatedField` is read-write, although you can change this ## ManyPrimaryKeyRelatedField -As with `RelatedField` field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. +This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. -`PrimaryKeyRelatedField` will represent the target of the field using their primary key. +`PrimaryKeyRelatedField` will represent the targets of the field using their primary key. Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. ## HyperlinkedRelatedField +This field can be applied to any "to-one" relationship, such as a `ForeignKey` field. + +`HyperlinkedRelatedField` will represent the target of the field using a hyperlink. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. + +Be default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. + ## ManyHyperlinkedRelatedField +This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. + +`ManyHyperlinkedRelatedField` will represent the targets of the field using hyperlinks. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. + +Be default, `ManyHyperlinkedRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. + ## HyperLinkedIdentityField +This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer. + +You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the model. + +This field is always read-only. + [cite]: http://www.python.org/dev/peps/pep-0020/ -- cgit v1.2.3 From 6e4ab09aae8295e4ef722d59894bc2934435ae46 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Oct 2012 20:21:45 +0000 Subject: readonly -> read_only --- docs/api-guide/fields.md | 10 +++++----- docs/api-guide/serializers.md | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index bf80945d..8c3df067 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -26,7 +26,7 @@ The value `source='*'` has a special meaning, and is used to indicate that the e Defaults to the name of the field. -### `readonly` +### `read_only` Set this to `True` to ensure that the field is used when serializing a representation, but is not used when updating an instance dureing deserialization. @@ -241,7 +241,7 @@ This field can be applied to any "to-one" relationship, such as a `ForeignKey` f `PrimaryKeyRelatedField` will represent the target of the field using it's primary key. -Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. +Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. ## ManyPrimaryKeyRelatedField @@ -249,7 +249,7 @@ This field can be applied to any "to-many" relationship, such as a `ManyToManyFi `PrimaryKeyRelatedField` will represent the targets of the field using their primary key. -Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. +Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. ## HyperlinkedRelatedField @@ -257,7 +257,7 @@ This field can be applied to any "to-one" relationship, such as a `ForeignKey` f `HyperlinkedRelatedField` will represent the target of the field using a hyperlink. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. -Be default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. +Be default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. ## ManyHyperlinkedRelatedField @@ -265,7 +265,7 @@ This field can be applied to any "to-many" relationship, such as a `ManyToManyFi `ManyHyperlinkedRelatedField` will represent the targets of the field using hyperlinks. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. -Be default, `ManyHyperlinkedRelatedField` is read-write, although you can change this behaviour using the `readonly` flag. +Be default, `ManyHyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. ## HyperLinkedIdentityField diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 057827d3..2338b879 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -190,7 +190,7 @@ The `ModelSerializer` class lets you automatically create a Serializer class wit 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. class AccountSerializer(serializers.ModelSerializer): - url = CharField(source='get_absolute_url', readonly=True) + url = CharField(source='get_absolute_url', read_only=True) group = NaturalKeyField() class Meta: @@ -246,7 +246,7 @@ When serializing objects using a nested representation any occurances of recursi model = Account def get_pk_field(self, model_field): - return serializers.Field(readonly=True) + return serializers.Field(read_only=True) def get_nested_field(self, model_field): return serializers.ModelSerializer() -- cgit v1.2.3 From 351382fe35f966c989b27add5bb04d0d983a99ee Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Oct 2012 20:43:43 +0000 Subject: nested -> depth --- docs/api-guide/serializers.md | 76 +++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 31 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 2338b879..902179ba 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -107,21 +107,21 @@ where some of the attributes of an object might not be simple datatypes such as The `Serializer` class is itself a type of `Field`, and can be used to represent relationships where one object type is nested inside another. class UserSerializer(serializers.Serializer): - email = serializers.EmailField() - username = serializers.CharField() - - def restore_object(self, attrs, instance=None): - return User(**attrs) - + email = serializers.Field() + username = serializers.Field() class CommentSerializer(serializers.Serializer): user = UserSerializer() - title = serializers.CharField() - content = serializers.CharField(max_length=200) - created = serializers.DateTimeField() - - def restore_object(self, attrs, instance=None): - return Comment(**attrs) + title = serializers.Field() + content = serializers.Field() + created = serializers.Field() + +--- + +**Note**: Nested serializers are only suitable for read-only representations, as there are cases where they would have ambiguous or non-obvious behavior if used when updating instances. For read-write representations you should always use a flat representation, by using one of the `RelatedField` subclasses. + +--- + ## Creating custom fields @@ -225,40 +225,54 @@ For example: ## Specifiying nested serialization -The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `nested` option: +The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account exclude = ('id',) - nested = True + depth = 1 -The `nested` option may be set to either `True`, `False`, or an integer value. If given an integer value it indicates the depth of relationships that should be traversed before reverting to a flat representation. +The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation. -When serializing objects using a nested representation any occurances of recursion will be recognised, and will fall back to using a flat representation. +## Customising the default fields -## Customising the default fields used by a ModelSerializer +You can create customized subclasses of `ModelSerializer` that use a different set of default fields for the representation, by overriding various `get__field` methods. +Each of these methods may either return a field or serializer instance, or `None`. +### get_pk_field - class AccountSerializer(serializers.ModelSerializer): - class Meta: - model = Account +**Signature**: `.get_pk_field(self, model_field)` - def get_pk_field(self, model_field): - return serializers.Field(read_only=True) +Returns the field instance that should be used to represent the pk field. + +### get_nested_field + +**Signature**: `.get_nested_field(self, model_field)` + +Returns the field instance that should be used to represent a related field when `depth` is specified as being non-zero. + +### get_related_field - def get_nested_field(self, model_field): - return serializers.ModelSerializer() +**Signature**: `.get_related_field(self, model_field, to_many=False)` - def get_related_field(self, model_field, to_many=False): - queryset = model_field.rel.to._default_manager - if to_many: - return serializers.ManyRelatedField(queryset=queryset) - return serializers.RelatedField(queryset=queryset) +Returns the field instance that should be used to represent a related field when `depth` is not specified, or when nested representations are being used and the depth reaches zero. + +### get_field + +**Signature**: `.get_field(self, model_field)` + +Returns the field instance that should be used for non-relational, non-pk fields. + +### Example: + +The following custom model serializer could be used as a base class for model serializers that should always exclude the pk by default. + + class NoPKModelSerializer(serializers.ModelSerializer): + def get_pk_field(self, model_field): + return None - def get_field(self, model_field): - return serializers.ModelField(model_field=model_field) [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion -- cgit v1.2.3 From 8d2774dc972ff5144d8ac0450c7f91ade2556ae0 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:07:42 +0100 Subject: fixed api_view decorator useage --- docs/api-guide/parsers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md index ac904720..59f00f99 100644 --- a/docs/api-guide/parsers.md +++ b/docs/api-guide/parsers.md @@ -37,7 +37,7 @@ You can also set the renderers used for an individual view, using the `APIView` Or, if you're using the `@api_view` decorator with function based views. - @api_view(('POST',)), + @api_view(['POST']) @parser_classes((YAMLParser,)) def example_view(request, format=None): """ -- cgit v1.2.3 From 842c8b4da4a556f7f4f337d482b2f039de3770ee Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:12:21 +0100 Subject: add missing "`" for code formatting --- docs/api-guide/views.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 96ce3be7..5b072827 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -122,7 +122,7 @@ REST framework also allows you to work with regular function based views. It pro ## @api_view() -**Signature:** `@api_view(http_method_names) +**Signature:** `@api_view(http_method_names)` The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: -- cgit v1.2.3 From 72f3a7e4a7e07035f428bf2f8ce8ad31aa85f297 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:13:56 +0100 Subject: add missing semicolon --- docs/api-guide/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 3556a5b1..a3668e2a 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -42,7 +42,7 @@ Default: ( 'rest_framework.renderers.JSONRenderer', - 'rest_framework.renderers.BrowsableAPIRenderer' + 'rest_framework.renderers.BrowsableAPIRenderer', 'rest_framework.renderers.TemplateHTMLRenderer' ) -- cgit v1.2.3 From 46e546ff2319fc2cf85279deee1ce2140e0c7f45 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:20:14 +0100 Subject: fixed missplaced semicolon --- docs/api-guide/throttling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index d54433b1..56f16226 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -32,8 +32,8 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttles.AnonThrottle', - 'rest_framework.throttles.UserThrottle', - ) + 'rest_framework.throttles.UserThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' -- cgit v1.2.3 From 5164f5d7978e68ff3e68eaab5d30faea21241fc8 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:21:27 +0100 Subject: fixed missplaced semicolon --- docs/api-guide/throttling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 56f16226..c8769a10 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -102,8 +102,8 @@ For example, multiple user throttle rates could be implemented by using the foll REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'example.throttles.BurstRateThrottle', - 'example.throttles.SustainedRateThrottle', - ) + 'example.throttles.SustainedRateThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'burst': '60/min', 'sustained': '1000/day' -- cgit v1.2.3 From 741b387f35a3f5daa98424d29fea4325898b7ff6 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:22:20 +0100 Subject: fixed missplaced semicolon --- docs/api-guide/throttling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index c8769a10..bfda7079 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -136,8 +136,8 @@ For example, given the following views... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( - 'rest_framework.throttles.ScopedRateThrottle', - ) + 'rest_framework.throttles.ScopedRateThrottle' + ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' -- cgit v1.2.3 From 73cf859e26608e1310c07df789553fa2b6cd1f8b Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:23:25 +0100 Subject: add missing whitespace --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index d43b7bed..1a746fb6 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -78,7 +78,7 @@ This permission is suitable if you want your API to only be accessible to regist ## IsAdminUser -The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff`is `True` in which case permission will be allowed. +The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff` is `True` in which case permission will be allowed. This permission is suitable is you want your API to only be accessible to a subset of trusted administrators. -- cgit v1.2.3 From ff4804a36079f9c1d480a788397af3a7f8391371 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:25:17 +0100 Subject: fix api_view decorator useage --- docs/api-guide/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 7bad4867..889d16c0 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -50,7 +50,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. - @api_view(('GET',)), + @api_view(['GET']) @authentication_classes((SessionAuthentication, UserBasicAuthentication)) @permissions_classes((IsAuthenticated,)) def example_view(request, format=None): -- cgit v1.2.3 From 2de89f2d53c4b06baca8df218c8de959af69a006 Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:29:45 +0100 Subject: remove empty rows --- docs/api-guide/serializers.md | 2 -- 1 file changed, 2 deletions(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 902179ba..c88b9b0c 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -135,7 +135,6 @@ Let's look at an example of serializing a class that represents an RGB color val """ A color represented in the RGB colorspace. """ - def __init__(self, red, green, blue): assert(red >= 0 and green >= 0 and blue >= 0) assert(red < 256 and green < 256 and blue < 256) @@ -145,7 +144,6 @@ Let's look at an example of serializing a class that represents an RGB color val """ Color objects are serialized into "rgb(#, #, #)" notation. """ - def to_native(self, obj): return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue) -- cgit v1.2.3 From 586584201967d9810f649def51cef577c65d50fb Mon Sep 17 00:00:00 2001 From: Stephan Groß Date: Mon, 29 Oct 2012 09:32:11 +0100 Subject: fixed api_view decorator useage --- docs/api-guide/renderers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/api-guide') diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index 5efb3610..c3d12ddb 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -42,7 +42,7 @@ You can also set the renderers used for an individual view, using the `APIView` Or, if you're using the `@api_view` decorator with function based views. - @api_view(('GET',)), + @api_view(['GET']) @renderer_classes((JSONRenderer, JSONPRenderer)) def user_count_view(request, format=None): """ -- cgit v1.2.3