Commit 0a91999d by Tymur Maryokhin

Merge branch 'master' of github.com:tomchristie/django-rest-framework

parents e2ea98e8 d1fe61ce
<a class="github" href="validators.py"></a> source: validators.py
--- ---
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
**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].
--- ---
<p> <p>
...@@ -201,6 +203,7 @@ General guides to using REST framework. ...@@ -201,6 +203,7 @@ General guides to using REST framework.
* [2.2 Announcement][2.2-announcement] * [2.2 Announcement][2.2-announcement]
* [2.3 Announcement][2.3-announcement] * [2.3 Announcement][2.3-announcement]
* [2.4 Announcement][2.4-announcement] * [2.4 Announcement][2.4-announcement]
* [3.0 Announcement][3.0-announcement]
* [Kickstarter Announcement][kickstarter-announcement] * [Kickstarter Announcement][kickstarter-announcement]
* [Release Notes][release-notes] * [Release Notes][release-notes]
* [Credits][credits] * [Credits][credits]
...@@ -319,6 +322,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -319,6 +322,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[2.2-announcement]: topics/2.2-announcement.md [2.2-announcement]: topics/2.2-announcement.md
[2.3-announcement]: topics/2.3-announcement.md [2.3-announcement]: topics/2.3-announcement.md
[2.4-announcement]: topics/2.4-announcement.md [2.4-announcement]: topics/2.4-announcement.md
[3.0-announcement]: topics/3.0-announcement.md
[kickstarter-announcement]: topics/kickstarter-announcement.md [kickstarter-announcement]: topics/kickstarter-announcement.md
[release-notes]: topics/release-notes.md [release-notes]: topics/release-notes.md
[credits]: topics/credits.md [credits]: topics/credits.md
......
...@@ -50,6 +50,7 @@ pages: ...@@ -50,6 +50,7 @@ pages:
- ['topics/2.2-announcement.md', 'Topics', '2.2 Announcement'] - ['topics/2.2-announcement.md', 'Topics', '2.2 Announcement']
- ['topics/2.3-announcement.md', 'Topics', '2.3 Announcement'] - ['topics/2.3-announcement.md', 'Topics', '2.3 Announcement']
- ['topics/2.4-announcement.md', 'Topics', '2.4 Announcement'] - ['topics/2.4-announcement.md', 'Topics', '2.4 Announcement']
- ['topics/3.0-announcement.md', 'Topics', '3.0 Announcement']
- ['topics/kickstarter-announcement.md', 'Topics', 'Kickstarter Announcement'] - ['topics/kickstarter-announcement.md', 'Topics', 'Kickstarter Announcement']
- ['topics/release-notes.md', 'Topics', 'Release Notes'] - ['topics/release-notes.md', 'Topics', 'Release Notes']
- ['topics/credits.md', 'Topics', 'Credits'] - ['topics/credits.md', 'Topics', 'Credits']
...@@ -11,6 +11,7 @@ python primitives. ...@@ -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): ...@@ -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): ...@@ -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:
...@@ -554,6 +565,14 @@ class ModelSerializer(Serializer): ...@@ -554,6 +565,14 @@ class ModelSerializer(Serializer):
* A set of default fields are automatically populated. * A set of default fields are automatically populated.
* A set of default validators are automatically populated. * A set of default validators are automatically populated.
* Default `.create()` and `.update()` implementations are provided. * Default `.create()` and `.update()` implementations are provided.
The process of automatically determining a set of serializer fields
based on the model fields is reasonably complex, but you almost certainly
don't need to dig into the implemention.
If the `ModelSerializer` class *doesn't* generate the set of fields that
you need you should either declare the extra/differing fields explicitly on
the serializer class, or simply use a `Serializer` class.
""" """
_field_mapping = ClassLookupDict({ _field_mapping = ClassLookupDict({
models.AutoField: IntegerField, models.AutoField: IntegerField,
...@@ -582,6 +601,26 @@ class ModelSerializer(Serializer): ...@@ -582,6 +601,26 @@ class ModelSerializer(Serializer):
_related_class = PrimaryKeyRelatedField _related_class = PrimaryKeyRelatedField
def create(self, validated_attrs): def create(self, validated_attrs):
"""
We have a bit of extra checking around this in order to provide
descriptive messages when something goes wrong, but this method is
essentially just:
return ExampleModel.objects.create(**validated_attrs)
If there are many to many fields present on the instance then they
cannot be set until the model is instantiated, in which case the
implementation is like so:
example_relationship = validated_attrs.pop('example_relationship')
instance = ExampleModel.objects.create(**validated_attrs)
instance.example_relationship = example_relationship
return instance
The default implementation also does not handle nested relationships.
If you want to support writable nested relationships you'll need
to write an explicit `.create()` method.
"""
# Check that the user isn't trying to handle a writable nested field. # Check that the user isn't trying to handle a writable nested field.
# If we don't do this explicitly they'd likely get a confusing # If we don't do this explicitly they'd likely get a confusing
# error at the point of calling `Model.objects.create()`. # error at the point of calling `Model.objects.create()`.
...@@ -632,14 +671,19 @@ class ModelSerializer(Serializer): ...@@ -632,14 +671,19 @@ class ModelSerializer(Serializer):
return instance return instance
def get_validators(self): def get_validators(self):
# If the validators have been declared explicitly then use that.
validators = getattr(getattr(self, 'Meta', None), 'validators', None)
if validators is not None:
return validators
# Determine the default set of validators.
validators = []
model_class = self.Meta.model
field_names = set([ field_names = set([
field.source for field in self.fields.values() field.source for field in self.fields.values()
if (field.source != '*') and ('.' not in field.source) if (field.source != '*') and ('.' not in field.source)
]) ])
validators = getattr(getattr(self, 'Meta', None), 'validators', [])
model_class = self.Meta.model
# Note that we make sure to check `unique_together` both on the # Note that we make sure to check `unique_together` both on the
# base model class, but also on any parent classes. # base model class, but also on any parent classes.
for parent_class in [model_class] + list(model_class._meta.parents.keys()): for parent_class in [model_class] + list(model_class._meta.parents.keys()):
......
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