@@ -561,6 +561,64 @@ def errors(self):
561561# ModelSerializer & HyperlinkedModelSerializer
562562# --------------------------------------------
563563
564+ def raise_errors_on_nested_writes (method_name , serializer ):
565+ """
566+ Give explicit errors when users attempt to pass writable nested data.
567+
568+ If we don't do this explicitly they'd get a less helpful error when
569+ calling `.save()` on the serializer.
570+
571+ We don't *automatically* support these sorts of nested writes brecause
572+ there are too many ambiguities to define a default behavior.
573+
574+ Eg. Suppose we have a `UserSerializer` with a nested profile. How should
575+ we handle the case of an update, where the `profile` realtionship does
576+ not exist? Any of the following might be valid:
577+
578+ * Raise an application error.
579+ * Silently ignore the nested part of the update.
580+ * Automatically create a profile instance.
581+ """
582+
583+ # Ensure we don't have a writable nested field. For example:
584+ #
585+ # class UserSerializer(ModelSerializer):
586+ # ...
587+ # profile = ProfileSerializer()
588+ assert not any (
589+ isinstance (field , BaseSerializer ) and (key in validated_attrs )
590+ for key , field in serializer .fields .items ()
591+ ), (
592+ 'The `.{method_name}()` method does not support writable nested'
593+ 'fields by default.\n Write an explicit `.{method_name}()` method for '
594+ 'serializer `{module}.{class_name}`, or set `read_only=True` on '
595+ 'nested serializer fields.' .format (
596+ method_name = method_name ,
597+ module = serializer .__class__ .__module__ ,
598+ class_name = serializer .__class__ .__name__
599+ )
600+ )
601+
602+ # Ensure we don't have a writable dotted-source field. For example:
603+ #
604+ # class UserSerializer(ModelSerializer):
605+ # ...
606+ # address = serializer.CharField('profile.address')
607+ assert not any (
608+ '.' in field .source and (key in validated_attrs )
609+ for key , field in serializer .fields .items ()
610+ ), (
611+ 'The `.{method_name}()` method does not support writable dotted-source '
612+ 'fields by default.\n Write an explicit `.{method_name}()` method for '
613+ 'serializer `{module}.{class_name}`, or set `read_only=True` on '
614+ 'dotted-source serializer fields.' .format (
615+ method_name = method_name ,
616+ module = serializer .__class__ .__module__ ,
617+ class_name = serializer .__class__ .__name__
618+ )
619+ )
620+
621+
564622class ModelSerializer (Serializer ):
565623 """
566624 A `ModelSerializer` is just a regular `Serializer`, except that:
@@ -624,18 +682,7 @@ def create(self, validated_data):
624682 If you want to support writable nested relationships you'll need
625683 to write an explicit `.create()` method.
626684 """
627- # Check that the user isn't trying to handle a writable nested field.
628- # If we don't do this explicitly they'd likely get a confusing
629- # error at the point of calling `Model.objects.create()`.
630- assert not any (
631- isinstance (field , BaseSerializer ) and (key in validated_attrs )
632- for key , field in self .fields .items ()
633- ), (
634- 'The `.create()` method does not support nested writable fields '
635- 'by default. Write an explicit `.create()` method for serializer '
636- '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
637- (self .__class__ .__module__ , self .__class__ .__name__ )
638- )
685+ raise_errors_on_nested_writes ('create' , self )
639686
640687 ModelClass = self .Meta .model
641688
@@ -675,19 +722,12 @@ def create(self, validated_data):
675722 return instance
676723
677724 def update (self , instance , validated_data ):
678- assert not any (
679- isinstance (field , BaseSerializer ) and (key in validated_attrs )
680- for key , field in self .fields .items ()
681- ), (
682- 'The `.update()` method does not support nested writable fields '
683- 'by default. Write an explicit `.update()` method for serializer '
684- '`%s.%s`, or set `read_only=True` on nested serializer fields.' %
685- (self .__class__ .__module__ , self .__class__ .__name__ )
686- )
725+ raise_errors_on_nested_writes ('update' , self )
687726
688727 for attr , value in validated_data .items ():
689728 setattr (instance , attr , value )
690729 instance .save ()
730+
691731 return instance
692732
693733 def get_validators (self ):
0 commit comments