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_kwargstambién sirve para definirrequired,allow_null,validatorsy 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.
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.