15

I have a model which contains sensitive data, let's say a social security number, I would like to transform that data on serialization to display only the last four digits.

I have the full social security number stored: 123-45-6789. I want my serializer output to contain: ***-**-6789

My model:

class Employee (models.Model):

    name  = models.CharField(max_length=64,null=True,blank=True)
    ssn   = models.CharField(max_length=16,null=True,blank=True)

My serializer:

class EmployeeSerializer(serializers.ModelSerializer):

    id = serializers.ReadOnlyField()

    class Meta:
        model = Employee

        fields = ('id','ssn')

        read_only_fields = ['id']

3 Answers 3

12

You can use SerializerMethodField:

class EmployeeSerializer(serializers.ModelSerializer):

    id = serializers.ReadOnlyField()
    ssn = SerializerMethodField() 

    class Meta:
        model = Employee

        fields = ('id','ssn')

        read_only_fields = ['id']

    def get_ssn(self, obj):
         return '***-**-{}'.format(obj.ssn.split('-')[-1]
Sign up to request clarification or add additional context in comments.

Comments

6

If you don't need to update the ssn, just shadow the field with a SerializerMethodField and define get_ssn(self, obj) on the serializer.

Otherwise, the most straightforward way is probably to just override .to_representation():

def to_representation(self, obj):
    data = super(EmployeeSerializer, self).to_representation(obj)
    data['ssn'] = self.mask_ssn(data['ssn'])
    return data

Please add special case handling ('ssn' in data) as necessary.

2 Comments

Where from mask_ssn is coming?
@shahriar-rahman-zahin Not shown, it must be defined on the serializer (or wherever) to perform the masking as requested. It's basically get_ssn() as above.
3

Elaborating on @dhke’s answer, if you want to be able to reuse this logic to modify serialization across multiple serializers, you can write your own field and use that as a field in your serializer, such as:

from rest_framework import serializers
from rest_framework.fields import CharField
from utils import mask_ssn


class SsnField(CharField):
    def to_representation(self, obj):
        val = super().to_representation(obj)
        return mask_ssn(val) if val else val


class EmployeeSerializer(serializers.ModelSerializer):
    ssn = SsnField()

    class Meta:
        model = Employee
        fields = ('id', 'ssn')
        read_only_fields = ['id']

You can also extend other fields like rest_framework.fields.ImageField to customize how image URLs are serialized (which can be nice if you’re using an image CDN on top of your images that lets you apply transformations to the images).

Comments