@@ -561,6 +561,64 @@ def errors(self):
561
561
# ModelSerializer & HyperlinkedModelSerializer
562
562
# --------------------------------------------
563
563
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 nested writable '
593
+ 'fields by default. 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. 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
+
564
622
class ModelSerializer (Serializer ):
565
623
"""
566
624
A `ModelSerializer` is just a regular `Serializer`, except that:
@@ -624,18 +682,7 @@ def create(self, validated_data):
624
682
If you want to support writable nested relationships you'll need
625
683
to write an explicit `.create()` method.
626
684
"""
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 )
639
686
640
687
ModelClass = self .Meta .model
641
688
@@ -675,15 +722,7 @@ def create(self, validated_data):
675
722
return instance
676
723
677
724
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 )
687
726
688
727
for attr , value in validated_data .items ():
689
728
setattr (instance , attr , value )
0 commit comments