Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest framework serializer with reverse relation

I have two models where employee have relation with person model but person have no relation with employee model.

Like:

class Person(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)

class Employee(models.Model):
    person = models.ForeignKey(Person, related_name='person_info')
    code = models.CharField()

In such cases I want code field data in person serializer.


I can solved this with writing method in person model or using SerializerMethodField in person serializer

like this:

def get_employee_code(self):
    return Employee.objects.get(person=self).id

and add this as source in person serializer

employee_code = serializers.CharField(source='get_employee_code')

Or adding employee serializer into person serialiszer

class PersonSerializer(serializers.ModelSerializer):
    employee = EmployeeSerializer()
    class Meta:
        model = Person
        fields = ('name', 'address', 'employee')

But i was trying to do this with reverse relation but i can't. I have tried like this, it gives an error

Serializer:

class PersonSerializer(serializers.ModelSerializer):
    employee_code = serializers.CharField(source='person_info.code')
    class Meta:
        model = Person
        fields = ('name', 'address', 'employee_code')

How can i solve this with reverse relation?

like image 324
frfahim Avatar asked Mar 06 '23 14:03

frfahim


1 Answers

At the moment because you are using a ForeignKey field on the person attribute, it means that its returning a list when you access the reverse relation.

One solution would be to use a slug related field, though this must have many and read_only set to True, and will return a list because of the ForeignKey field.

class PersonSerializer(serializers.ModelSerializer):
    employee_code = serializers.SlugRelatedField(
        source='person_info',
        slug_field='code',
        many=True,
        read_only=True,
    )

    class Meta:
        model = Person
        fields = ('name', 'address', 'employee_code')

The other option is to change your ForeignKey into a OneToOneField, which would still need read_only set to True but it will not return a list.

class Person(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)

class Employee(models.Model):
    person = models.OneToOneField(Person, related_name='person_info')
    code = models.CharField()

class PersonSerializer(serializers.ModelSerializer):
    employee_code = serializers.SlugRelatedField(
        source='person_info',
        slug_field='code',
        read_only=True,
    )

    class Meta:
        model = Person
        fields = ('name', 'address', 'employee_code')

Or, if you don't want to change the ForeignKey, you could add a employee_code property method to the model instead to return the first employee code in the person_info relation.

class Person(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)

    @property
    def employee_code(self):
        employees = self.person_info.filter()
        if employees.exists():
            return employees.first().code
        return ''

class Employee(models.Model):
    person = models.OneToOneField(Person, related_name='person_info')
    code = models.CharField()

class PersonSerializer(serializers.ModelSerializer):
    employee_code = serializers.CharField(
        read_only=True,
    )

    class Meta:
        model = Person
        fields = ('name', 'address', 'employee_code')
like image 91
A. J. Parr Avatar answered Mar 16 '23 19:03

A. J. Parr