Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest framework one to one relation

Tags:

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.

like image 263
Ansuman Bebarta Avatar asked Aug 07 '15 13:08

Ansuman Bebarta


People also ask

How do I create a one to many relationship in Django?

One to many relationships in Django models. To define a one to many relationship in Django models you use the ForeignKey data type on the model that has the many records (e.g. on the Item model). Listing 7-22 illustrates a sample of a one to many Django relationship.

What is a one to one field Django?

This is used when one record of a model A is related to exactly one record of another model B. This field can be useful as a primary key of an object if that object extends another object in some way. For example – a model Car has one-to-one relationship with a model Vehicle, i.e. a car is a vehicle.

Why serializers are used in Django?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.


1 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 !

like image 98
ddelemeny Avatar answered Nov 09 '22 04:11

ddelemeny