@@ -685,8 +685,9 @@ the coordinate pair:
685685 fields = ['label', 'coordinates']
686686
687687Note 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+ with using` source='*' ` , with two` IntegerField ` instances, each with their own` source `
690+ pointing to the relevant field.
690691
691692The key points from the example, though, are:
692693
@@ -717,6 +718,67 @@ suitable for updating our target object. With `source='*'`, the return from
717718 ('y_coordinate', 4),
718719 ('x_coordinate', 3)])
719720
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+
720782#Third party packages
721783
722784The following third party packages are also available.