Cuando se exponen datos desde un modelo Django hacia una API o un serializer, los nombres de los campos del modelo no siempre coinciden con lo que se quiere devolver. El enfoque habitual: recuperar las instancias y renombrar en Python. Existe una opción mejor: dejar que la base de datos haga el trabajo usando F() dentro de values().

El problema: los nombres de campo del modelo se imponen

class Task(models.Model):
    name = models.CharField(...)
    created_at = models.DateTimeField(...)

Si queremos devolver nombre_tarea en lugar de name, lo habitual es recuperar los datos y renombrar en Python, ya sea con una comprensión de diccionario o dentro del serializer. En cualquier caso, la transformación ocurre a posteriori, en memoria.

F() en values(): el alias a nivel SQL

Django permite pasar expresiones F() como argumentos nombrados a values(). El alias se convierte en el nombre del campo en el resultado.

from django.db.models import F

Task.objects.values(
    identificador=F('id'),
    nombre_tarea=F('name'),
    fecha_creacion=F('created_at'),
)

El SQL generado:

SELECT id AS identificador, name AS nombre_tarea, created_at AS fecha_creacion
FROM tasks_task

La base de datos renombra las columnas, y Django devuelve directamente los diccionarios con los nombres correctos. Sin transformación Python.

Casos de uso concretos

Adaptar a una convención de nombres

Un modelo con nombres heredados o una convención interna puede exponerse con nombres más explícitos sin tocar el modelo:

from django.db.models import F

TaskRelation.objects.values(
    relation_id=F('id'),
    tarea_padre=F('parent_task_id'),
    orden=F('position'),
)

Preparar una respuesta API

Cuando se construye un payload JSON sin DRF, o se alimenta un serializer plano:

def get_tasks_payload(user_id: int) -> list[dict]:
    return list(
        Task.objects.filter(owner_id=user_id).values(
            id=F('id'),
            etiqueta=F('name'),
            creadoEn=F('created_at'),
        )
    )

El payload sale directamente con los nombres esperados por el frontend. Sin postprocesamiento.

Traducir nombres de campos

En un contexto multilingüe, o cuando se exponen datos a socios con una nomenclatura impuesta:

Producto.objects.values(
    referencia=F('sku'),
    descripcion=F('label'),
    precio_unitario=F('unit_price'),
)

Lo que cambia en la práctica

Renombrar en Python implica iterar sobre cada diccionario para construir uno nuevo. En un QuerySet de 10.000 filas, son 10.000 asignaciones adicionales. Con el alias SQL, la base devuelve directamente las columnas con los nombres correctos. Django las lee una vez, sin capa intermedia.

No es una micro-optimización despreciable en exportaciones, informes o endpoints que devuelven grandes volúmenes de datos.

Un límite a conocer

values() devuelve diccionarios, no instancias del modelo. Se pierde el acceso a los métodos y propiedades del modelo. Este enfoque es adecuado para lecturas orientadas a datos (APIs, exportaciones, agregaciones), no para lógica de negocio que necesite el comportamiento del modelo.


¿Quieres profundizar en las optimizaciones de QuerySet? El artículo sobre in_bulk() muestra cómo eliminar el N+1 con acceso O(1) por identificador.