Dynamic fields in Django Rest Framwork serializers

(Comments)

Often we need to have custom fields that must be calculated dynamically. In Entity-Relationship model notation the fields are called Derived Attributes. Such fields are derived from other fields of the same entity (model) and they might change over time (age, for example). We can create dynamic fields in django in more than one way.

1. Dynamic field as a model property

Let us create a model Author as follows.

from django.db import models
from django.utils import timezone


class Author(models.Model):
    name = models.CharField(max_length=40)
    dob = models.DateField(verbose_name='Date of birth')

We need a field that gives us the age. An obvious choice for this is to create a method that returns the age calculated based on the dob field value. But Python has the property decorator that allows us to access a class methods as if it is a class attribute. It also allows us to write setter and getter for the attribute, but our case is simple and we can use it to create a read-only property that can act as a field.

Add the following method along with the decorator.

@property
def age(self):
    return timezone.now().year - self.dob.year

Finally the model looks like this.

from django.db import models
from django.utils import timezone


class Author(models.Model):
    name = models.CharField(max_length=40)
    dob = models.DateField(verbose_name='Date of birth')

    @property
    def age(self):
        return timezone.now().year - self.dob.year

This property (method) will be treated as if it is a field of a model by Django and Django Rest Framework serializer. Create the following serializer for the model.

from rest_framework import serializers
from .models import Post, Author

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ('id', 'name', 'dob', 'age')

Note that age field has to be explicitly mentioned. If fields is set to '__all__' string, the serializer does not include the age field.

Author serializer

2. Using SerializerMethodField

Django Rest Framework gives us the SerializerMethodField field whose value is dynamically evaluated by the serializer itself and not the model. Using this method has an advantage because the value can be generated in the context of the current session.

Let us revert back to the original Author model that does not include the age property.

class Author(models.Model):
    name = models.CharField(max_length=40)
    dob = models.DateField(verbose_name='Date of birth')

We use the serializer to calculate the age and pass it as a serializer field.

class AuthorSerializer(serializers.ModelSerializer):
    age = serializers.SerializerMethodField()

    class Meta:
        model = Author
        fields = '__all__'

    def get_age(self, instance):
        return datetime.datetime.now().year - instance.dob.year

Any SerializerMethodField will look for a get_<field_name> method and use it as source. This is equivalent to our previous procedure.

Author serializer

We can also use the current session in the serializer. Let us change the method to show the age only to admins. Show 'hidden' for other users. Change the get_age method as to the following.

def calculate_age(self, instance):
    request = self.context.get('request')
    user = request.user
    if user.is_authenticated() and user.is_admin:
        return datetime.datetime.now().year - instance.dob.year
    return 'Hidden'

and the age field to age = serializers.SerializerMethodField(method_name='calculate_age'). The full serializer looks like this.

class AuthorSerializer(serializers.ModelSerializer):
    age = serializers.SerializerMethodField(method_name='calculate_age')

    class Meta:
        model = Author
        fields = ('id', 'name', 'dob', 'age')

    def calculate_age(self, instance):
        request = self.context.get('request')
        user = request.user
        if user.is_authenticated() and user.is_staff:
            return datetime.datetime.now().year - instance.dob.year
        return 'Hidden'

The result for any user (including AnonymousUser) is as follows.

Serializer for AnonymousUser

The result for admin looks like this.

Serializer for admin

Surely there are more ways to generate dynamic fields, using annotations for example. These are the simplest ways to achieve this at the application level. Hope this helps.

Comments

Recent Posts

Archive

2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom