Permisos declarativos en DRF con rest_access_policy

Permisos declarativos en DRF con rest_access_policy

Los permisos en Django REST Framework funcionan, pero muestran sus límites en cuanto las reglas de acceso se vuelven moderadamente complejas. Varios roles, objetos pertenecientes a un usuario específico, acciones custom en un ViewSet: acabas con clases has_permission y has_object_permission que mezclan verificaciones heterogéneas, difíciles de leer y aún más difíciles de testear. rest_access_policy (paquete djangorestframework-access-policy) propone un enfoque diferente: declarar las reglas de acceso como statements, similar a las políticas IAM de AWS. El resultado es legible de un vistazo, testeable independientemente del ViewSet, y extensible sin reescribir toda la clase. ...

26 de mayo de 2026 · 7 min · Anthony
Hash, HMAC y cifrado: cómo proteger un token en Django

Hash, HMAC y cifrado: cómo proteger un token en Django

Una comparación == sobre un hash no es suficiente para elegir el mecanismo correcto. sha256, HMAC, hash salado, cifrado: cada enfoque ofrece garantías distintas. Entender cuáles son cambia concretamente la forma de almacenar y verificar un token en Django. Hash simple import hashlib token_hash = hashlib.sha256(token.encode()).hexdigest() Un hash simple es determinista: la misma entrada siempre produce la misma salida. No interviene ningún secreto del servidor. Es imposible recuperar el token original a partir del hash (sha256 es una función unidireccional). Pero si alguien conoce o adivina el token, puede recalcular el hash y comparar. ...

25 de mayo de 2026 · 4 min · Anthony
Django: save() no llama a full_clean() — ciclo de validación

Django: save() no llama a full_clean() — ciclo de validación

Llamar a obj.save() después de definir validators y un método clean() en el modelo da la impresión de que la validación está garantizada. No lo está. Django no llama a full_clean() durante un save(), y este comportamiento es deliberado. Entender por qué cambia la forma de diseñar la validación en un proyecto. Lo que save() hace realmente El ciclo de vida de un save() es más corto de lo que parece: ...

18 de mayo de 2026 · 5 min · Anthony
Timing attacks en Django con constant_time_compare

Timing attacks en Django con constant_time_compare

Una comparación == sobre un token parece inofensiva. En la práctica, filtra información medible: el tiempo de ejecución varía según cuántos caracteres coinciden. Ese es el principio de una timing attack, y basta para que un atacante reconstruya el token carácter a carácter. El problema: la comparación que se detiene demasiado pronto Python compara cadenas carácter a carácter y se detiene en cuanto encuentra una diferencia. def verify_token(request): user_token = request.GET.get('token') valid_token = "secret_abc123" if user_token == valid_token: return JsonResponse({'access': 'granted'}) return JsonResponse({'access': 'denied'}) En la práctica: ...

14 de mayo de 2026 · 3 min · Anthony
Vistas materializadas vs cache Django para queries lentas

Vistas materializadas vs cache Django para queries lentas

El instinto ante un endpoint de reporting lento suele ser el cache. Un @cache_page, un cache.set(), y el problema parece desaparecer hasta la próxima expiración. Este enfoque tiene una limitación estructural que las vistas materializadas PostgreSQL resuelven desde la raíz. El problema del cache en los endpoints analíticos El cache Django almacena el resultado de una vista Python. La consulta SQL costosa se ejecuta igualmente en cada expiración del cache. Para un informe construido sobre múltiples JOINs y agregaciones, eso significa que el primer usuario tras cada cache miss espera varios segundos. ...

13 de mayo de 2026 · 4 min · Anthony
Optimizar las consultas Django ORM con defer(), only() y Prefetch()

Optimizar las consultas Django ORM con defer(), only() y Prefetch()

Por defecto, Django carga todos los campos de un modelo en cada consulta. En una vista de lista con 50 artículos, eso significa obtener el contenido completo, el resumen, los metadatos y los campos de traducción 50 veces, aunque solo se muestre el título y la fecha. Cuatro métodos QuerySet de Django permiten controlar exactamente qué se carga: defer(), only(), values_list() y Prefetch(). El resultado: 2 consultas SQL en lugar de N+2, con únicamente las columnas necesarias. ...

8 de mayo de 2026 · 6 min · Anthony
Django squashmigrations: limpiar el historial de migraciones

Django squashmigrations: limpiar el historial de migraciones

Después de algunos meses de desarrollo, no es raro acumular 30, 50 o incluso 100 archivos de migración en una aplicación Django. Cada ejecución de tests que parte de una base vacía los reproduce todos. Cada despliegue en un entorno nuevo también. squashmigrations permite fusionar varias migraciones en una sola, sin perder la compatibilidad con los entornos que ya han aplicado migraciones anteriores. El comando y lo que genera La sintaxis acepta un rango de migraciones: ...

7 de mayo de 2026 · 5 min · Anthony
Django select_for_update(): bloqueo de filas en transacciones concurrentes

Django select_for_update(): bloqueo de filas en transacciones concurrentes

Dos peticiones simultáneas leen el stock de un producto, las dos ven que queda una unidad, y las dos confirman el pedido. El stock baja a -1. Este tipo de condición de carrera es casi imposible de reproducir en desarrollo y devastador en producción. select_for_update() es la respuesta de Django: adquirir un bloqueo SQL en el momento de la lectura para garantizar que ninguna otra transacción pueda modificar la fila antes de que termine la operación actual. ...

6 de mayo de 2026 · 4 min · Anthony
Renombrar campos del ORM de Django con F() en values()

Renombrar campos del ORM de Django con F() en values()

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. ...

5 de mayo de 2026 · 3 min · Anthony
Django Window Function vs GROUP BY: QuerySets encadenables

Django Window Function vs GROUP BY: QuerySets encadenables

Con el ORM de Django hay dos formas de añadir un valor calculado sobre un conjunto de filas: annotate() con una agregación clásica (Max, Count, Sum…) o annotate() con una Window function. A simple vista se parecen. En la práctica, su comportamiento es fundamentalmente distinto — y elegir la opción equivocada puede bloquear toda la cadena de filtrado. GROUP BY con annotate(): filas que se comprimen Cuando combinamos values() y annotate() con una agregación, Django genera un GROUP BY en SQL. El resultado: las filas se agrupan y obtenemos una fila por grupo. ...

4 de mayo de 2026 · 4 min · Anthony

Newsletter

Recibe los nuevos artículos directamente en tu bandeja de entrada.

Sin spam. Baja en un clic.