Sobre mí

José Miguel Amaya Camacho

Perfil

Desarrollador Python/Django. Full Stack en Xprende Tech (Ecuador) e Infobibliotecas (España). Fundador de Python Piura.

Hoy

Construimos una API REST paso a paso: del modelo al endpoint, con código que puedes llevarte.

GitHub github.com/joseamaya
Comunidad Python Piura · Piura AI
Agenda

Qué vamos a ver

  • 01 Qué es REST y por qué importa 10 min
  • 02 Django y Django REST Framework 10 min
  • 03 Proyecto, app y configuración 10 min
  • 04 Modelos, serializers, vistas y routers 25 min
  • 05 Probemos la API y siguientes pasos 5 min
01

REST

Qué es una API REST y qué restricciones sigue.

Punto de partida

¿Qué es una API REST?

2000

Roy Fielding describe REST en su tesis doctoral: uno de los padres de la especificación HTTP.

≠ arquitectura

No es una arquitectura de software: es un conjunto de restricciones para diseñar un estilo de arquitectura.

JSON / XML

Una interfaz entre sistemas que usa HTTP para obtener datos u operar sobre ellos.

Restricciones

Características de REST (I)

Cliente-servidor

Débilmente acoplados: el cliente no conoce la implementación del servidor y viceversa. Pueden evolucionar por separado.

Interfaz uniforme

Cada recurso tiene una única dirección: su URI. /cursos/, /cursos/3/…

Operaciones

POST crear · GET leer · PUT editar · DELETE eliminar.

Restricciones

Características de REST (II)

Sin estado

Cada petición es independiente: el servidor no necesita recordar sesiones. Todo lo necesario viaja en la petición.

Cacheable

Las respuestas deben poder marcarse como cacheables para evitar trabajo repetido.

Sistema de capas

El servidor puede implementarse en capas: balanceadores, cachés, proxies… el cliente no necesita saberlo.

La idea en una imagen

Cliente, métodos y servidor

Cliente web · móvil · otro servicio petición · JSON respuesta GET POST PUT / PATCH DELETE HTTP HTTP Servidor Django + DRF Base de datos El cliente envía una petición Métodos HTTP El servidor responde
Vocabulario HTTP

Métodos y códigos de estado

Métodos
GETLeer un recurso o una colección
POSTCrear un recurso
PUTReemplazar un recurso completo
PATCHActualizar parte de un recurso
DELETEEliminar un recurso
Códigos frecuentes
200OK · la operación salió bien
201Created · recurso creado
400Bad Request · datos inválidos
404Not Found · no existe
500Server Error · fallo del servidor
Por qué REST

Ventajas para el desarrollo

Separación cliente-servidor

Frontend y backend en servidores distintos, con equipos y ciclos independientes. La interfaz es la API.

Visibilidad, fiabilidad y escalabilidad

Puedes migrar servidores o cambiar la base de datos sin tocar al cliente, mientras se respete el contrato.

Independiente de la plataforma

La misma API la consumen una web, una app móvil, otro servicio o una integración.

Un contrato claro

Recursos, métodos y códigos de estado: cualquiera puede integrarse sin leer tu código.

02

Django y DRF

El framework web y su toolkit para APIs.

El framework

¿Qué es Django?

Baterías incluidas

ORM, autenticación, admin, formularios, plantillas y migraciones: listo para producción.

Apps reutilizables

Un proyecto se compone de aplicaciones con una responsabilidad cada una: cursos, usuarios…

ORM

Clases Python que se reflejan en tablas de la base de datos, sin escribir SQL a mano.

✓
Django 5.2 LTS: soporte extendido y la versión que usarás en proyectos reales. Todo lo que veamos aplica igual en 4.2+.
Cómo fluye una petición

Django en una imagen

1

URL

urls.py decide qué vista atiende
→
2

Vista

views.py procesa la petición
→
3

Modelo

models.py consulta la base de datos
→
4

Respuesta

template o dato listo para enviar
i
DRF cambia solo los pasos 2 y 4: en vez de vistas con templates, tendremos vistas que devuelven JSON.
El toolkit

Django REST Framework

Una app de Django para construir APIs REST reutilizando piezas, con una API navegable desde el navegador.

Serialización

Convierte modelos (y otros orígenes) a JSON y valida lo que entra: como los Forms, pero para APIs.

Vistas y routers

Vistas genéricas y ViewSets que implementan un CRUD completo con muy poco código.

Autenticación y permisos

Integración con sesiones, OAuth1a/2, tokens y JWT; permisos declarados por vista.

Comunidad y docs

Documentación excelente y uso intensivo en producción (Mozilla, Red Hat, Heroku…).

La diferencia

¿Por qué no con vistas Django a mano?

Vista DjangoDjango REST Framework
DevuelveHTML con un templateJSON, XML u otro formato
ValidaFormSerializer
CRUDEscribes cada vistaViewSet + router generan rutas y acciones
ProbarFormulario en el navegadorAPI navegable + herramientas HTTP
!
No es que no se pueda: es que con DRF escribes menos código y ganan consistencia los errores, la paginación y los permisos.
03

Manos a la obra

Proyecto, aplicación y configuración inicial.

Paso 1

Preparar el entorno

Un entorno virtual aísla las dependencias de cada proyecto.

zsh — terminal
$ python3 --version Python 3.13.5 $ python3 -m venv .venv $ source .venv/bin/activate # Windows: .venv\Scripts\activate (.venv) $
i
¿Por qué .venv? Cada proyecto tiene sus propias versiones; tu sistema queda limpio.
Paso 2

Instalar Django y DRF

zsh — terminal
(.venv) $ pip install "Django>=5.2,<6" djangorestframework Collecting Django>=5.2,<6 Collecting djangorestframework Successfully installed django-5.2.7 djangorestframework-3.16.1 ... (.venv) $ pip freeze > requirements.txt
Paso 3

Crear el proyecto

zsh — terminal
(.venv) $ django-admin startproject taller . (.venv) $ ls manage.py taller/
✓
El punto al final hace que manage.py quede en la raíz: menos anidamiento y comandos más cortos.
Paso 4

Estructura del proyecto

taller/ — árbol
taller/
├── manage.py
├── requirements.txt
└── taller/
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    ├── asgi.py
    └── wsgi.py
  • settings.pyapps, base de datos, idioma, zona horaria…
  • urls.pyenruta cada URL a una vista
  • asgi.pyentrada ASGI (async) para producción
  • wsgi.pyentrada WSGI clásica para producción
  • manage.pyscript de administración del proyecto
Paso 5

Configurar DRF

En taller/settings.py sumamos DRF y nuestra app a las aplicaciones instaladas.

taller/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "cursos",
]
i
rest_framework habilita la API navegable; cursos se agrega al crearla (siguiente slide).
Paso 6

La aplicación cursos

zsh — terminal
(.venv) $ python manage.py startapp cursos (.venv) $ ls cursos admin.py apps.py models.py tests.py views.py migrations/
cursos/ — árbol
cursos/
├── migrations/
├── admin.py
├── apps.py
├── models.py
├── tests.py
└── views.py
i
Django no crea serializers.py: es una convención de DRF y lo crearemos nosotros.
Paso 7

Arrancar el proyecto

zsh — terminal
(.venv) $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK ... (16 migraciones más) (.venv) $ python manage.py runserver Watching for file changes with StatReloader Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
!
El servidor de desarrollo es solo para local. En producción: gunicorn/uvicorn detrás de nginx.
Paso 8

El admin y el superusuario

  • /admin/panel listo para administrar usuarios y modelos
  • createsuperuseracceso total al admin y a las apps registradas
zsh — terminal
(.venv) $ python manage.py createsuperuser Nombre de usuario: miguel Correo electrónico: miguel@example.com Password: ******** Superuser created successfully.
04

El CRUD

Modelos, serializadores, vistas y routers.

Pieza 1

Los modelos

  • Descripciónlos datos, en código Python, reflejados en tablas de la base de datos.
  • SQL por detráscada consulta ejecuta SQL y devuelve objetos Python cómodos de usar.
  • models.pycada aplicación declara ahí sus modelos.
✓
Nuestro dominio: Categoria y Curso, con una relación de muchos a muchos y un creador.
Pieza 1

models.py

cursos/models.py
from django.contrib.auth.models import User
from django.db import models


class Categoria(models.Model):
    nombre = models.CharField(max_length=20)


class Curso(models.Model):
    titulo = models.CharField(max_length=100)
    descripcion = models.TextField(blank=True)
    categorias = models.ManyToManyField(Categoria, related_name="cursos")
    creador = models.ForeignKey(
        User, on_delete=models.CASCADE, null=True, blank=True
    )

    def __str__(self):
        return self.titulo
i
Cambios frente al original: on_delete es obligatorio desde Django 2.0, related_name="cursos" es más claro y agregamos __str__.
Pieza 2

Los serializadores

Convierten objetos Python ⇄ JSON y validan lo que entra, como los Forms.

Modelo Curso Serializer valida JSON { ... } instancia crear / actualizar serializar deserializar
Serializercontrol total: defines cada campo a mano
ModelSerializerinfiere campos y validaciones del modelo
serializers.pyconvención de DRF para declararlos
Pieza 2

serializers.py

cursos/serializers.py
from rest_framework import serializers

from cursos.models import Categoria, Curso


class CategoriaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Categoria
        fields = ("id", "nombre")


class CursoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Curso
        fields = ("id", "titulo", "descripcion", "categorias", "creador")
i
En producción marca creador como read_only_fields y asígnalo desde self.request.user: nadie debería crear cursos a nombre de otro.
Pieza 3

Las vistas

  • Entradareciben un objeto con la información de la petición (request).
  • Salidadevuelven JSON (u otro formato) o una excepción si algo falla.
  • views.pycada aplicación declara ahí sus vistas; en DRF suelen ser clases.
✓
DRF trae vistas genéricas que ya implementan listar, crear, recuperar, actualizar y borrar.
Pieza 3

La jerarquía de vistas de DRF

APIViewcontrol total: escribes cada get(), post() a mano
GenericAPIView+ queryset, serializer_class, get_object()
ListCreateAPIViewlistar + crear
RetrieveUpdateDestroyAPIViewdetalle + editar + borrar
ModelViewSetlas 6 acciones + router · lo que usaremos
i
Cuanto más abajo, menos código escribes y más convenciones aceptas. Puedes bajar de nivel cuando lo necesites.
Pieza 3

ModelViewSet: las 6 acciones

AcciónMétodoRuta generadaQué hace
listGET/cursos/Lista la colección
createPOST/cursos/Crea un recurso
retrieveGET/cursos/{id}/Devuelve un recurso
updatePUT/cursos/{id}/Reemplaza el recurso
partial_updatePATCH/cursos/{id}/Edita parte del recurso
destroyDELETE/cursos/{id}/Elimina el recurso
Pieza 3

views.py

cursos/views.py
from rest_framework import viewsets

from cursos.models import Categoria, Curso
from cursos.serializers import CategoriaSerializer, CursoSerializer


class CategoriaViewSet(viewsets.ModelViewSet):
    queryset = Categoria.objects.all()
    serializer_class = CategoriaSerializer


class CursoViewSet(viewsets.ModelViewSet):
    queryset = Curso.objects.all()
    serializer_class = CursoSerializer
!
Error clásico: en el taller original los queryset estaban cruzados (CategoriasView consultaba Curso). Cada vista debe apuntar a su propio modelo.
Pieza 3

¿Cuánto código necesitas?

El mismo CRUD de Curso, según el enfoque elegido.

Cifras ilustrativas para comparar enfoques; el código real varía según el proyecto.

Pieza 4

Los routers

  • Definir URLsgeneran las rutas del API de forma ordenada a partir de los ViewSets.
  • Mapear verbosdeciden qué acción se ejecuta según el verbo HTTP y el path.
  • Organizartodo en el urls.py del proyecto o uno por aplicación.
✓
Con DefaultRouter registramos cursos una vez y obtenemos las 6 rutas del CRUD, sin escribir ningún path() a mano.
Pieza 4

urls.py

taller/urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework import routers

from cursos.views import CategoriaViewSet, CursoViewSet

router = routers.DefaultRouter()
router.register("cursos", CursoViewSet, basename="cursos")
router.register("categorias", CategoriaViewSet, basename="categorias")

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", include(router.urls)),
]
i
Del taller original a hoy: url() con regex pasó a path(), y la API se monta bajo /api/v1/ para poder versionarla.
Todo junto

El viaje de una petición

PeticiónGET /api/v1/cursos/
→
Routeren urls.py elige la acción
→
ViewSetusa queryset + serializer
→
Serializervalida y convierte
→
ORMconsulta la base de datos
→
PostgreSQLdevuelve las filas
✓
De vuelta: filas → objetos → JSON → HTTP 200 OK. Si algo no valida, se corta antes con 400.
Pieza 5

Probemos la API

http://127.0.0.1:8000/api/v1/cursos/GET
HTTP 200 OK Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json [ { "id": 1, "titulo": "Django REST Framework", "descripcion": "Charla en Python Piura", "categorias": [1], "creador": 1 } ]
POSTPUTPATCHDELETEOPTIONS

La API navegable permite hacer POST y PUT desde el propio navegador.

Pieza 5

También desde la terminal

curl
$ API=http://127.0.0.1:8000/api/v1 $ curl $API/cursos/ [{"id": 1, "titulo": "Django REST Framework", ...}] $ curl -X POST $API/cursos/ \ -H "Content-Type: application/json" \ -d '{"titulo": "FastAPI", "descripcion": "Nueva charla"}' {"id": 2, "titulo": "FastAPI", ...}

Postman / httpie

Clientes gráficos o de terminal para guardar colecciones de peticiones y compartirlas con el equipo.

i
Lo importante es la API: cualquier cliente que hable HTTP sirve para probarla.
Aprendiendo de los errores

Errores comunes

Querysets cruzados

CategoriaViewSet debe usar Categoria.objects.all(), no Curso. Síntoma: el endpoint devuelve datos del otro modelo.

Falta on_delete

Obligatorio en cada ForeignKey desde Django 2.0. Y si cambias el modelo: makemigrations + migrate.

Olvidar rest_framework

Sin registrarlo en INSTALLED_APPS no hay API navegable ni routers.

Rutas sin prefijo

Montar la API en /api/v1/ desde el inicio te deja evolucionar sin romper clientes.

Para seguir

Buenas prácticas y siguientes pasos

Paginación Permisos Autenticación (Token/JWT) Filtros y búsqueda drf-spectacular (OpenAPI) Testing con APIClient Versionado Throttling
Documentacióndjango-rest-framework.org
Djangodocs.djangoproject.com/en/5.2/
ComunidadPython Piura · pythonpiura.org
Este deckjoseamaya.github.io/introduccion-a-django-rest-framework/
✓
Todo lo anterior son piezas de DRF que se activan con configuración: el CRUD ya está listo para crecer.