Support Django's core ValidationError for backwards compat. Refs #2145.

This commit is contained in:
Tom Christie 2014-12-01 10:48:45 +00:00
parent 72c4ec4e18
commit b9503cd603
3 changed files with 48 additions and 7 deletions

View File

@ -11,7 +11,7 @@
**Note**: This is the documentation for the **version 3.0** of REST framework. Documentation for [version 2.4](http://tomchristie.github.io/rest-framework-2-docs/) is also available. **Note**: This is the documentation for the **version 3.0** of REST framework. Documentation for [version 2.4](http://tomchristie.github.io/rest-framework-2-docs/) is also available.
For more details see the [3.0 release notes](3.0-announcement). For more details see the [3.0 release notes][3.0-announcement].
--- ---

View File

@ -6,9 +6,11 @@ This release is incremental in nature. There *are* some breaking API changes, an
The difference in quality of the REST framework API and implementation should make writing, maintaining and debugging your application far easier. The difference in quality of the REST framework API and implementation should make writing, maintaining and debugging your application far easier.
3.0 is the first of three releases that have been funded by our recent [Kickstarter campaign](kickstarter.com/projects/tomchristie/django-rest-framework-3). 3.0 is the first of three releases that have been funded by our recent [Kickstarter campaign][kickstarter].
As ever, a huge thank you to our many [wonderful sponsors](sponsors). If you're looking for a Django gig, and want to work with smart community-minded folks, you should probably check out that list and see who's hiring. As ever, a huge thank you to our many [wonderful sponsors][sponsors]. If you're looking for a Django gig, and want to work with smart community-minded folks, you should probably check out that list and see who's hiring.
---
## New features ## New features
@ -51,6 +53,8 @@ Instead of passing the files argument separately:
The usage of `request.QUERY_PARAMS` is now pending deprecation in favor of the lowercased `request.query_params`. The usage of `request.QUERY_PARAMS` is now pending deprecation in favor of the lowercased `request.query_params`.
---
## Serializers ## Serializers
#### Single-step object creation. #### Single-step object creation.
@ -149,7 +153,7 @@ Previously `serializers.ValidationError` error was simply a synonym for `django.
The reason behind this is that Django's `ValidationError` class is intended for use with HTML forms and its API makes using it slightly awkward with nested validation errors that can occur in serializers. The reason behind this is that Django's `ValidationError` class is intended for use with HTML forms and its API makes using it slightly awkward with nested validation errors that can occur in serializers.
For most users this change shouldn't require any updates to your codebase, but it is worth ensuring that whenever raising validation errors you are always using the `serializers.ValidationError` exception class, and not Django's built-in exception. For most users this change shouldn't require any updates to your codebase, but it is worth ensuring that whenever raising validation errors you should prefer using the `serializers.ValidationError` exception class, and not Django's built-in exception.
We strongly recommend that you use the namespaced import style of `import serializers` and not `from serializers import ValidationError` in order to avoid any potential confusion. We strongly recommend that you use the namespaced import style of `import serializers` and not `from serializers import ValidationError` in order to avoid any potential confusion.
@ -218,7 +222,18 @@ If you absolutely need to preserve `transform_<field_name>` behavior, for exampl
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.
It's important to note that this change also means that the model `.clean()` method will not be called as part of serializer validation, as it would be if using a `ModelForm`. Use the serializer `.validate()` method to perform a final validation step on incoming data where required. For the most part this change should be transparent. Field validation and uniqueness checks will still be run as normal, but the implementation is a little different.
The one difference that you do need to note is that the `.clean()` method will not be called as part of serializer validation, as it would be if using a `ModelForm`. Use the serializer `.validate()` method to perform a final validation step on incoming data where required.
There may be some cases where you really do need to keep validation logic in the model `.clean()` method, and cannot instead separate it into the serializer `.validate()`. You can do so by explicitly instantiating a model instance in the `.validate()` method.
def validate(self, attrs):
instance = ExampleModel(**attrs)
instance.clean()
return attrs
Again, you really should look at properly separating the validation logic out of the model method if possible, but the above might be useful in some backwards compatibility cases, or for an easy migration path.
#### Writable nested serialization. #### Writable nested serialization.
@ -524,6 +539,8 @@ The following class is an example of a generic serializer that can handle coerci
# Force anything else to its string representation. # Force anything else to its string representation.
output[attribute_name] = str(attribute) output[attribute_name] = str(attribute)
---
## Serializer fields ## Serializer fields
#### The `Field` and `ReadOnly` field classes. #### The `Field` and `ReadOnly` field classes.
@ -721,6 +738,8 @@ REST framework also now includes explicit validator classes for validating the `
These classes are documented in the [Validators](../api-guide/validators.md) section of the documentation. These classes are documented in the [Validators](../api-guide/validators.md) section of the documentation.
---
## Generic views ## Generic views
#### Simplification of view logic. #### Simplification of view logic.
@ -769,12 +788,16 @@ The generic views now raise `ValidationFailed` exception for invalid data. This
This change means that you can now easily customize the style of error responses across your entire API, without having to modify any of the generic views. This change means that you can now easily customize the style of error responses across your entire API, without having to modify any of the generic views.
---
## The metadata API ## The metadata API
Behavior for dealing with `OPTIONS` requests was previously built directly into the class based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework. Behavior for dealing with `OPTIONS` requests was previously built directly into the class based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework.
This makes it far easier to use a different style for `OPTIONS` responses throughout your API, and makes it possible to create third-party metadata policies. This makes it far easier to use a different style for `OPTIONS` responses throughout your API, and makes it possible to create third-party metadata policies.
---
## Serializers as HTML forms ## Serializers as HTML forms
REST framework 3.0 includes templated HTML form rendering for serializers. REST framework 3.0 includes templated HTML form rendering for serializers.
@ -806,6 +829,8 @@ Similarly, to use a radio button control instead of the default `select` control
This API should be considered provisional, and there may be minor alterations with the incoming 3.1 release. This API should be considered provisional, and there may be minor alterations with the incoming 3.1 release.
---
## API style ## API style
There are some improvements in the default style we use in our API responses. There are some improvements in the default style we use in our API responses.
@ -899,12 +924,16 @@ Or modify it on an individual serializer field, using the `coerce_to_string` key
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. 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
* The serializer `ChoiceField` does not currently display nested choices, as was the case in 2.4. This will be address as part of 3.1. * The serializer `ChoiceField` does not currently display nested choices, as was the case in 2.4. This will be address as part of 3.1.
* Due to the new templated form rendering, the 'widget' option is no longer valid. This means there's no easy way of using third party "autocomplete" widgets for rendering select inputs that contain a large number of choices. You'll either need to use a regular select or a plain text input. We may consider addressing this in 3.1 or 3.2 if there's sufficient demand. * Due to the new templated form rendering, the 'widget' option is no longer valid. This means there's no easy way of using third party "autocomplete" widgets for rendering select inputs that contain a large number of choices. You'll either need to use a regular select or a plain text input. We may consider addressing this in 3.1 or 3.2 if there's sufficient demand.
## What's coming next. ---
## 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. 3.0 is an incremental release, and there are several upcoming features that will build on the baseline improvements that it makes.
@ -919,5 +948,6 @@ The 3.2 release is planned to introduce an alternative admin-style interface to
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones). You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).
[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3
[sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors [sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
[mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py [mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py

View File

@ -11,6 +11,7 @@ python primitives.
response content is handled by parsers and renderers. response content is handled by parsers and renderers.
""" """
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import models from django.db import models
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
from django.utils import six from django.utils import six
@ -330,6 +331,14 @@ class Serializer(BaseSerializer):
raise ValidationError({ raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [exc.detail] api_settings.NON_FIELD_ERRORS_KEY: [exc.detail]
}) })
except DjangoValidationError as exc:
# Normally you should raise `serializers.ValidationError`
# inside your codebase, but we handle Django's validation
# exception class as well for simpler compat.
# Eg. Calling Model.clean() explictily inside Serializer.validate()
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages)
})
return value return value
@ -353,6 +362,8 @@ class Serializer(BaseSerializer):
validated_value = validate_method(validated_value) validated_value = validate_method(validated_value)
except ValidationError as exc: except ValidationError as exc:
errors[field.field_name] = exc.detail errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = list(exc.messages)
except SkipField: except SkipField:
pass pass
else: else: