Django rest framework one to one relation

Question:

So I have follwoing models:

class A(models.Model):
  name = models.CharField()
  age = models.SmallIntergerField()

class B(models.Model):
  a = models.OneToOneField(A)
  salary = model.IntergerField()

Now I want to create one rest end point for there two as they are one to one. So I want following as get

{
  url: 'http://localhost/customs/1/',
  name: 'abc',
  age: 24,
  salary: 10000
}

Similary, I want to create records and update as well. Please let me know how can I achieve this in django rest framework 3.

Asked By: Ansuman Bebarta

||

Answers:

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 !

Answered By: ddelemeny

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.

Answered By: Nseabasi etim