From b9503cd603613e4ae72b7718ba70a00b1537b289 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Dec 2014 10:48:45 +0000 Subject: [PATCH] Support Django's core ValidationError for backwards compat. Refs #2145. --- docs/index.md | 2 +- docs/topics/3.0-announcement.md | 42 ++++++++++++++++++++++++++++----- rest_framework/serializers.py | 11 +++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/docs/index.md b/docs/index.md index 10b45584c..7631be1e2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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. -For more details see the [3.0 release notes](3.0-announcement). +For more details see the [3.0 release notes][3.0-announcement]. --- diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md index b32fe5102..72fee0193 100644 --- a/docs/topics/3.0-announcement.md +++ b/docs/topics/3.0-announcement.md @@ -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. -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 @@ -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`. +--- + ## Serializers #### 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. -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. @@ -218,7 +222,18 @@ If you absolutely need to preserve `transform_` 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. -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. @@ -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. output[attribute_name] = str(attribute) +--- + ## Serializer fields #### 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. +--- + ## Generic views #### 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. +--- + ## 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. 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 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. +--- + ## API style 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. -## 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. * 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. @@ -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). +[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3 [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 diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f7aa3a7dc..de0d026da 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -11,6 +11,7 @@ python primitives. response content is handled by parsers and renderers. """ from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import ValidationError as DjangoValidationError from django.db import models from django.db.models.fields import FieldDoesNotExist from django.utils import six @@ -330,6 +331,14 @@ class Serializer(BaseSerializer): raise ValidationError({ 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 @@ -353,6 +362,8 @@ class Serializer(BaseSerializer): validated_value = validate_method(validated_value) except ValidationError as exc: errors[field.field_name] = exc.detail + except DjangoValidationError as exc: + errors[field.field_name] = list(exc.messages) except SkipField: pass else: