Skip to content

Commit b9503cd

Browse files
committed
Support Django's core ValidationError for backwards compat. Refs #2145.
1 parent 72c4ec4 commit b9503cd

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
**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.
1313

14-
For more details see the [3.0 release notes](3.0-announcement).
14+
For more details see the [3.0 release notes][3.0-announcement].
1515

1616
---
1717

docs/topics/3.0-announcement.md

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ This release is incremental in nature. There *are* some breaking API changes, an
66

77
The difference in quality of the REST framework API and implementation should make writing, maintaining and debugging your application far easier.
88

9-
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).
9+
3.0 is the first of three releases that have been funded by our recent [Kickstarter campaign][kickstarter].
1010

11-
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.
11+
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.
12+
13+
---
1214

1315
## New features
1416

@@ -51,6 +53,8 @@ Instead of passing the files argument separately:
5153

5254
The usage of `request.QUERY_PARAMS` is now pending deprecation in favor of the lowercased `request.query_params`.
5355

56+
---
57+
5458
## Serializers
5559

5660
#### Single-step object creation.
@@ -149,7 +153,7 @@ Previously `serializers.ValidationError` error was simply a synonym for `django.
149153

150154
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.
151155

152-
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.
156+
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.
153157

154158
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.
155159

@@ -218,7 +222,18 @@ If you absolutely need to preserve `transform_<field_name>` behavior, for exampl
218222

219223
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.
220224

221-
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.
225+
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.
226+
227+
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.
228+
229+
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.
230+
231+
def validate(self, attrs):
232+
instance = ExampleModel(**attrs)
233+
instance.clean()
234+
return attrs
235+
236+
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.
222237

223238
#### Writable nested serialization.
224239

@@ -524,6 +539,8 @@ The following class is an example of a generic serializer that can handle coerci
524539
# Force anything else to its string representation.
525540
output[attribute_name] = str(attribute)
526541

542+
---
543+
527544
## Serializer fields
528545

529546
#### The `Field` and `ReadOnly` field classes.
@@ -721,6 +738,8 @@ REST framework also now includes explicit validator classes for validating the `
721738

722739
These classes are documented in the [Validators](../api-guide/validators.md) section of the documentation.
723740

741+
---
742+
724743
## Generic views
725744

726745
#### Simplification of view logic.
@@ -769,12 +788,16 @@ The generic views now raise `ValidationFailed` exception for invalid data. This
769788

770789
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.
771790

791+
---
792+
772793
## The metadata API
773794

774795
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.
775796

776797
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.
777798

799+
---
800+
778801
## Serializers as HTML forms
779802

780803
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
806829

807830
This API should be considered provisional, and there may be minor alterations with the incoming 3.1 release.
808831

832+
---
833+
809834
## API style
810835

811836
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
899924

900925
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.
901926

902-
## Miscellaneous notes.
927+
---
928+
929+
## Miscellaneous notes
903930

904931
* 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.
905932
* 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.
906933

907-
## What's coming next.
934+
---
935+
936+
## What's coming next
908937

909938
3.0 is an incremental release, and there are several upcoming features that will build on the baseline improvements that it makes.
910939

@@ -919,5 +948,6 @@ The 3.2 release is planned to introduce an alternative admin-style interface to
919948

920949
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).
921950

951+
[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3
922952
[sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
923953
[mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py

rest_framework/serializers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
response content is handled by parsers and renderers.
1212
"""
1313
from django.core.exceptions import ImproperlyConfigured
14+
from django.core.exceptions import ValidationError as DjangoValidationError
1415
from django.db import models
1516
from django.db.models.fields import FieldDoesNotExist
1617
from django.utils import six
@@ -330,6 +331,14 @@ def run_validation(self, data=empty):
330331
raise ValidationError({
331332
api_settings.NON_FIELD_ERRORS_KEY: [exc.detail]
332333
})
334+
except DjangoValidationError as exc:
335+
# Normally you should raise `serializers.ValidationError`
336+
# inside your codebase, but we handle Django's validation
337+
# exception class as well for simpler compat.
338+
# Eg. Calling Model.clean() explictily inside Serializer.validate()
339+
raise ValidationError({
340+
api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages)
341+
})
333342

334343
return value
335344

@@ -353,6 +362,8 @@ def to_internal_value(self, data):
353362
validated_value = validate_method(validated_value)
354363
except ValidationError as exc:
355364
errors[field.field_name] = exc.detail
365+
except DjangoValidationError as exc:
366+
errors[field.field_name] = list(exc.messages)
356367
except SkipField:
357368
pass
358369
else:

0 commit comments

Comments
 (0)