Django rest framework one to one relation

I just encountered the same problem, it would indeed be useful to make the response structure less tied to the underlying model structure. Here's my take :

Reading is easy

Serializer fields have a source parameter, which can take dotted names to traverse attributes.

class ABSerializer(serializers.ModelSerializer):

    class Meta:
        model = A
        fields = ['name', 'age', 'salary']

    salary = serializer.IntegerField(source='b.salary') # this is your related_name

Writing is ... not officially supported

Validated data will show a nested structure, and the standard create and update methods will choke trying to assign a data dict to a OneToOneField. The good news is that you can work around it by overriding create and update methods. Here's an example with update :

class ABSerializer(serializers.ModelSerializer):

    class Meta:
        model = A
        fields = ['name', 'age', 'salary']
        related_fields = ['b']

    salary = serializer.IntegerField(source='b.salary') # this is your related_name

    def update(self, instance, validated_data):
        # Handle related objects
        for related_obj_name in self.Meta.related_fields:

            # Validated data will show the nested structure
            data = validated_data.pop(related_obj_name)
            related_instance = getattr(instance, related_obj_name)

            # Same as default update implementation
            for attr_name, value in data.items():
                setattr(related_instance, attr_name, value)
            related_instance.save()
        return super(ABSerializer,self).update(instance, validated_data)

Of course, this example is very simplistic, doesn't do any exception handling, and won't work with more deeply nested objects... but you get the idea.

Another option

You could also create a read-write flavor of SerializerMethodField, which would consider both a getter and a setter, however that would probably end up being far more verbose in the end.

Hope that helps !


I know this is an old post but I came across this and after some research and reading through the Django Rest Framework documentation So a quick search I found that you could use the related_name parameter for reverse relationships as stated here:

reverse relationships are not automatically included by the ModelSerializer and HyperlinkedModelSerializer classes. To include a reverse relationship, you must explicitly add it to the fields list.

For example:

    class AlbumSerializer(serializers.ModelSerializer):
        class Meta:
        fields = ['tracks', ...]

You'll normally want to ensure that you've set an appropriate related_name argument on the relationship, that you can use as the field name.

For example:

    class Track(models.Model):
        album = models.ForeignKey(Album, related_name='tracks', 
                on_delete=models.CASCADE)
     ...

If you have not set a related name for the reverse relationship, you'll need to use the automatically generated related name in the fields argument.

For example:

    class AlbumSerializer(serializers.ModelSerializer):
       
        class Meta:
            fields = ['track_set', ...]

Also, see the Django documentation on reverse relationships for more details.