The usage of `request.DATA` and `request.FILES` is now discouraged in favor of a single `request.data` attribute that contains *all* the parsed data.
#### The parser API.
Having seperate attributes is reasonable for web applications that only ever parse URL encoded or MultiPart requests, but makes less sense for the general-purpose request parsing that REST framework supports.
You may now pass all the request data to a serializer class in a single argument:
The usage of `request.QUERY_PARAMS` is now discouraged in favor of the lowercased `request.query_params`.
## Serializers
## Serializers
#### Single-step object creation.
#### Single-step object creation.
#### The `.create()` and `.update()` methods.
**TODO**: Drop `.restore_object()`, use `.create()` and `.update()` which should save the instance.
**TODO**: Drop `.restore_object()`, use `.create()` and `.update()` which should save the instance.
**TODO**: Drop`.object`, use `.validated_data` or get the instance with `.save()`.
#### Use `.validated_data` instead of `.object`.
#### The `BaseSerializer` class.
You must now use the `.validated_data` attribute if you need to inspect the data before saving, rather than using the `.object` attribute, which no longer exists.
**TODO**
For example the following code *is no longer valid*:
if serializer.is_valid():
name = serializer.object.name # Inspect validated field data.
logging.info('Creating ticket "%s"' % name)
serializer.object.user = request.user # Include the user when saving.
serializer.save()
Instead of using `.object` to inspect a partially constructed instance, you would now use `.validated_data` to inspect the cleaned incoming values. Also you can't set extra attributes on the instance directly, but instead pass them to the `.save()` method using the `extras` keyword argument.
The corresponding code would now look like this:
if serializer.is_valid():
name = serializer.validated_data['name'] # Inspect validated field data.
logging.info('Creating ticket "%s"' % name)
extras = {'user': request.user} # Include the user when saving.
serializer.save(extras=extras)
#### Always use `fields`, not `exclude`.
#### Always use `fields`, not `exclude`.
...
@@ -111,42 +140,287 @@ These fields will be mapped to `serializers.ReadOnlyField()` instances.
...
@@ -111,42 +140,287 @@ These fields will be mapped to `serializers.ReadOnlyField()` instances.
message = CharField(max_length=1000)
message = CharField(max_length=1000)
expiry_date = ReadOnlyField()
expiry_date = ReadOnlyField()
#### The `ListSerializer` class.
The `ListSerializer` class has now been added, and allows you to create base serializer classes for only accepting multiple inputs.
class MultipleUserSerializer(ListSerializer):
child = UserSerializer()
You can also still use the `many=True` argument to serializer classes. It's worth noting that `many=True` argument transparently creates a `ListSerializer` instance, allowing the validation logic for list and non-list data to be cleanly seperated in the REST framework codebase.
See also the new `ListField` class, which validates input in the same way, but does not include the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
#### The `BaseSerializer` class.
REST framework now includes a simple `BaseSerializer` class that can be used to easily support alternative serialization and deserialization styles.
This class implements the same basic API as the `Serializer` class:
*`.data` - Returns the outgoing primative representation.
*`.is_valid()` - Deserializes and validates incoming data.
*`.validated_data` - Returns the validated incoming data.
*`.errors` - Returns an errors during validation.
*`.save()` - Persists the validated data into an object instance.
There are four mathods that can be overriding, depending on what functionality you want the serializer class to support:
*`.to_representation()` - Override this to support serialization, for read 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.
##### Read-only serializers.
To implement a read-only serializer using the `BaseSerializer` class, we just need to override the `.to_representation()` method. Let's take a look at an example using a simple Django model:
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name = models.CharField(max_length=10)
score = models.IntegerField()
It's simple to create a read-only serializer for converting `HighScore` instances into primative data types.
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
return {
'score': obj.score,
'player_name': obj.player_name
}
We can now use this class to serialize single `HighScore` instances:
To create a read-write serializer we first need to implement a `.to_internal_value()` method. This method returns the validated values that will be used to construct the object instance, and may raise a `ValidationError` if the supplied data is in an incorrect format.
Once you've implemented `.to_internal_value()`, the basic validation API will be available on the serializer, and you will be able to use `.is_valid()`, `.validated_data` and `.errors`.
If you want to also support `.save()` you'll need to also implement either or both of the `.create()` and `.update()` methods.
Here's a complete example of our previous `HighScoreSerializer`, that's been updated to support both read and write operations.
class HighScoreSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
score = data.get('score')
player_name = data.get('player_name')
# Perform the data validation.
if not score:
raise ValidationError({
'score': 'This field is required.'
})
if not player_name:
raise ValidationError({
'player_name': 'This field is required.'
})
if len(player_name) > 10:
raise ValidationError({
'player_name': 'May not be more than 10 characters.'
})
# Return the validated values. This will be available as
# the `.validated_data` property.
return {
'score': int(score),
'player_name': player_name
}
def to_representation(self, obj):
return {
'score': obj.score,
'player_name': obj.player_name
}
def create(self, validated_data):
return HighScore.objects.create(**validated_data)
#### Creating new base classes with `BaseSerializer`.
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 different storage backends.
The following class is an example of a generic serializer that can handle coercing aribitrary objects into primative representations.
class ObjectSerializer(serializers.BaseSerializer):
"""
A read-only serializer that coerces arbitrary complex objects
# Primative types can be passed through unmodified.
output[attribute_name] = attribute
elif isinstance(attribute, list):
# Recursivly deal with items in lists.
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, dict):
# Recursivly deal with items in dictionarys.
output[attribute_name] = {
str(key): self.to_representation(value)
for key, value in attribute.items()
}
else:
# Force anything else to its string representation.
output[attribute_name] = str(attribute)
## Serializer fields
## Serializer fields
#### The `Field` and `ReadOnly` field classes.
#### The `Field` and `ReadOnly` field classes.
**TODO**
There are some minor tweaks to the field base classes.
Previously we had these two base classes:
*`Field` as the base class for read-only fields. A default implementation was included for serializing data.
*`WriteableField` as the base class for read-write fields.
We now use the following:
*`Field` is the base class for all fields. It does not include any default implementation for either serializing or deserializing data.
*`ReadOnlyField` is a concrete implementation for read-only fields that simply returns the attribute value without modification.
#### The `required`, `allow_none`, `allow_blank` and `default` arguments.
REST framework now has more explict and clear control over validating empty values for fields.
Previously the meaning of the `required=False` keyword argument was underspecified. In practice it's use meant that a field could either be not included in the input, or it could be included, but be `None`.
We now have a better seperation, with seperate `required` and `allow_none` arguments.
The following set of arguments are used to control validation of empty values:
*`required=False`: The value does not need to be present in the input, and will not be passed to `.create()` or `.update()` if it is not seen.
*`default=<value>`: The value does not need to be present in the input, and a default value will be passed to `.create()` or `.update()` if it is not seen.
*`allow_none=True`: `None` is a valid input.
*`allow_blank=True`: `''` is valid input. For `CharField` and subclasses only.
Typically you'll want to use `required=False` if the corresponding model field has a default value, and additionally set either `allow_none=True` or `allow_blank=True` if required.
The `default` argument is there if you need it, but you'll more typically want defaults to be set on model fields, rather than serializer fields.
#### Coercing output types.
#### Coercing output types.
**TODO**
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 `ListSerializer` class.
#### The `ListField` class.
**TODO**
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:
You can also use a declarative style to create new subclasses of `ListField`, like this:
class ScoresField(ListField):
child = IntegerField(min_value=0, max_value=100)
We can now use the `ScoresField` class inside another serializer:
scores = ScoresField()
See also the new `ListSerializer` class, which validates input in the same way, but also includes the serializer interfaces of `.is_valid()`, `.data`, `.save()` and so on.
#### The `ChoiceField` class may now accept a flat list.
The `ChoiceField` class may now accept a list of choices in addition to the existing style of using a list of pairs of `(name, display_value)`. The following is now valid:
color = ChoiceField(choices=['red', 'green', 'blue'])
#### The `MultipleChoiceField` class.
#### The `MultipleChoiceField` class.
**TODO**
The `MultipleChoiceField` class has been added. This field acts like `ChoiceField`, but returns a set, which may include none, one or many of the valid choices.
#### Changes to the custom field API.
#### Changes to the custom field API.
**TODO**`to_representation`, `to_internal_value`.
The `from_native(self, value)` and `to_native(self, data)` method names have been replaced with the more obviously named `to_representation(self, value)` and `to_internal_value(self, data)`.
#### Explicit `querysets` required on relational fields.
The `field_from_native()` and `field_to_native()` methods are removed.
**TODO**
#### Explicit `queryset` required on relational fields.
Previously relational fields that were explicitly declared on a serializer class could omit the queryset argument if (and only if) they were declared on a `ModelSerializer`.
This code *would be valid* in `2.4.3`:
class AccountSerializer(serializers.ModelSerializer):
In order to ensure a consistent code style an assertion error will be raised if you include a redundant method name argument that matches the default method name. For example, the following code *will raise an error*:
I've see several codebases that unneccessarily include the `source` argument, setting it to the same value as the field name. This usage is redundant and confusing, making it less obvious that `source` is usually not required.
The following usage will *now raise an error*:
email = serializers.EmailField(source='email')
## Generic views
## Generic views
#### Simplification of view logic.
#### Simplification of view logic.
**TODO**
The view logic for the default method handlers has been significantly simplified, due to the new serializers API.
#### Removal of pre/post save hooks.
#### Removal of pre/post save hooks.
...
@@ -169,6 +443,20 @@ I would personally recommend that developers treat view instances as immutable o
...
@@ -169,6 +443,20 @@ I would personally recommend that developers treat view instances as immutable o
#### PUT as create.
#### PUT as create.
Allowing `PUT` as create operations is problematic, as it neccessarily exposes information about the existence or non-existance of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is neccessarily a better default behavior than simply returning `404` responses.
Both styles "`PUT` as 404" and "`PUT` as create" can be valid in different circumstances, but we've now opted for the 404 behavior as the default, due to it being simpler and more obvious.
If you need to restore the previous behavior you can include the `AllowPUTAsCreateMixin` class in your view. This class can be imported from `rest_framework.mixins`.
#### Customizing error responses.
The generic views now raise `ValidationError` for invalid data. This exception is then dealt with by the exception handler, rather than the view returning a `400 Bad Request` response directly.
This change means that you can now easily cusomize the style of error responses across your entire API, without having to modify any of the generic views.
## The metadata API
**TODO**
**TODO**
## API style
## API style
...
@@ -241,3 +529,17 @@ Or modify it on an individual serializer field, using the `corece_to_string` key
...
@@ -241,3 +529,17 @@ Or modify it on an individual serializer field, using the `corece_to_string` key
)
)
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 uncoerced `Decimal` instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.
## What's coming next.
3.0 is an incremental release, and there are several upcoming features that will build on the baseline improvements that it makes.
The 3.1 release is planned to address improvements in the following components:
* Request parsing, mediatypes & the implementation of the browsable API.
* Introduction of a new pagination API.
* Better support for API versioning.
The 3.2 release is planned to introduce an alternative admin-style interface to the browsable API.
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).