methuselah methuselah - 5 months ago 8
Python Question

Creating new fields in serializer that access that access another model's attributes

My API call to

api/business-review/3abe3a1e-199c-4a4b-9d3b-e7cb522d6bed/
currently returns the following:

[
{
"id": "3abe3a1e-199c-4a4b-9d3b-e7cb522d6bed",
"date_time": "2016-05-31T19:18:24Z",
"review": "Another quality job, Anna has a no fuss approach to his job and clearly takes pride in what he does. Will continue to use again and again.",
"rating": "4.0",
"person": "c1cc5684-1be1-4120-9d81-05aec29f352a",
"employee": "ecdc1f99-138c-4f9f-9e1f-b959d59209aa",
"service": "1dfa408f-d5bc-4eb2-96ae-e07e7999a01a",
}
]


Now I want to create three new fields:


  • person_name
    - which grabs the first_name and last_name of the reviewer

  • employee_name
    - which grabs the first_name and last_name of the employee

  • service_name
    - which grabs the title of the service



I've tried the following so far but it doesn't create any new variables:

serializers.py

class ReviewSerializer(serializers.ModelSerializer):
"""
Class to serialize Review objects
"""
person_name = serializers.CharField(source='person.reviewer.first_name', read_only=True)
employee_name = serializers.CharField(source='person.employer.first_name', read_only=True)
service_name = serializers.CharField(source='service.title', read_only=True)

class Meta:
model = Review
fields = '__all__'
read_only_fields = 'id'


models.py

class Review(models.Model):
"""
Review model
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
date_time = models.DateTimeField(blank=True, null=True, default=None)
person = models.ForeignKey(Person, null=True, default=None, related_name='reviewer')
employee = models.ForeignKey(Person, null=True, default=None, related_name='employee')
review = models.TextField(null=True, default=None)
service = models.ForeignKey(Service, null=True, default=None)
rating = models.DecimalField(max_digits=4, decimal_places=1)

class Person(models.Model):
"""
Person entity
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)

class Service(models.Model):
"""
Service model
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
title = models.CharField(max_length=255)

Answer

Since you want add data to the serialized representation of your object, its better to use SerializerMethodField() for person_name, service_name and employee_name fields.

This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.

Also, since the ForeignKey fields person, employee and service allow null values, you will have to handle the case when they are actually null. Otherwise, AttributeError exception will be raised on the serializer.

class ReviewSerializer(serializers.ModelSerializer):
    """
    Class to serialize Review objects
    """
    person_name = serializers.SerializerMethodField()
    employee_name = serializers.SerializerMethodField()
    service_name = serializers.SerializerMethodField()

    class Meta:
        model = Review
        fields = ['person', 'service', 'review', 'employee', 'person_name', 'service_name', 'employee_name', 'date_time']
        read_only_fields = 'id'

    def get_person_name(self, obj):
        try:
            person_name = obj.person.first_name + obj.person.last_name
        except AttributeError: # handle case if person is null
            return None
        else:
            return person_name

    def get_employee_name(self, obj):
        try:
            employee_name = obj.employee.first_name + obj.employee.last_name
        except AttributeError: # handle case if employee is null
            return None
        else:
            return employee_name

    def get_service_name(self, obj):
        try:
            service_name = obj.service.title
        except AttributeError: # handle case if service is null
            return None
        else:
            return service_name
Comments