Commit d4b8e356 by Tom Christie

Merge branch '3.0-docs'

parents d2d7e1df 34ca8cd2
...@@ -100,7 +100,7 @@ For example, if your API relies on a third party service that may sometimes be u ...@@ -100,7 +100,7 @@ For example, if your API relies on a third party service that may sometimes be u
**Signature:** `ParseError(detail=None)` **Signature:** `ParseError(detail=None)`
Raised if the request contains malformed data when accessing `request.DATA` or `request.FILES`. Raised if the request contains malformed data when accessing `request.data`.
By default this exception results in a response with the HTTP status code "400 Bad Request". By default this exception results in a response with the HTTP status code "400 Bad Request".
...@@ -140,7 +140,7 @@ By default this exception results in a response with the HTTP status code "405 M ...@@ -140,7 +140,7 @@ By default this exception results in a response with the HTTP status code "405 M
**Signature:** `UnsupportedMediaType(media_type, detail=None)` **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`. Raised if there are no parsers that can handle the content type of the request data when accessing `request.data`.
By default this exception results in a response with the HTTP status code "415 Unsupported Media Type". By default this exception results in a response with the HTTP status code "415 Unsupported Media Type".
...@@ -152,5 +152,23 @@ Raised when an incoming request fails the throttling checks. ...@@ -152,5 +152,23 @@ 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". By default this exception results in a response with the HTTP status code "429 Too Many Requests".
## ValidationError
**Signature:** `ValidationError(detail)`
The `ValidationError` exception is slightly different from the other `APIException` classes:
* The `detail` argument is mandatory, not optional.
* The `detail` argument may be a list or dictionary of error details, and may also be a nested data structure.
* By convention you should import the serializers module and use a fully qualified `ValidationError` style, in order to differentiate it from Django's built-in validation error. For example. `raise serializers.ValidationError('This field must be an integer value.')`
The `ValidationError` class should be used for serializer and field validation, and by validator classes. It is also raised when calling `serializer.is_valid` with the `raise_exception` keyword argument:
serializer.is_valid(raise_exception=True)
The generic views use the `raise_exception=True` flag, which means that you can override the style of validation error responses globally in your API. To do so, use a custom exception handler, as described above.
By default this exception results in a response with the HTTP status code "400 Bad Request".
[cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html [cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html
[authentication]: authentication.md [authentication]: authentication.md
...@@ -7,7 +7,7 @@ source: mixins.py ...@@ -7,7 +7,7 @@ source: mixins.py
> >
> — [Django Documentation][cite] > — [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. One of the key benefits of class based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns.
The generic views provided by REST framework allow you to quickly build API views that map closely to your database models. The generic views provided by REST framework allow you to quickly build API views that map closely to your database models.
...@@ -171,24 +171,26 @@ For example: ...@@ -171,24 +171,26 @@ For example:
return 20 return 20
return 100 return 100
**Save / deletion hooks**: **Save and deletion hooks**:
The following methods are provided as placeholder interfaces. They contain empty implementations and are not called directly by `GenericAPIView`, but they are overridden and used by some of the mixin classes. The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
* `pre_save(self, obj)` - A hook that is called before saving an object. * `perform_create(self, serializer)` - Called by `CreateModelMixin` when saving a new object instance.
* `post_save(self, obj, created=False)` - A hook that is called after saving an object. * `perform_update(self, serializer)` - Called by `UpdateModelMixin` when saving an existing object instance.
* `pre_delete(self, obj)` - A hook that is called before deleting an object. * `perform_destroy(self, instance)` - Called by `DestroyModelMixin` when deleting an object instance.
* `post_delete(self, obj)` - A hook that is called after deleting an object.
The `pre_save` method in particular is a useful hook for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument. These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.
def pre_save(self, obj): def perform_create(self, serializer):
""" serializer.save(user=self.request.user)
Set the object's owner, based on the incoming request.
""" These override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update.
obj.owner = self.request.user
def perform_update(self, serializer):
instance = serializer.save()
send_email_confirmation(user=self.request.user, modified=instance)
Remember that the `pre_save()` method is not called by `GenericAPIView` itself, but it is called by `create()` and `update()` methods on the `CreateModelMixin` and `UpdateModelMixin` classes. **Note**: These methods replace the old-style version 2.x `pre_save`, `post_save`, `pre_delete` and `post_delete` methods, which are no longer available.
**Other methods**: **Other methods**:
...@@ -352,7 +354,7 @@ You can then simply apply this mixin to a view or viewset anytime you need to ap ...@@ -352,7 +354,7 @@ You can then simply apply this mixin to a view or viewset anytime you need to ap
serializer_class = UserSerializer serializer_class = UserSerializer
lookup_fields = ('account', 'username') lookup_fields = ('account', 'username')
Using custom mixins is a good option if you have custom behavior that needs to be used Using custom mixins is a good option if you have custom behavior that needs to be used.
## Creating custom base classes ## Creating custom base classes
......
...@@ -12,7 +12,7 @@ REST framework includes a number of built in Parser classes, that allow you to a ...@@ -12,7 +12,7 @@ REST framework includes a number of built in Parser classes, that allow you to a
## How the parser is determined ## 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. The set of valid parsers for a view is always defined as a list of classes. When `request.data` 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.
--- ---
...@@ -48,7 +48,7 @@ using the `APIView` class based views. ...@@ -48,7 +48,7 @@ using the `APIView` class based views.
parser_classes = (YAMLParser,) parser_classes = (YAMLParser,)
def post(self, request, format=None): def post(self, request, format=None):
return Response({'received data': request.DATA}) return Response({'received data': request.data})
Or, if you're using the `@api_view` decorator with function based views. Or, if you're using the `@api_view` decorator with function based views.
...@@ -58,7 +58,7 @@ Or, if you're using the `@api_view` decorator with function based views. ...@@ -58,7 +58,7 @@ Or, if you're using the `@api_view` decorator with function based views.
""" """
A view that can accept POST requests with YAML content. A view that can accept POST requests with YAML content.
""" """
return Response({'received data': request.DATA}) return Response({'received data': request.data})
--- ---
...@@ -92,7 +92,7 @@ Requires the `defusedxml` package to be installed. ...@@ -92,7 +92,7 @@ Requires the `defusedxml` package to be installed.
## FormParser ## 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. Parses HTML form content. `request.data` will be populated with a `QueryDict` of data.
You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data. You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
...@@ -100,7 +100,7 @@ You will typically want to use both `FormParser` and `MultiPartParser` together ...@@ -100,7 +100,7 @@ You will typically want to use both `FormParser` and `MultiPartParser` together
## MultiPartParser ## MultiPartParser
Parses multipart HTML form content, which supports file uploads. Both `request.DATA` and `request.FILES` will be populated with a `QueryDict`. Parses multipart HTML form content, which supports file uploads. Both `request.data` 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. You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
...@@ -108,7 +108,7 @@ You will typically want to use both `FormParser` and `MultiPartParser` together ...@@ -108,7 +108,7 @@ You will typically want to use both `FormParser` and `MultiPartParser` together
## FileUploadParser ## FileUploadParser
Parses raw file upload content. The `request.DATA` property will be an empty `QueryDict`, and `request.FILES` will be a dictionary with a single key `'file'` containing the uploaded file. Parses raw file upload content. The `request.data` property will be a dictionary with a single key `'file'` containing the uploaded file.
If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename. If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`. If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename. If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`.
...@@ -126,7 +126,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword ...@@ -126,7 +126,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword
parser_classes = (FileUploadParser,) parser_classes = (FileUploadParser,)
def put(self, request, filename, format=None): def put(self, request, filename, format=None):
file_obj = request.FILES['file'] file_obj = request.data['file']
# ... # ...
# do some staff with uploaded file # do some staff with uploaded file
# ... # ...
...@@ -139,7 +139,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword ...@@ -139,7 +139,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword
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. 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. The method should return the data that will be used to populate the `request.data` property.
The arguments passed to `.parse()` are: The arguments passed to `.parse()` are:
...@@ -161,7 +161,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa ...@@ -161,7 +161,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa
## Example ## Example
The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request. The following is an example plaintext parser that will populate the `request.data` property with a string representing the body of the request.
class PlainTextParser(BaseParser): class PlainTextParser(BaseParser):
""" """
......
...@@ -14,26 +14,29 @@ REST framework's `Request` class extends the standard `HttpRequest`, adding supp ...@@ -14,26 +14,29 @@ REST framework's `Request` class extends the standard `HttpRequest`, adding supp
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. 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 ## .data
`request.DATA` returns the parsed content of the request body. This is similar to the standard `request.POST` attribute except that: `request.data` returns the parsed content of the request body. This is similar to the standard `request.POST` and `request.FILES` attributes except that:
* It includes all parsed content, including *file and non-file* inputs.
* 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 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. * 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.
For more details see the [parsers documentation]. For more details see the [parsers documentation].
## .FILES ## .query_params
`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`. `request.query_params` is a more correctly named synonym for `request.GET`.
For more details see the [parsers documentation]. For clarity inside your code, we recommend using `request.query_params` instead of the Django's standard `request.GET`. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just `GET` requests.
## .QUERY_PARAMS ## .DATA and .FILES
`request.QUERY_PARAMS` is a more correctly named synonym for `request.GET`. The old-style version 2.x `request.data` and `request.FILES` attributes are still available, but are now pending deprecation in favor of the unified `request.data` attribute.
## .QUERY_PARAMS
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. The old-style version 2.x `request.QUERY_PARAMS` attribute is still available, but is now pending deprecation in favor of the more pythonic `request.query_params`.
## .parsers ## .parsers
...@@ -43,7 +46,7 @@ You won't typically need to access this property. ...@@ -43,7 +46,7 @@ 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. **Note:** If a client sends malformed content, then accessing `request.data` 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. 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.
......
...@@ -51,7 +51,7 @@ Default: ...@@ -51,7 +51,7 @@ Default:
#### DEFAULT_PARSER_CLASSES #### DEFAULT_PARSER_CLASSES
A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.DATA` property. A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.data` property.
Default: Default:
......
...@@ -124,7 +124,7 @@ For example: ...@@ -124,7 +124,7 @@ For example:
@detail_route(methods=['post']) @detail_route(methods=['post'])
def set_password(self, request, pk=None): def set_password(self, request, pk=None):
user = self.get_object() user = self.get_object()
serializer = PasswordSerializer(data=request.DATA) serializer = PasswordSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
user.set_password(serializer.data['password']) user.set_password(serializer.data['password'])
user.save() user.save()
......
...@@ -172,7 +172,7 @@ The API guide is your complete reference manual to all the functionality provide ...@@ -172,7 +172,7 @@ The API guide is your complete reference manual to all the functionality provide
* [Serializers][serializers] * [Serializers][serializers]
* [Serializer fields][fields] * [Serializer fields][fields]
* [Serializer relations][relations] * [Serializer relations][relations]
<!--* [Validators][validators]--> * [Validators][validators]
* [Authentication][authentication] * [Authentication][authentication]
* [Permissions][permissions] * [Permissions][permissions]
* [Throttling][throttling] * [Throttling][throttling]
...@@ -294,7 +294,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -294,7 +294,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[serializers]: api-guide/serializers.md [serializers]: api-guide/serializers.md
[fields]: api-guide/fields.md [fields]: api-guide/fields.md
[relations]: api-guide/relations.md [relations]: api-guide/relations.md
[validation]: api-guide/validation.md [validators]: api-guide/validators.md
[authentication]: api-guide/authentication.md [authentication]: api-guide/authentication.md
[permissions]: api-guide/permissions.md [permissions]: api-guide/permissions.md
[throttling]: api-guide/throttling.md [throttling]: api-guide/throttling.md
......
...@@ -199,6 +199,33 @@ Alternatively if you want the errors to be against a specific field, use a dicti ...@@ -199,6 +199,33 @@ Alternatively if you want the errors to be against a specific field, use a dicti
This ensures you can still write validation that compares all the input fields, but that marks the error against a particular field. This ensures you can still write validation that compares all the input fields, but that marks the error against a particular field.
#### Removal of `transform_<field_name>`.
The under-used `transform_<field_name>` on serializer classes is no longer provided. Instead you should just override `to_representation()` if you need to apply any modifications to the representation style.
For example:
def to_representation(self, instance):
ret = super(UserSerializer, self).to_representation(instance)
ret['username'] = ret['username'].lower()
return ret
Dropping the extra point of API means there's now only one right way to do things. This helps with repetition and reinforcement of the core API, rather than having multiple differing approaches.
If you absolutely need to preserve `transform_<field_name>` behavior, for example, in order to provide a simpler 2.x to 3.0 upgrade, you can use a mixin, or serializer base class that add the behavior back in. For example:
class BaseModelSerializer(ModelSerializer):
"""
A custom ModelSerializer class that preserves 2.x style `transform_<field_name>` behavior.
"""
def to_representation(self, instance):
ret = super(BaseModelSerializer, self).to_representation(instance)
for key, value in ret.items():
method = getattr(self, 'transform_' + key, None)
if method is not None:
ret[key] = method(value)
return ret
#### Differences between ModelSerializer validation and ModelForm. #### Differences between ModelSerializer validation and ModelForm.
This change also means that we no longer use the `.full_clean()` method on model instances, but instead perform all validation explicitly on the serializer. This gives a cleaner separation, and ensures that there's no automatic validation behavior on `ModelSerializer` classes that can't also be easily replicated on regular `Serializer` classes. This change also means that we no longer use the `.full_clean()` method on model instances, but instead perform all validation explicitly on the serializer. This gives a cleaner separation, and ensures that there's no automatic validation behavior on `ModelSerializer` classes that can't also be easily replicated on regular `Serializer` classes.
...@@ -386,7 +413,11 @@ There are four methods that can be overridden, depending on what functionality y ...@@ -386,7 +413,11 @@ There are four methods that can be overridden, depending on what functionality y
* `.to_representation()` - Override this to support serialization, for read operations. * `.to_representation()` - Override this to support serialization, for read operations.
* `.to_internal_value()` - Override this to support deserialization, for write operations. * `.to_internal_value()` - Override this to support deserialization, for write operations.
* `.create()` and `.update()` - Overide either or both of these to support saving instances. * `.create()` and `.update()` - Override either or both of these to support saving instances.
Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`.
The only difference you'll notice when doing so is the `BaseSerializer` classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input.
##### Read-only `BaseSerializer` classes. ##### Read-only `BaseSerializer` classes.
...@@ -471,7 +502,7 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd ...@@ -471,7 +502,7 @@ Here's a complete example of our previous `HighScoreSerializer`, that's been upd
The `BaseSerializer` class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends. The `BaseSerializer` class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends.
The following class is an example of a generic serializer that can handle coercing aribitrary objects into primitive representations. The following class is an example of a generic serializer that can handle coercing arbitrary objects into primitive representations.
class ObjectSerializer(serializers.BaseSerializer): class ObjectSerializer(serializers.BaseSerializer):
""" """
...@@ -491,12 +522,12 @@ The following class is an example of a generic serializer that can handle coerci ...@@ -491,12 +522,12 @@ The following class is an example of a generic serializer that can handle coerci
# Primitive types can be passed through unmodified. # Primitive types can be passed through unmodified.
output[attribute_name] = attribute output[attribute_name] = attribute
elif isinstance(attribute, list): elif isinstance(attribute, list):
# Recursivly deal with items in lists. # Recursively deal with items in lists.
output[attribute_name] = [ output[attribute_name] = [
self.to_representation(item) for item in attribute self.to_representation(item) for item in attribute
] ]
elif isinstance(attribute, dict): elif isinstance(attribute, dict):
# Recursivly deal with items in dictionarys. # Recursively deal with items in dictionaries.
output[attribute_name] = { output[attribute_name] = {
str(key): self.to_representation(value) str(key): self.to_representation(value)
for key, value in attribute.items() for key, value in attribute.items()
...@@ -544,6 +575,19 @@ The `default` argument is also available and always implies that the field is no ...@@ -544,6 +575,19 @@ The `default` argument is also available and always implies that the field is no
The previous field implementations did not forcibly coerce returned values into the correct type in many cases. For example, an `IntegerField` would return a string output if the attribute value was a string. We now more strictly coerce to the correct return type, leading to more constrained and expected behavior. The previous field implementations did not forcibly coerce returned values into the correct type in many cases. For example, an `IntegerField` would return a string output if the attribute value was a string. We now more strictly coerce to the correct return type, leading to more constrained and expected behavior.
#### Removal of `.validate()`.
The `.validate()` method is now removed from field classes. This method was in any case undocumented and not public API. You should instead simply override `to_internal_value()`.
class UppercaseCharField(serializers.CharField):
def to_internal_value(self, data):
value = super(UppercaseCharField, self).to_internal_value(data)
if value != value.upper():
raise serializers.ValidationError('The input should be uppercase only.')
return value
Previously validation errors could be raised in either `.to_native()` or `.validate()`, making it non-obvious which should be used. Providing only a single point of API ensures more repetition and reinforcement of the core API.
#### The `ListField` class. #### The `ListField` class.
The `ListField` class has now been added. This field validates list input. It takes a `child` keyword argument which is used to specify the field used to validate each item in the list. For example: The `ListField` class has now been added. This field validates list input. It takes a `child` keyword argument which is used to specify the field used to validate each item in the list. For example:
...@@ -865,7 +909,7 @@ Or modify it on an individual serializer field, using the `coerce_to_string` key ...@@ -865,7 +909,7 @@ Or modify it on an individual serializer field, using the `coerce_to_string` key
coerce_to_string=False coerce_to_string=False
) )
The default JSON renderer will return float objects for uncoerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs. The default JSON renderer will return float objects for un-coerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.
## Miscellaneous notes. ## Miscellaneous notes.
......
...@@ -130,34 +130,24 @@ You can override the `BrowsableAPIRenderer.get_context()` method to customise th ...@@ -130,34 +130,24 @@ You can override the `BrowsableAPIRenderer.get_context()` method to customise th
For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you. For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you.
#### Autocompletion #### Handling `ChoiceField` with large numbers of items.
When a `ChoiceField` has too many items, rendering the widget containing all the options can become very slow, and cause the browsable API rendering to perform poorly. One solution is to replace the selector by an autocomplete widget, that only loads and renders a subset of the available options as needed. When a relationship or `ChoiceField` has too many items, rendering the widget containing all the options can become very slow, and cause the browsable API rendering to perform poorly.
There are [a variety of packages for autocomplete widgets][autocomplete-packages], such as [django-autocomplete-light][django-autocomplete-light]. To setup `django-autocomplete-light`, follow the [installation documentation][django-autocomplete-light-install], add the the following to the `api.html` template: The simplest option in this case is to replace the select input with a standard text input. For example:
{% block script %} author = serializers.HyperlinkedRelatedField(
{{ block.super }} queryset=User.objects.all(),
{% include 'autocomplete_light/static.html' %} style={'base_template': 'input.html'}
{% endblock %}
You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer field.
import autocomplete_light
class BookSerializer(serializers.ModelSerializer):
author = serializers.ChoiceField(
widget=autocomplete_light.ChoiceWidget('AuthorAutocomplete')
) )
class Meta: #### Autocomplete
model = Book
--- An alternative, but more complex option would be to replace the input with an autocomplete widget, that only loads and renders a subset of the available options as needed. If you need to do this you'll need to do some work to build a custom autocomplete HTML template yourself.
![Autocomplete][autocomplete-image] There are [a variety of packages for autocomplete widgets][autocomplete-packages], such as [django-autocomplete-light][django-autocomplete-light], that you may want to refer to. Note that you will not be able to simply include these components as standard widgets, but will need to write the HTML template explicitly. This is because REST framework 3.0 no longer supports the `widget` keyword argument since it now uses templated HTML generation.
*Screenshot of the autocomplete-light widget* Better support for autocomplete inputs is planned in future versions.
--- ---
...@@ -175,4 +165,3 @@ You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer f ...@@ -175,4 +165,3 @@ You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer f
[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/ [autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light [django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
[django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install [django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install
[autocomplete-image]: ../img/autocomplete.png
...@@ -442,7 +442,7 @@ The security vulnerabilities only affect APIs which use the `XMLParser` class, b ...@@ -442,7 +442,7 @@ The security vulnerabilities only affect APIs which use the `XMLParser` class, b
* Bugfix: Validation errors instead of exceptions when related fields receive incorrect types. * Bugfix: Validation errors instead of exceptions when related fields receive incorrect types.
* Bugfix: Handle ObjectDoesNotExist exception when serializing null reverse one-to-one * Bugfix: Handle ObjectDoesNotExist exception when serializing null reverse one-to-one
**Note**: Prior to 2.1.16, The Decimals would render in JSON using floating point if `simplejson` was installed, but otherwise render using string notation. Now that use of `simplejson` has been deprecated, Decimals will consistently render using string notation. See [#582] for more details. **Note**: Prior to 2.1.16, The Decimals would render in JSON using floating point if `simplejson` was installed, but otherwise render using string notation. Now that use of `simplejson` has been deprecated, Decimals will consistently render using string notation. See [ticket 582](ticket-582) for more details.
### 2.1.15 ### 2.1.15
...@@ -614,122 +614,7 @@ This change will not affect user code, so long as it's following the recommended ...@@ -614,122 +614,7 @@ This change will not affect user code, so long as it's following the recommended
* **Fix all of the things.** (Well, almost.) * **Fix all of the things.** (Well, almost.)
* For more information please see the [2.0 announcement][announcement]. * For more information please see the [2.0 announcement][announcement].
--- For older release notes, [please see the GitHub repo](old-release-notes).
## 0.4.x series
### 0.4.0
* Supports Django 1.5.
* Fixes issues with 'HEAD' method.
* Allow views to specify template used by TemplateRenderer
* More consistent error responses
* Some serializer fixes
* Fix internet explorer ajax behavior
* Minor xml and yaml fixes
* Improve setup (e.g. use staticfiles, not the defunct ADMIN_MEDIA_PREFIX)
* Sensible absolute URL generation, not using hacky set_script_prefix
---
## 0.3.x series
### 0.3.3
* Added DjangoModelPermissions class to support `django.contrib.auth` style permissions.
* Use `staticfiles` for css files.
- Easier to override. Won't conflict with customized admin styles (e.g. grappelli)
* Templates are now nicely namespaced.
- Allows easier overriding.
* Drop implied 'pk' filter if last arg in urlconf is unnamed.
- Too magical. Explicit is better than implicit.
* Saner template variable auto-escaping.
* Tidier setup.py
* Updated for URLObject 2.0
* Bugfixes:
- Bug with PerUserThrottling when user contains unicode chars.
### 0.3.2
* Bugfixes:
* Fix 403 for POST and PUT from the UI with UserLoggedInAuthentication (#115)
* serialize_model method in serializer.py may cause wrong value (#73)
* Fix Error when clicking OPTIONS button (#146)
* And many other fixes
* Remove short status codes
- Zen of Python: "There should be one-- and preferably only one --obvious way to do it."
* get_name, get_description become methods on the view - makes them overridable.
* Improved model mixin API - Hooks for build_query, get_instance_data, get_model, get_queryset, get_ordering
### 0.3.1
* [not documented]
### 0.3.0
* JSONP Support
* Bugfixes, including support for latest markdown release
---
## 0.2.x series
### 0.2.4
* Fix broken IsAdminUser permission.
* OPTIONS support.
* XMLParser.
* Drop mentions of Blog, BitBucket.
### 0.2.3
* Fix some throttling bugs.
* ``X-Throttle`` header on throttling.
* Support for nesting resources on related models.
### 0.2.2
* Throttling support complete.
### 0.2.1
* Couple of simple bugfixes over 0.2.0
### 0.2.0
* Big refactoring changes since 0.1.0, ask on the discussion group if anything isn't clear.
The public API has been massively cleaned up. Expect it to be fairly stable from here on in.
* ``Resource`` becomes decoupled into ``View`` and ``Resource``, your views should now inherit from ``View``, not ``Resource``.
* The handler functions on views ``.get() .put() .post()`` etc, no longer have the ``content`` and ``auth`` args.
Use ``self.CONTENT`` inside a view to access the deserialized, validated content.
Use ``self.user`` inside a view to access the authenticated user.
* ``allowed_methods`` and ``anon_allowed_methods`` are now defunct. if a method is defined, it's available.
The ``permissions`` attribute on a ``View`` is now used to provide generic permissions checking.
Use permission classes such as ``FullAnonAccess``, ``IsAuthenticated`` or ``IsUserOrIsAnonReadOnly`` to set the permissions.
* The ``authenticators`` class becomes ``authentication``. Class names change to ``Authentication``.
* The ``emitters`` class becomes ``renderers``. Class names change to ``Renderers``.
* ``ResponseException`` becomes ``ErrorResponse``.
* The mixin classes have been nicely refactored, the basic mixins are now ``RequestMixin``, ``ResponseMixin``, ``AuthMixin``, and ``ResourceMixin``
You can reuse these mixin classes individually without using the ``View`` class.
---
## 0.1.x series
### 0.1.1
* Final build before pulling in all the refactoring changes for 0.2, in case anyone needs to hang on to 0.1.
### 0.1.0
* Initial release.
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html [cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
[deprecation-policy]: #deprecation-policy [deprecation-policy]: #deprecation-policy
...@@ -742,5 +627,6 @@ This change will not affect user code, so long as it's following the recommended ...@@ -742,5 +627,6 @@ This change will not affect user code, so long as it's following the recommended
[staticfiles13]: https://docs.djangoproject.com/en/1.3/howto/static-files/#with-a-template-tag [staticfiles13]: https://docs.djangoproject.com/en/1.3/howto/static-files/#with-a-template-tag
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion [2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
[announcement]: rest-framework-2-announcement.md [announcement]: rest-framework-2-announcement.md
[#582]: https://github.com/tomchristie/django-rest-framework/issues/582 [ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3 [rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
[old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/2.4.4/docs/topics/release-notes.md#04x-series
...@@ -5,10 +5,10 @@ Let's introduce a couple of essential building blocks. ...@@ -5,10 +5,10 @@ Let's introduce a couple of essential building blocks.
## Request objects ## Request objects
REST framework introduces a `Request` object that extends the regular `HttpRequest`, and provides more flexible request parsing. The core functionality of the `Request` object is the `request.DATA` attribute, which is similar to `request.POST`, but more useful for working with Web APIs. REST framework introduces a `Request` object that extends the regular `HttpRequest`, and provides more flexible request parsing. The core functionality of the `Request` object is the `request.data` attribute, which is similar to `request.POST`, but more useful for working with Web APIs.
request.POST # Only handles form data. Only works for 'POST' method. request.POST # Only handles form data. Only works for 'POST' method.
request.DATA # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods. request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
## Response objects ## Response objects
...@@ -29,7 +29,7 @@ REST framework provides two wrappers you can use to write API views. ...@@ -29,7 +29,7 @@ REST framework provides two wrappers you can use to write API views.
These wrappers provide a few bits of functionality such as making sure you receive `Request` instances in your view, and adding context to `Response` objects so that content negotiation can be performed. These wrappers provide a few bits of functionality such as making sure you receive `Request` instances in your view, and adding context to `Response` objects so that content negotiation can be performed.
The wrappers also provide behaviour such as returning `405 Method Not Allowed` responses when appropriate, and handling any `ParseError` exception that occurs when accessing `request.DATA` with malformed input. The wrappers also provide behaviour such as returning `405 Method Not Allowed` responses when appropriate, and handling any `ParseError` exception that occurs when accessing `request.data` with malformed input.
## Pulling it all together ## Pulling it all together
...@@ -55,7 +55,7 @@ We don't need our `JSONResponse` class in `views.py` anymore, so go ahead and de ...@@ -55,7 +55,7 @@ We don't need our `JSONResponse` class in `views.py` anymore, so go ahead and de
return Response(serializer.data) return Response(serializer.data)
elif request.method == 'POST': elif request.method == 'POST':
serializer = SnippetSerializer(data=request.DATA) serializer = SnippetSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
...@@ -80,7 +80,7 @@ Here is the view for an individual snippet, in the `views.py` module. ...@@ -80,7 +80,7 @@ Here is the view for an individual snippet, in the `views.py` module.
return Response(serializer.data) return Response(serializer.data)
elif request.method == 'PUT': elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.DATA) serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return Response(serializer.data) return Response(serializer.data)
...@@ -92,7 +92,7 @@ Here is the view for an individual snippet, in the `views.py` module. ...@@ -92,7 +92,7 @@ Here is the view for an individual snippet, in the `views.py` module.
This should all feel very familiar - it is not a lot different from working with regular Django views. This should all feel very familiar - it is not a lot different from working with regular Django views.
Notice that we're no longer explicitly tying our requests or responses to a given content type. `request.DATA` can handle incoming `json` requests, but it can also handle `yaml` and other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us. Notice that we're no longer explicitly tying our requests or responses to a given content type. `request.data` can handle incoming `json` requests, but it can also handle `yaml` and other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.
## Adding optional format suffixes to our URLs ## Adding optional format suffixes to our URLs
......
...@@ -24,7 +24,7 @@ We'll start by rewriting the root view as a class based view. All this involves ...@@ -24,7 +24,7 @@ We'll start by rewriting the root view as a class based view. All this involves
return Response(serializer.data) return Response(serializer.data)
def post(self, request, format=None): def post(self, request, format=None):
serializer = SnippetSerializer(data=request.DATA) serializer = SnippetSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
...@@ -49,7 +49,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be ...@@ -49,7 +49,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
def put(self, request, pk, format=None): def put(self, request, pk, format=None):
snippet = self.get_object(pk) snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.DATA) serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return Response(serializer.data) return Response(serializer.data)
......
...@@ -26,7 +26,7 @@ pages: ...@@ -26,7 +26,7 @@ pages:
- ['api-guide/serializers.md', 'API Guide', 'Serializers'] - ['api-guide/serializers.md', 'API Guide', 'Serializers']
- ['api-guide/fields.md', 'API Guide', 'Serializer fields'] - ['api-guide/fields.md', 'API Guide', 'Serializer fields']
- ['api-guide/relations.md', 'API Guide', 'Serializer relations'] - ['api-guide/relations.md', 'API Guide', 'Serializer relations']
# - ['api-guide/validators.md', 'API Guide', 'Validators'] - ['api-guide/validators.md', 'API Guide', 'Validators']
- ['api-guide/authentication.md', 'API Guide', 'Authentication'] - ['api-guide/authentication.md', 'API Guide', 'Authentication']
- ['api-guide/permissions.md', 'API Guide', 'Permissions'] - ['api-guide/permissions.md', 'API Guide', 'Permissions']
- ['api-guide/throttling.md', 'API Guide', 'Throttling'] - ['api-guide/throttling.md', 'API Guide', 'Throttling']
......
...@@ -49,6 +49,21 @@ class RelatedField(Field): ...@@ -49,6 +49,21 @@ class RelatedField(Field):
@classmethod @classmethod
def many_init(cls, *args, **kwargs): def many_init(cls, *args, **kwargs):
"""
This method handles creating a parent `ManyRelatedField` instance
when the `many=True` keyword argument is passed.
Typically you won't need to override this method.
Note that we're over-cautious in passing most arguments to both parent
and child classes in order to try to cover the general case. If you're
overriding this method you'll probably want something much simpler, eg:
@classmethod
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls()
return CustomManyRelatedField(*args, **kwargs)
"""
list_kwargs = {'child_relation': cls(*args, **kwargs)} list_kwargs = {'child_relation': cls(*args, **kwargs)}
for key in kwargs.keys(): for key in kwargs.keys():
if key in MANY_RELATION_KWARGS: if key in MANY_RELATION_KWARGS:
...@@ -306,7 +321,9 @@ class ManyRelatedField(Field): ...@@ -306,7 +321,9 @@ class ManyRelatedField(Field):
The `ManyRelatedField` class is responsible for handling iterating through The `ManyRelatedField` class is responsible for handling iterating through
the values and passing each one to the child relationship. the values and passing each one to the child relationship.
You shouldn't need to be using this class directly yourself. This class is treated as private API.
You shouldn't generally need to be using this class directly yourself,
and should instead simply set 'many=True' on the relationship.
""" """
initial = [] initial = []
default_empty_html = [] default_empty_html = []
......
...@@ -310,7 +310,7 @@ class Request(object): ...@@ -310,7 +310,7 @@ class Request(object):
def _load_data_and_files(self): def _load_data_and_files(self):
""" """
Parses the request content into self.DATA and self.FILES. Parses the request content into `self.data`.
""" """
if not _hasattr(self, '_content_type'): if not _hasattr(self, '_content_type'):
self._load_method_and_content_type() self._load_method_and_content_type()
......
...@@ -102,7 +102,9 @@ class BaseSerializer(Field): ...@@ -102,7 +102,9 @@ class BaseSerializer(Field):
(key, value) for key, value in kwargs.items() (key, value) for key, value in kwargs.items()
if key in LIST_SERIALIZER_KWARGS if key in LIST_SERIALIZER_KWARGS
])) ]))
return ListSerializer(*args, **list_kwargs) meta = getattr(cls, 'Meta', None)
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
return list_serializer_class(*args, **list_kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
raise NotImplementedError('`to_internal_value()` must be implemented.') raise NotImplementedError('`to_internal_value()` must be implemented.')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment