From ad060aa360fa2ed33bd83cbb419d7b996a428726 Mon Sep 17 00:00:00 2001 From: Gregor Müllegger Date: Sat, 15 Nov 2014 15:23:58 +0100 Subject: More helpful error message when default `.create` fails. Closes #2013. --- rest_framework/serializers.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e7e93f38..8dafea4d 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -34,6 +34,7 @@ from rest_framework.validators import ( ) import copy import inspect +import sys import warnings # Note: We do the following so that users of the framework can use this style: @@ -593,7 +594,18 @@ class ModelSerializer(Serializer): if relation_info.to_many and (field_name in validated_attrs): many_to_many[field_name] = validated_attrs.pop(field_name) - instance = ModelClass.objects.create(**validated_attrs) + try: + instance = ModelClass.objects.create(**validated_attrs) + except TypeError as exc: + msg = ( + 'The mentioned argument might be a field on the serializer ' + 'that is not part of the model. You need to override the ' + 'create() method in your ModelSerializer subclass to support ' + 'this.') + six.reraise( + type(exc), + type(exc)(str(exc) + '. ' + msg), + sys.exc_info()[2]) # Save many-to-many relationships after the instance is created. if many_to_many: -- cgit v1.2.3 From 79e18a2a06178e8c00dfafc1cfd062f2528ec2c1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Dec 2014 09:27:40 +0000 Subject: Raise assertion error if calling .save() on a serializer with errors. Closes #2098. --- rest_framework/serializers.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 0d0a4d9a..a4140c0f 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -127,6 +127,14 @@ class BaseSerializer(Field): (self.__class__.__module__, self.__class__.__name__) ) + assert hasattr(self, '_errors'), ( + 'You must call `.is_valid()` before calling `.save()`.' + ) + + assert not self.errors, ( + 'You cannot call `.save()` on a serializer with invalid data.' + ) + validated_data = dict( list(self.validated_data.items()) + list(kwargs.items()) -- cgit v1.2.3 From 76ac641fbd6c9d7dff5da3c551c3fd1ef7dedd2e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Dec 2014 13:04:49 +0000 Subject: Minor tweaks for helpful message on Model.objects.create() failure. --- rest_framework/serializers.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 143d205d..d417ca80 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -35,7 +35,6 @@ from rest_framework.validators import ( ) import copy import inspect -import sys import warnings # Note: We do the following so that users of the framework can use this style: @@ -658,14 +657,20 @@ class ModelSerializer(Serializer): instance = ModelClass.objects.create(**validated_attrs) except TypeError as exc: msg = ( - 'The mentioned argument might be a field on the serializer ' - 'that is not part of the model. You need to override the ' - 'create() method in your ModelSerializer subclass to support ' - 'this.') - six.reraise( - type(exc), - type(exc)(str(exc) + '. ' + msg), - sys.exc_info()[2]) + 'Got a `TypeError` when calling `%s.objects.create()`. ' + 'This may be because you have a writable field on the ' + 'serializer class that is not a valid argument to ' + '`%s.objects.create()`. You may need to make the field ' + 'read-only, or override the %s.create() method to handle ' + 'this correctly.\nOriginal exception text was: %s.' % + ( + ModelClass.__name__, + ModelClass.__name__, + self.__class__.__name__, + exc + ) + ) + raise TypeError(msg) # Save many-to-many relationships after the instance is created. if many_to_many: -- cgit v1.2.3 From f2dd05a6e661525908fe5ec99b52b5274b04a198 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 3 Dec 2014 22:43:40 +0000 Subject: Improved nested update test in update(). Closes #2194. --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d417ca80..b1175b5b 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -681,8 +681,8 @@ class ModelSerializer(Serializer): def update(self, instance, validated_attrs): assert not any( - isinstance(field, BaseSerializer) and not field.read_only - for field in self.fields.values() + isinstance(field, BaseSerializer) and (key in validated_attrs) + for key, field in self.fields.values() ), ( 'The `.update()` method does not suport nested writable fields ' 'by default. Write an explicit `.update()` method for serializer ' -- cgit v1.2.3 From e1d98f77563abf49c4b19dcfb95f263515ae4087 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 3 Dec 2014 22:45:44 +0000 Subject: Improve nested update and create testing. --- rest_framework/serializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index b1175b5b..c7f04b40 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -633,8 +633,8 @@ class ModelSerializer(Serializer): # If we don't do this explicitly they'd likely get a confusing # error at the point of calling `Model.objects.create()`. assert not any( - isinstance(field, BaseSerializer) and not field.read_only - for field in self.fields.values() + isinstance(field, BaseSerializer) and (key in validated_attrs) + for key, field in self.fields.items() ), ( 'The `.create()` method does not suport nested writable fields ' 'by default. Write an explicit `.create()` method for serializer ' @@ -682,7 +682,7 @@ class ModelSerializer(Serializer): def update(self, instance, validated_attrs): assert not any( isinstance(field, BaseSerializer) and (key in validated_attrs) - for key, field in self.fields.values() + for key, field in self.fields.items() ), ( 'The `.update()` method does not suport nested writable fields ' 'by default. Write an explicit `.update()` method for serializer ' -- cgit v1.2.3 From ab25d706c78627dfd582fe9d142ada510c4d6d90 Mon Sep 17 00:00:00 2001 From: Martin Tschammer Date: Wed, 3 Dec 2014 23:52:35 +0100 Subject: Renamed validated_attrs to validated_data to be more in line with other similar code. --- rest_framework/serializers.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'rest_framework/serializers.py') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d417ca80..a289b021 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -608,20 +608,20 @@ class ModelSerializer(Serializer): }) _related_class = PrimaryKeyRelatedField - def create(self, validated_attrs): + def create(self, validated_data): """ 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) + return ExampleModel.objects.create(**validated_data) 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) + example_relationship = validated_data.pop('example_relationship') + instance = ExampleModel.objects.create(**validated_data) instance.example_relationship = example_relationship return instance @@ -644,17 +644,17 @@ class ModelSerializer(Serializer): ModelClass = self.Meta.model - # Remove many-to-many relationships from validated_attrs. + # Remove many-to-many relationships from validated_data. # They are not valid arguments to the default `.create()` method, # as they require that the instance has already been saved. info = model_meta.get_field_info(ModelClass) many_to_many = {} for field_name, relation_info in info.relations.items(): - if relation_info.to_many and (field_name in validated_attrs): - many_to_many[field_name] = validated_attrs.pop(field_name) + if relation_info.to_many and (field_name in validated_data): + many_to_many[field_name] = validated_data.pop(field_name) try: - instance = ModelClass.objects.create(**validated_attrs) + instance = ModelClass.objects.create(**validated_data) except TypeError as exc: msg = ( 'Got a `TypeError` when calling `%s.objects.create()`. ' @@ -679,7 +679,7 @@ class ModelSerializer(Serializer): return instance - def update(self, instance, validated_attrs): + def update(self, instance, validated_data): assert not any( isinstance(field, BaseSerializer) and not field.read_only for field in self.fields.values() @@ -690,7 +690,7 @@ class ModelSerializer(Serializer): (self.__class__.__module__, self.__class__.__name__) ) - for attr, value in validated_attrs.items(): + for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance -- cgit v1.2.3