[DRF] Como extender el SerializerMethodFields para ahorrar un poco de codigo

Hoy estuve trabajando en un “serializador” de Django Rest Framework, y vi como repetía código en muchas funciones, y me pregunte como podía evitar repetir tantas veces las mismas lineas, en la “serialización” me veía forzado a utilizar SerializerMethodFields para crear funciones que me ayudaran a extraer ciertas informaciones de los objetos relacionados de mi modelo, así que comencé a ver el código de SerializerMethodFields aquí :

    class SerializerMethodField(Field):
        """
        A read-only field that get its representation from calling a method on the
        parent serializer class. The method called will be of the form
        "get_{field_name}", and should take a single argument, which is the
        object being serialized.
        For example:
        class ExampleSerializer(self):
            extra_info = SerializerMethodField()
            def get_extra_info(self, obj):
                return ...  # Calculate some data to return.
        """
        def __init__(self, method_name=None, **kwargs):
            self.method_name = method_name
            kwargs['source'] = '*'
            kwargs['read_only'] = True
            super(SerializerMethodField, self).__init__(**kwargs)

        def bind(self, field_name, parent):
            # In order to enforce a consistent style, we error if a redundant
            # 'method_name' argument has been used. For example:
            # my_field = serializer.SerializerMethodField(method_name='get_my_field')
            default_method_name = 'get_{field_name}'.format(field_name=field_name)
            assert self.method_name != default_method_name, (
                "It is redundant to specify `%s` on SerializerMethodField '%s' in "
                "serializer '%s', because it is the same as the default method name. "
                "Remove the `method_name` argument." %
                (self.method_name, field_name, parent.__class__.__name__)
            )

            # The method name should default to `get_{field_name}`.
            if self.method_name is None:
                self.method_name = default_method_name

            super(SerializerMethodField, self).bind(field_name, parent)

        def to_representation(self, value):
            method = getattr(self.parent, self.method_name)
            return method(value)

Entonces revisando el código encontramos 2 fuentes de entrada, si sobreescribimos el metodo to_representation podemos retornar como valor la ejecución del método que pasamos por parametros y podemos agregar tanto *args y **kwargs, (En el ejemplo solo usaremos args) y así podemos cambiar el comportamiento de la función según sus argumentos, por ultimo en el metodo init podemos pasar como parametros una lista y/o un diccionario con los parámetros que queremos pasar a la función quedando así :

    class SerializerMethodWithArgsField(SerializerMethodField):
        def __init__(self, method_name=None, args=list(), **kwargs):
            if args != None:
                self.args = args
            super(SerializerMethodWithArgsField, self).__init__(method_name=method_name, **kwargs)

        def to_representation(self, value):
            method = getattr(self.parent, self.method_name)
            return method(value, *self.args)

Y de esta manera tenemos un nuevo tipo de SerializerField que acepta argumentos y que puede ayudar a reutilizar código, como en mi caso que accedía a un objeto relacionado y que este tenía un campo con un texto JSON que debía siempre convertir en diccionario para luego obtener una key (por que en mi caso no podía serializar todo el campo por motivos de seguridad)

Espero les funcione este pequeño truco de DRF, acá les dejo un gist un poco mas completo con ejemplo de uso.

Hasta la proxima