Serializers Avanzados10 temas

Serializers Avanzados

Los serializers definen qué datos entran y salen de la API. Con técnicas avanzadas se controla con precisión qué se expone y cómo se valida.

Read-only fields

Un campo read-only aparece en la respuesta pero nunca se espera en el request. Ideal para IDs, fechas de creación, o cualquier cosa que genere el servidor:

from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ["id", "title", "content", "created_at"]
        read_only_fields = ["id", "created_at"]

El cliente no necesita enviarlos: si lo hace, DRF los ignora silenciosamente. En la respuesta aparecen con los valores generados por el servidor.

Write-only fields

Un campo write-only se espera en el request pero no se devuelve en la respuesta. Ideal para contraseñas, tokens, o datos sensibles:

from rest_framework import serializers


class SignupSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["username", "email", "password"]
        extra_kwargs = {
            "password": {"write_only": True},
        }

La API nunca lo devuelve: ni en la respuesta, ni en logs, ni en la documentación automática.

Tip: extra_kwargs también sirve para definir required, allow_null, validators y más, todo por campo y sin escribir código redundante.

SerializerMethodField (campos calculados)

SerializerMethodField: expone un campo calculado a partir del modelo, sin columna en la base de datos:

from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    author_name = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Post
        fields = ["id", "title", "content", "author_name"]

    def get_author_name(self, obj):
        """Calcula el nombre del autor a partir de la relación."""
        return obj.author.display_name if obj.author else "Anónimo"

El método get_<nombre_del_campo>(self, obj) recibe la instancia del modelo y devuelve el valor que se incluirá en la respuesta. DRF lo llama automáticamente para cada objeto.

Cuidado:

Si el cálculo genera una query por objeto (N+1), conviene agregarlo como propiedad del modelo o usar annotate() en el queryset.

Validación cross-field

Hay reglas de negocio que involucran múltiples campos. Por ejemplo: “si el status es ‘published’, el título no puede estar vacío”. Eso se resuelve en validate():

from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ["id", "title", "content", "status"]

    def validate(self, attrs):
        """Validación cross-field: reglas que cruzan múltiples campos."""

        if attrs.get("status") == "published" and not attrs.get("title"):
            raise serializers.ValidationError(
                "Un post publicado debe tener un título."
            )

        return attrs

A diferencia de validate_<campo>(self, value) que valida un campo individual, validate(self, attrs) recibe todos los campos ya validados individualmente y permite aplicar reglas que los crucen.

Recursos

DRF Docs: Serializers