Saltar al contenido
🐍 Curso de Python
/

Módulo 5 · Lección 12 de 30

Instalación y primer punto de acceso

Una API funcionando en veinte líneas, y las tres cosas que trae apagadas de fábrica: una de ellas deja que cualquiera borre tus datos.

⏱ 16 min de lectura

Django REST Framework se instala y funciona rápido. Tan rápido que se puede tener una API completa antes de haber entendido nada de lo que hace, y eso es exactamente el problema de esta lección.

Vamos a montarla, y después vamos a mirar los ajustes por defecto, que son la parte que no sale en los tutoriales y la que decide si la API es publicable.

La instalación#

bash
pip install djangorestframework
Salida en pantalla
Name: djangorestframework
Version: 3.18.1
Requires: django

Una sola dependencia, y es Django. Es de las bibliotecas grandes más limpias que hay, y conviene saberlo porque casi todos los tutoriales instalan a la vez cuatro paquetes de terceros que no hacen falta para empezar.

Se registra como una aplicación:

python
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'catalogo',
]

El punto de acceso#

Tres piezas. Primero el serializador, que convierte entre modelos y JSON y es la lección siguiente:

python
from rest_framework import serializers

from .models import Libro


class LibroSerializer(serializers.ModelSerializer):
        class Meta:
                model = Libro
        fields = '__all__'

Después la vista:

python
from rest_framework import viewsets


class LibroViewSet(viewsets.ModelViewSet):
        queryset = Libro.objects.all()
    serializer_class = LibroSerializer

Y las rutas:

python
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('libros', LibroViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

Con eso hay una API con lista, creación, consulta, modificación y borrado. Y la raíz del enrutador la enseña:

Salida en pantalla
GET /api/

{
  "editoriales": "http://127.0.0.1:8077/api/editoriales/",
  "autores": "http://127.0.0.1:8077/api/autores/",
  "libros": "http://127.0.0.1:8077/api/libros/"
}

Un libro concreto:

Salida en pantalla
GET /api/libros/1/

{
  "id": 1,
  "titulo": "El libro número 0",
  "isbn": "978-84-0000-00-0",
  "precio": "19.98",
  "paginas": 234,
  "estado": "publicado",
  "publicado_en": "2020-01-01",
  "creado": "2026-09-22T21:15:20.844072+02:00",
  "editorial": 1,
  "autores": [5]
}

Tres cosas que mirar antes de seguir:

El precio va entre comillas. Es la decisión de la lección anterior, y DRF la toma por ti con el ajuste COERCE_DECIMAL_TO_STRING, que vale True de fábrica. Acertado: si fuera número, el cliente lo leería como coma flotante.

Las fechas son ISO 8601 con zona horaria, que es lo que la lección 3 pedía.

Las relaciones son identificadores. editorial: 1 y autores: [5]. Es lo que hay que cambiar en la lección 14.

Los errores, en español#

Esto es una alegría para un proyecto en español y casi nadie lo menciona. Con LANGUAGE_CODE = 'es':

Salida en pantalla
DELETE /api/libros/     -> 405  {"detail": "Método \"DELETE\" no permitido."}
POST /api/libros/ {}    -> 400  {"titulo": ["Este campo es requerido."],
                                 "isbn": ["Este campo es requerido."], ...}

Traducidos solos, sin configurar nada más. DRF viene con las traducciones dentro.

Y la forma de los errores es la de la lección 2, ahora desde el lado que los escribe: un objeto con detail cuando el problema es general, y un objeto con una lista de mensajes por campo cuando son de validación. Esa forma es razonable y merece la pena conocerla, porque es lo que van a recibir tus clientes.

El navegador de la API#

Si abres /api/libros/ en un navegador no sale JSON: sale una página con formularios para probar la API. Es la característica más llamativa de DRF.

Y funciona por negociación de contenido, que es la lección 2 desde el otro lado:

Salida en pantalla
Accept: */*        -> application/json         54 044 bytes
Accept: text/html  -> text/html; charset=utf-8  132 253 bytes

Un navegador manda text/html, así que ve la página; curl manda */* y recibe JSON. Por eso funciona sin que hayas configurado nada.

Es genuinamente útil para explorar y para enseñársela a alguien. Y tiene un coste que hay que conocer:

Salida en pantalla
json  0,042 s  54 044 bytes
html  0,050 s  132 253 bytes

Dos veces y media más grande, porque construye formularios HTML con todas las opciones de todos los campos de todos los objetos. Con relaciones grandes, eso son desplegables de miles de elementos y la página tarda segundos.

En producción se apaga, dejando solo el renderizador de JSON:

python
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
}

Y si quieres conservarlo en desarrollo, es la configuración por entornos de la lección 40 del módulo 4:

python
# ajustes/desarrollo.py
REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] += [
    'rest_framework.renderers.BrowsableAPIRenderer',
]

Los ajustes de fábrica#

Aquí está lo importante de la lección. Estos son los valores por defecto de DRF, medidos:

Salida en pantalla
DEFAULT_RENDERER_CLASSES        [JSONRenderer, BrowsableAPIRenderer]
DEFAULT_PARSER_CLASSES          [JSONParser, FormParser, MultiPartParser]
DEFAULT_AUTHENTICATION_CLASSES  [SessionAuthentication, BasicAuthentication]
DEFAULT_PERMISSION_CLASSES      [AllowAny]
DEFAULT_THROTTLE_CLASSES        []
DEFAULT_PAGINATION_CLASS        None
PAGE_SIZE                       None
DEFAULT_FILTER_BACKENDS         []
COERCE_DECIMAL_TO_STRING        True
DATETIME_FORMAT                 iso-8601

Cuatro de esas líneas son las que hay que cambiar.

No hay paginación. DEFAULT_PAGINATION_CLASS es None, así que un listado devuelve todo:

Salida en pantalla
GET /api/libros/  ->  200 elementos, 54 044 bytes, lista pelada

Con doscientos libros son cincuenta kilobytes. Con doscientos mil, la petición tumba el servidor. Y el problema añadido, que es el de la lección anterior: activar la paginación cambia la forma de la respuesta de una lista pelada a un objeto con envoltorio, así que hacerlo más tarde rompe a todos los clientes.

Se activa el primer día:

python
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 50,
}

Es la lección 20, y esas dos líneas van desde el principio.

No hay límite de uso. DEFAULT_THROTTLE_CLASSES está vacío: cualquiera puede pedir mil veces por segundo. Es todo el bloque A visto desde el otro lado, y se configura con AnonRateThrottle y UserRateThrottle.