Skip to content

Custom Fields

DRF covers most data with its built-in fields, but you will eventually need output that no model field produces. There are two ways to get it: SerializerMethodField for read-only computed values, and a Field subclass when the data has to convert in both directions.

SerializerMethodField

SerializerMethodField is a read-only field whose value comes from a method on the serializer. It is the simplest way to expose a model property, a computed value, or anything else that is not a concrete field.

By default it calls get_<field_name>(), passing the object being serialized. Pass method_name if you want to point it at a differently named method.

python
class ArticleSerializer(serializers.ModelSerializer):
    word_count = serializers.SerializerMethodField()
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Article
        fields = ['id', 'title', 'body', 'word_count', 'author_name']

    def get_word_count(self, obj):
        return len(obj.body.split())

    def get_author_name(self, obj):
        return obj.author.get_full_name()
json
{
    "id": 1,
    "title": "Working with Django Signals",
    "body": "Signals allow decoupled applications to ...",
    "word_count": 412,
    "author_name": "Ada Lovelace"
}

This is also how you expose the values fields = '__all__' never picks up, such as model properties, methods and GenericForeignKey.

WARNING

A SerializerMethodField is always read-only and is ignored on write. It also runs once per object, which makes N+1 queries easy to introduce: the get_author_name() method above hits the database for every article unless the view's queryset uses select_related('author').

Subclassing Field

When a field has to be written as well as read, subclass Field and override .to_representation() and .to_internal_value().

to_representation()

The .to_representation() method is called to convert the initial datatype into a primitive, serializable datatype.

to_internal_value()

The .to_internal_value() method is called to restore a primitive datatype into its internal python representation. This method should raise a serializers.ValidationError if the data is invalid.

Example

A custom field that serializes a class representing an RGB color value:

python
import re
from rest_framework import serializers

class Color:
    """
    A simple color class used for demonstration purposes.
    """
    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

class ColorField(serializers.Field):
    """
    Color objects are serialized into 'rgb(#, #, #)' notation.
    """
    default_error_messages = {
        'invalid_type': 'Incorrect type. Expected a string.',
        'incorrect_format': 'Incorrect format. Expected `rgb(#, #, #)`',
    }

    def to_representation(self, value):
        return f"rgb({value.red}, {value.green}, {value.blue})"

    def to_internal_value(self, data):
        if not isinstance(data, str):
            self.fail('invalid_type')

        match = re.match(r'^rgb\((?P<red>\d+),\s*(?P<green>\d+),\s*(?P<blue>\d+)\)$', data)
        if match is None:
            self.fail('incorrect_format')

        return Color(
            int(match.group('red')),
            int(match.group('green')),
            int(match.group('blue'))
        )

Usage in a serializer:

python
class ItemSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=100)
    # The 'source' argument maps the field to a differently-named attribute on the object being serialized
    color = ColorField(source='color_obj')

NOTE

Idiomatic custom fields define default_error_messages on the class and use self.fail('error_key') to raise validation errors. This allows developers to easily override your error messages when they instantiate the field.