Search code examples
pythondjangodjango-rest-frameworkdjango-serializer

Proper usage of "callable default function" in Django REST Framework (DRF)


Question:

How can I access the serializer instance or any relevant arguments in the default callable function of DRF field?


Scenario:

I have a serializer configuration as below,

def bar_value_callable_function(serializer_instance):
    if serializer_instance.context['request'].method == 'GET':
        return 'return value One'
    return 'return value Two'

class FooSerializer(serializers.Serializer): bar = serializers.CharField(source='foo.bar', default=bar_value_callable_function)

and when I try to serialize the data as,

serializer = FooSerializer(foo_instance, context={'request': request})
print(serializer.data)

I am gettting error as,

TypeError: bar_value_callable_function() missing 1 required positional argument: 'serializer_instance'

Solution

  • Update
    For DRF>=3.12, use this king of default class

    class DefaultBarValue:
        requires_context = True
    
        def __call__(self, serializer_instance):
            if serializer_instance.context['request'].method == 'GET':
                return 'return value One'
            return 'return value Two'

    pass a class instance instead of function to the default argument

    # default callable class
    class DefaultBarValue:
        """
        "Method `set_context` on defaults is deprecated and will
        no longer be called starting with 3.12. Instead set
        `requires_context = True` on the class, and accept the
        context as an additional argument.
        """
        requires_context = True  # for DRF>=3.12
        serializer_instance = None # not required for DRF>=3.12
    
        def set_context(self, serializer_instance): # not required for DRF>=3.12
            self.serializer_instance = serializer_instance
    
        def __call__(self, serializer_instance=None):
            if serializer_instance is None:  # will be None for older versions of DRF
                if self.serializer_instance.context['request'].method == 'GET':
                    return 'return value One'
                return 'return value Two'
            else:  # for DRF>=3.12
                if serializer_instance.context['request'].method == 'GET':
                    return 'return value One'
                return 'return value Two'
    
    # serializer
    class FooSerializer(serializers.Serializer):
        bar = serializers.CharField(source='foo.bar', default=DefaultBarValue())