(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.
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.
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.
The result for admin looks like this.
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.
We develop web applications to our customers using python/django/angular.
Contact us at hello@cowhite.com
Comments