@@ -685,8 +685,9 @@ the coordinate pair:
685
685
fields = ['label', 'coordinates']
686
686
687
687
Note that this example doesn't handle validation. Partly for that reason, in a
688
- real project, the coordinate nesting might be better handled with a nested serialiser using two
689
- ` IntegerField ` instances, each with ` source='*' ` .
688
+ real project, the coordinate nesting might be better handled with a nested serialiser
689
+ using ` source='*' ` , with two ` IntegerField ` instances, each with their own ` source `
690
+ pointing to the relevant field.
690
691
691
692
The key points from the example, though, are:
692
693
@@ -717,6 +718,67 @@ suitable for updating our target object. With `source='*'`, the return from
717
718
('y_coordinate', 4),
718
719
('x_coordinate', 3)])
719
720
721
+ For completeness lets do the same thing again but with the nested serialiser
722
+ approach suggested above:
723
+
724
+ class NestedCoordinateSerializer(serializers.Serializer):
725
+ x = serializers.IntegerField(source='x_coordinate')
726
+ y = serializers.IntegerField(source='y_coordinate')
727
+
728
+
729
+ class DataPointSerializer(serializers.ModelSerializer):
730
+ coordinates = NestedCoordinateSerializer(source='*')
731
+
732
+ class Meta:
733
+ model = DataPoint
734
+ fields = ['label', 'coordinates']
735
+
736
+ Here the mapping between the target and source attribute pairs (` x ` and
737
+ ` x_coordinate ` , ` y ` and ` y_coordinate ` ) is handled in the ` IntegerField `
738
+ declarations. It's our ` NestedCoordinateSerializer ` that takes ` source='*' ` .
739
+
740
+ Our new ` DataPointSerializer ` exhibits the same behaviour as the custom field
741
+ approach.
742
+
743
+ Serialising:
744
+
745
+ >>> out_serializer = DataPointSerializer(instance)
746
+ >>> out_serializer.data
747
+ ReturnDict([('label', 'testing'),
748
+ ('coordinates', OrderedDict([('x', 1), ('y', 2)]))])
749
+
750
+ Deserialising:
751
+
752
+ >>> in_serializer = DataPointSerializer(data=data)
753
+ >>> in_serializer.is_valid()
754
+ True
755
+ >>> in_serializer.validated_data
756
+ OrderedDict([('label', 'still testing'),
757
+ ('x_coordinate', 3),
758
+ ('y_coordinate', 4)])
759
+
760
+ But we also get the built-in validation for free:
761
+
762
+ >>> invalid_data = {
763
+ ... "label": "still testing",
764
+ ... "coordinates": {
765
+ ... "x": 'a',
766
+ ... "y": 'b',
767
+ ... }
768
+ ... }
769
+ >>> invalid_serializer = DataPointSerializer(data=invalid_data)
770
+ >>> invalid_serializer.is_valid()
771
+ False
772
+ >>> invalid_serializer.errors
773
+ ReturnDict([('coordinates',
774
+ {'x': ['A valid integer is required.'],
775
+ 'y': ['A valid integer is required.']})])
776
+
777
+ For this reason, the nested serialiser approach would be the first to try. You
778
+ would use the custom field approach when the nested serialiser becomes infeasible
779
+ or overly complex.
780
+
781
+
720
782
# Third party packages
721
783
722
784
The following third party packages are also available.
0 commit comments