Cómo crear una Rest API en Django paso a paso (con Django Rest Framework)

Video thumbnail

Django es uno de esos frameworks que llevan mucho tiempo en el mercado y que cada día se emplean más junto con el ecosistema Python: la creación de API Rest que ha estado, que como hemos visto en varios tutoriales, videos y mis cursos, son fácilmente escalables, son flexibles y todo esto es gracia a la gran modularidad que tiene el ecosistema Python que por supuesto también se hereda a Django.

Construir una Rest API en Django es una de las tareas más comunes hoy en día, al igual que enviar eventos mediante los signals como vimos antes, sobre todo si quieres conectar tu backend con aplicaciones web o móviles en Flutter, React, Android o iOS. Django, junto con Django Rest Framework (DRF), ofrece una forma elegante, modular y segura de crear APIs escalables sin complicaciones.

Una Rest Api es un componente fundamental en cualquier aplicación hoy en día que nos ofrece un mecanismo sencillo, organizado y personalizable para compartir datos o funcionalidades mediante otras aplicaciones; inclusive nos permite agregar capas de seguridad con relativa facilidad si empleas un framework, paquete o algo por el estilo; en Django, empleando Django Rest Framework no puede ser la excepción y nos permite crear mediante este paquete para Django RestApis muy personalizadas mediante clases o métodos que podemos personalizar.

Así que, nuevamente te repito, existen muchas formas de crear recursos Rest mediante Django Rest Framework (DRF) y en esta entrada vamos a ver el enfoque más sencillo, generado y directo para aprender a dar los primeros pasos con DRF.

En mi experiencia, después de varios cursos y proyectos reales, puedo decir que DRF facilita muchísimo el trabajo, especialmente cuando entiendes bien su estructura: modelos, serializadores, vistas, rutas y autenticación.

Introducción a Django y las Rest APIs

Qué es una Rest API y por qué usar Django para construirla?

Una Rest API (Representational State Transfer) es una forma estándar de comunicar aplicaciones entre sí mediante solicitudes HTTP. En pocas palabras, es la “puerta” por la que una app externa puede acceder a los datos de tu sistema.

Django es perfecto para construir APIs porque combina un potente ORM (para manejar bases de datos sin escribir SQL) con un ecosistema maduro y una comunidad enorme. Si ya conoces Django, dar el salto a Django Rest Framework es completamente natural.

Las Apis Rest aunque NO es un estándar, es una medida que ha sido tomada por múltiples frameworks para construir este tipo de sistema cuyo propósito principal es conectarse con otros sistemas o aplicaciones como te comentaba anteriormente; así que, por ejemplo, teniendo una app en Django, la podemos conectar con apps creadas en Flutter, Android, iOS, React o cualquier otra cosa similar... lo cual es excelente ya que con un solo backend podemos crear múltiples aplicaciones que consuman esta app en Django.

Un ejemplo del mundo real es una app de compras en líneas, la cual es una app con un repositorio central creada (por ejemplo) en Django y luego puedes crear las apps para el cliente, ya sea para móviles y/o navegadores empleando (por ejemplo) las tecnologías señaladas anteriormente...

Ventajas de Django Rest Framework (DRF)

  • Serialización automática de modelos a JSON.
  • Control de permisos y autenticación muy flexible.
  • Sistema de vistas genéricas y ViewSets que evita duplicar código.
  • Integración directa con herramientas como Postman o Swagger.

En mi caso, he usado DRF para crear APIs que conectan con frontends hechos en Flutter y React, aprovechando un solo backend estable y mantenible.

Los principales conceptos de Django y el marco REST son:

Las clases de modelo proporcionan un mapa relacional de objetos (ORM) para la base de datos establecida. Un modelo se asigna a una tabla en la base de datos. Puede consultar las bases de datos sin lanzar una sola línea de SQL. Con los modelos, es fácil definir tablas y relaciones entre ellos.

Las vistas están a cargo del proceso de solicitudes. Funcionan como controladores en el MVC, recordemos que Django usa es el MTV. Puede implementarlos de muchas formas, como funciones o clases.

Las clases de serializador proporcionan control de tipos de datos y estructuras de solicitudes y respuestas.

Las plantillas o templates son archivos con contenido estático y dinámico. Están formados por código estático y otros elementos que dependen del contexto aunque esto en una app de tipo Rest no viene al caso.

Estas son algunas de las mejores prácticas a seguir cuando usa Django para el desarrollo:

Estructura de configuración: cualquier persona que sea nueva en el desarrollo puede crear fácilmente una aplicación con facilidad utilizando el marco de trabajo de Django. Es vital crear una estructura que sea fácil de mantener y reutilizar cuando sea necesario, siempre que uses Django genera un archivo settings.py que contiene todos los detalles relacionados con el proyecto o tu entorno de trabajo para que no realices ningún cambio en el archivo de configuración principal y generar nuevos archivos para diferentes entornos.

Un proyecto, varias aplicaciones en Django: puede haber muchas aplicaciones en un solo proyecto de Django. Una aplicación es una colección de conjuntos de funciones y modelos relacionados. Es una buena práctica mantener un orden y definir ubicaciones para funciones estándar. Existe una aplicación llamada api, mediante la cual se puede definir el enrutamiento de las otras aplicaciones en la carpeta principal de la aplicación.

ViewSet: es el componente más importante para ver las Rest APIs en los modelos de Django. Las vistas regulares en Django actúan como un controlador para las solicitudes HTTP, los conjuntos de vistas le brindan opciones como crear o listar. La mejor característica de los conjuntos de vistas es que hacen que el código sea consistente y se puede evitar la repetición; por lo tanto, con este componente, podemos especificar la estructura y con qué modelos van a trabajar.

Vistas genéricas: el framework Django tiene una característica llamada vistas genéricas que facilita las funciones y tareas relacionadas con los modelos. Algunas de las funciones incluyen recuperar datos, crear y destruir listas. Lo único que tienes que hacer es definir la clase. ver como un elemento secundario de la vista genérica.

Personalizar funciones: las vistas genéricas que se proporcionan en la documentación de Django enumeran una variedad de funciones como ListAPIView, CreateAPIView, RetreiveAPIVIew, RetrieveDestroyAPIView. Pero si está buscando personalizar las vistas, puede redefinir obtener, publicar, colocar, eliminar y otros métodos. También puede usar los mixins para redefinir los métodos y funciones específicas como crear, actualizar, destruir y otros.

Componentes para crear una Api Rest en DRF

En DRF existen 3 componentes principales para crear cualquier tipo de recurso Rest:

  1. Las clases de serialización, que nos permite indicar el conjunto de datos con los cuales vamos a trabajar.
  2. Las clases o métodos con los recursos Rest, la cual emplean los serializadores para hacer el mapeo a las respuestas de los recursos Rest que creamos en este punto.
  3. El ruteo, como cualquier componente http en Django, tenemos que crear un componente de ruta.

Y todo esto nosotros lo podemos administrar mediante clases, propiedades y funciones en DRF.

Configuración inicial del entorno de desarrollo

Requisitos previos

Antes de empezar, asegúrate de tener instalado Python 3.10+, pip y un entorno virtual. Desde la terminal:

$ python -m venv env
source env/bin/activate  # o env\Scripts\activate en Windows

Instalación de Django y Django Rest Framework

$ pip install django djangorestframework

Luego, crea un nuevo proyecto y una app principal:

$ django-admin startproject myproject
$ cd myproject
$ python manage.py startapp api

Configuración básica del proyecto

En tu archivo settings.py, añade las aplicaciones necesarias:

INSTALLED_APPS = [
   ...
   'rest_framework',
   'api',
]

Mantener un archivo settings.py por entorno (desarrollo, testing y producción) evita muchos errores al desplegar. Así puedes personalizar variables sin tocar el archivo principal.

Modelos y Serializadores: definiendo la estructura de datos

Creando el modelo base

Como ejemplo, definamos una tabla de categorías:

from django.db import models
class Category(models.Model):
   title = models.CharField(max_length=255)
   url_clean = models.CharField(max_length=255)
   def __str__(self):
       return self.title

Serialización con ModelSerializer

En serializers.py:

from rest_framework import serializers
from .models import Category
class CategorySerializer(serializers.ModelSerializer):
   class Meta:
       model = Category
       fields = '__all__'

El serializador se encarga de convertir los objetos de Django en formato JSON y viceversa. Gracias al ORM de Django, puedes consultar sin escribir una sola línea de SQL.

Creando vistas y ViewSets en Django Rest Framework

Diferencia entre APIView, GenericView y ViewSet

  • APIView: control total, más código.
  • GenericView: vistas preconfiguradas (listar, crear, eliminar).
  • ViewSet: agrupa acciones CRUD en una sola clase.

Si tu intención es crear una Api Rest de tipo CRUD, ya en DRF existe una clase que puedes heredar y de manera automática se va a crear gracias a los dos atributos que definimos, el queryset, para definir el pull de datos, y el serializer_class para definir la clase serializadora; todo esto lo haremos en un archivo llamado viewsets.py:

from rest_framework import viewsets
from .models import Category
from .serializers import CategorySerializer
class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer

La primera clase que tenemos que definir sería el de serializers.py que nos permite definir la serialización de nuestras clases, esto es útil para indicar con qué campos vamos a trabajar, por ejemplo, todos:

from rest_framework import serializers 
from .models import Category 
class CategorySerializer(serializers.ModelSerializer):    
  class Meta:        
    model = Category        
    fields = '__all__'

Ruteo y pruebas de la API

Configurando rutas con routers.SimpleRouter().

Con nuestros dos recursos creados, lo siguiente que necesitamos sería el manejo de las rutas; para eso:

from rest_framework import routers
from .viewsets import CategoryViewSet
router = routers.SimpleRouter()
router.register('category', CategoryViewSet)
urlpatterns = router.urls

Luego, en nuestro archivo principal para las rutas:

path('api/', include('restful.urls')),

Probando endpoints con Postman o cURL

Con el servidor en marcha:

$ python manage.py runserver

Visita http://localhost:8000/category/ o prueba con Postman.

Personalmente, siempre pruebo cada endpoint en Postman antes de desplegar para asegurar que los permisos y respuestas funcionan como espero.

Token de autenticación en la Rest Api empleando Django Rest Framework

Proteger una Rest Api como la construida anteriormente ya sea de tipo CRUD o cualquier otra, para que solo puedan ser accedido mediante algún mecanismo es una característica común hoy en día y un mecanismo muy empleado es mediante Tokens, Tokens de Autenticación y esto en Django Rest Framework lo podemos hacer fácilmente sin mucha complicación.

La autenticación vía Tokens es un esquema ampliamente en las aplicaciones web en la cual definimos nuestra Rest Api, para proteger ciertos recurso o inclusive toda la Rest Api; todo esto nosotros lo podemos configurar fácilmente en una Rest Api creada con Django Rest Framework cosa que vamos a hacer en esta entrada.

Puntos a tener en cuenta

Para crear nuestra Rest Api con protección de tokens de autenticación lo podemos hacer nativamente empleando Django Rest Framework, para esto, tenemos que definir un par de configuraciones en nuestro archivo de settings, específicamente en la key o clave de REST_FRAMEWORK que es la que empleamos para manejar este tipo de configuraciones a todo nivel.

Proteger una Rest Api

La configuración que vamos a hacer sería para aplicar globalmente en una clase de tipo Rest; ya que como puedes ver, al momento de trabajar con DRF tenemos muchas maneras de hacer lo mismo y aquí simplemente te ofrecemos una versión. Simplemente tienes que indicar en el archivo de configuración de tu proyecto en Django, el llamado settings.py y agregar la siguiente configuración:

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES':[
        'rest_framework.permissions.IsAuthenticated'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES':[
        'rest_framework.authentication.TokenAuthentication'
    ]
}

También debes añadir la app de tokens:

INSTALLED_APPS += ['rest_framework.authtoken']

Ejecuta migraciones:

$ python manage.py migrate authtoken

Crear la función para generar los Tokens de autenticación y verificar credenciales (login)

Ahora, lo siguiente que tenemos que hacer sería crear la función que emplearía nuestro usuario para generar el token, este método o función será un recurso Rest personalizado por nosotros que nos permitirá generar dicho token mediante la función de  get_or_create del modelo Token que importamos de gratis de nuestro DRF.

Ahora, generamos dicho token una vez comprobadas las credenciales del usuario con la que tenemos en el modelo de User y en la base de datos; así que, finalmente:

from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
 
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
 
@api_view(['POST'])
def login(request):
 
    username = request.POST.get('username')
    password = request.POST.get('password')
 
    try: 
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        return Response("Usuario inválido")
 
    if not check_password(password, user.password):
        return Response("Contraseña inválida")
    token, _ = Token.objects.get_or_create(user=user)
    return Response(token.key)

Explicación de la función anterior

Como puedes ver, al ser este el recurso que tenemos que emplear para el login, y al tener toda la Rest Api protegida mediante los tokens auth, tenemos que indicar específicamente que el recurso anterior puede ser accedido sin necesidad de que este token esté presente en la petición que estamos enviando; por lo tanto, empleamos el decorador de permission_classes con el valor de AllowAny para que sea de acceso público; por lo demás, es una sencilla comprobación de credenciales como la que hicimos anteriormente para comprobar las credenciales; por lo demás, tenemos la generación del token de autenticación al cual le tenemos que pasar el usuario al cual le queremos crear o buscar en caso de que exista, el token.

La función realmente se auto explica sola, tenemos los parámetros que recibimos vía una petición post, en este caso empleamos los mismos usuarios que emplea Django de manera interna en toda la aplicación. Por lo tanto el proceso de login personalizado emplea la función de check_password que nos provee Django para tal fin; ahora, desde Postman podemos consumir la función anterior.

Probando autenticación en Postman

Enviando una petición POST a /login, recibirás un token. Luego, usa ese token en los encabezados (Authorization: Token <tu_token>) para acceder a recursos protegidos.

Cuando probé este flujo por primera vez, me sorprendió lo sencillo que es proteger toda una API con DRF: basta con configurar un par de clases en el settings.py y ajustar los permisos.

Login a Postman en Django

Y si el login va bien, creamos (en caso de que no exista) y devolvemos el token de autenticación que tiene que emplear nuestro usuario para realizar peticiones.

Luego creamos la ruta para esta función:

urlpatterns = route.urls
urlpatterns += path('login',views.login),

Y ahora, cuando queramos realizar una petición, tenemos que enviar el token; por ejemplo, empleando Postman:

Prueba consumir recurso con token en postman

Protección de los ModelViewSet

Podemos agregar la siguiente configuración en en nuestra clase (EN caso de que la quieras hacer local a una clase, puedes evitar la configuración de 'DEFAULT_PERMISSION_CLASSES'); una de las propiedades que cualquier clase tipo Rest viene siendo la siguiente:

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Category
from .serializers import CategorySerializer
class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [IsAuthenticated]  #  Solo usuarios autenticados

Es necesario estar autenticado DEFAULT_PERMISSION_CLASSES y la siguiente es que vamos a emplear como backend, el sistema de tokens; ahora, también tenemos que indicar que vamos a emplear la aplicación de tokens.

Esto es útil si nosotros NO estamos empleando el sistema de migraciones de Django, en casos -por ejemplo- de que estemos empleando MongoDB con Django, que al menos para manejar las tablas/colecciones creadas por nosotros, no debería ser necesario aplicar las migraciones.

Esto hace que todas las peticiones a /categories/ requieran autenticación, es decir:

Si el usuario no está autenticado, recibirá:

{
   "detail": "Authentication credentials were not provided."
}

Con código HTTP 401 Unauthorized.

Si el usuario sí está autenticado, podrá acceder y usar el endpoint normalmente.

Ya con esto, puedes consumir esta Rest API para consumir la misma desde cualquier tipo de aplicación como Flutter o inclusive con Vue haciendo peticiones fetch o mediante axios, que es como lo hacemos en el curso y libro completo.

Siguiente paso, aprende a usar los Websockets en Django empleando Django Channels, Servidores wsgi y asgi

Acepto recibir anuncios de interes sobre este Blog.

Guía completa para construir una API REST con Django y Django Rest Framework (DRF). Descubre cómo estructurar tu proyecto, usar ViewSets y proteger tu API con autenticación por tokens.

| 👤 Andrés Cruz

🇺🇸 In english