Gestión de Versiones en Django REST Framework

Django REST Framework (DRF) ofrece mecanismos flexibles para gestionar las versiones de tus APIs, permitiendo a los clientes interactuar con diferentes iteraciones de tu servicio sin interrupciones.

  1. Versiones Basadas en Parámetros GET (Menos Recomendado)

Aunque es posible, basar la versión en parámetros de conuslta GET no es la práctica más robusta ni semántica.

1.1. Implementación Personalizada

Puedes crear una clase personalizada para determinar la versión a partir de los parámetros de la solicitud:


from rest_framework.views import APIView
from django.http import HttpResponse

class CustomVersioner(object):
   def determine_version(self, request, *args, **kwargs):
       version_param = request._request.GET.get("api-version")
       return version_param

class ItemListView(APIView):
   versioning_class = CustomVersioner
   def get(self, request, *args, **kwargs):
       # La versión se accede a través de request.version
       print(f"Versión solicitada: {request.version}")
       return HttpResponse("Lista de Ítems")
   

1.2. Acceso a la Información de la Solicitud en DRF

DRF extiende el objeto de solicitud nativo de Django. Cuando accedes a atributos como query\_params, DRF primero intenta obtenerlos de su propia capa. Si no los encuentra, recurre al objeto de solicitud subyacente de Django.


# Dentro de una solicitud DRF, request.query_params es un alias para request.GET
# Si un atributo no existe en la instancia de DRF, se delega a la solicitud nativa de Django.
class DRFRequestWrapper:
   @property
   def query_params(self):
       return self._original_request.GET

   def __getattr__(self, name):
       try:
           return super().__getattribute__(name)
       except AttributeError:
           return getattr(self._original_request, name)
   

1.3. Clases Integradas de DRF

DRF proporciona clases de versionado predefinidas, simplificando la configuración.

1.3.1. Configuración Global y Uso

Puedes definir la configuración de versionado en tu archivo settings.py:


# settings.py
REST_FRAMEWORK = {
   "DEFAULT_VERSION": "v1",
   "ALLOWED_VERSIONS": ["v1", "v2", "v3"],
   "VERSION_PARAM": "version", # Nombre del parámetro en la query string
}
   

Y en tu vista:


from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning
from django.http import HttpResponse

class ProductListView(APIView):
   versioning_class = QueryParameterVersioning
   def get(self, request, *args, **kwargs):
       print(f"Versión de la API: {request.version}")
       return HttpResponse("Lista de Productos")
   

Esto permitiría solicitudes como /api/products/?version=v2.

1.3.2. Principio de Funcionamiento de QueryParameterVersioning


# fragmento simplificado de QueryParameterVersioning
from rest_framework.versioning import BaseVersioning
from rest_framework import exceptions
from rest_framework.settings import api_settings

class QueryParameterVersioning(BaseVersioning):
   invalid_version_message = "Versión inválida especificada."
   default_version = api_settings.DEFAULT_VERSION
   version_param = api_settings.VERSION_PARAM

   def determine_version(self, request, *args, **kwargs):
       version = request.query_params.get(self.version_param, self.default_version)
       if version not in api_settings.ALLOWED_VERSIONS:
           raise exceptions.NotFound(self.invalid_version_message)
       return version

   # Método reverse para generar URLs con la versión
   def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
       url = super().reverse(viewname, args, kwargs, request, format, **extra)
       if request and request.version:
           # Añade el parámetro de versión a la URL generada
           return f"{url}?{self.version_param}={request.version}"
       return url
   
  1. Versiones Basadas en la URL (Recomendado)

Este enfoque integra la versión directamente en la estructura de la URL, lo cual es más limpio y descriptivo.

2.1. Configuración y Uso

Define tus rutas usando expresiones regulares para capturar la versión:


# urls.py
from django.urls import path, re_path
from . import views

urlpatterns = [
   re_path(r'^(?P<version>(v1|v2))/items/$', views.ItemListView.as_view()),
]
   

Y en tu vista, usa URLPathVersioning:


from rest_framework.views import APIView
from rest_framework.versioning import URLPathVersioning
from django.http import HttpResponse

class ItemListView(APIView):
   versioning_class = URLPathVersioning
   def get(self, request, *args, **kwargs):
       print(f"Versión de la API (desde URL): {request.version}")
       return HttpResponse("Lista de Ítems")
   

Las solicitudes serían como /v1/items/ o /v2/items/.

  1. Flujo Interno en DRF

El manejo de versiones ocurre durante la fase de inicialización de la vista.

3.1. Procesamiento en dispatch e initial

Cuando una solicitud llega, DRF llama al método dispatch, que a su vez llama a initial. Dentro de initial, se determina la versión:


# fragmento simplificado de APIView.dispatch
def dispatch(self, request, *args, **kwargs):
   self.args = args
   self.kwargs = kwargs
   # ...
   self.initial(request, *args, **kwargs)
   # ...

# fragmento simplificado de APIView.initial
def initial(self, request, *args, **kwargs):
   # ...
   self.determine_version(request, *args, **kwargs)
   # ...

# fragmento simplificado de BaseView.determine_version
def determine_version(self, request, *args, **kwargs):
   if self.versioning_class is None:
       version, scheme = None, None
   else:
       scheme = self.versioning_class()
       version = scheme.determine_version(request, *args, **kwargs)
   
   request.version = version
   request.versioning_scheme = scheme
   # ...
   

Aquí, se instancia la clase de versionado configurada (ej. URLPathVersioning) y se llama a su método determine_version. El resultado (la versión y el esquema) se adjunta al objeto request.

3.2. Método determine_version de los Esquemas

Este método es el corazón de cada esquema de versionado. Implementa la lógica específica para extraer la versión (ej., de la URL o de los parámetros GET).

3.3. Acceso a la Versión y Esquema en la Vista

Una vez que initial ha terminaod, puedes acceder a la versión determinada y al objeto de esquema directamente desde el objeto request dentro de tus métodos de vista (get, post, etc.):


# Dentro de un método de vista (ej. get)
def get(self, request, *args, **kwargs):
   current_version = request.version
   versioning_handler = request.versioning_scheme
   print(f"Versión actual: {current_version}")
   # Puedes usar versioning_handler para operaciones avanzadas si es necesario
   return HttpResponse("Datos")
   
  1. Generación de URLs con Versión

DRF facilita la generación de URLs que incluyen la versión correcta, esepcialmente útil para enlaces o redirecciones.

4.1. Uso del Método reverse

Si has configurado el versionado basado en URL y nombrado tus patrones (name='uuu' en el ejemplo), puedes usar el método reverse del esquema de versionado para obtener la URL completa con la versión incluida:


# urls.py
from django.urls import re_path
from api import views

urlpatterns = [
   re_path(r'^(?P<version>(v1|v2))/items/(?P<pk>\d+)/$', views.ItemDetailView.as_view(), name='item-detail'),
]

# views.py
from rest_framework.views import APIView
from rest_framework.versioning import URLPathVersioning
from django.http import HttpResponse

class ItemDetailView(APIView):
   versioning_class = URLPathVersioning
   def get(self, request, pk, *args, **kwargs):
       item_id = pk
       # Obtener el esquema de versionado que se usó
       version_scheme = request.versioning_scheme
       
       # Generar la URL para este recurso con la versión actual
       # Nota: Se debe pasar el 'pk' y cualquier otro kwarg necesario para la URL nombrada
       url_with_version = version_scheme.reverse(
           viewname='item-detail', 
           kwargs={'pk': item_id}, 
           request=request # Pasar la solicitud es crucial para que reverse sepa la versión
       )
       print(f"URL generada: {url_with_version}")
       
       return HttpResponse(f"Detalle del ítem {item_id}")
   

Cuando se accede a /v1/items/123/, el request.version será 'v1'. Al llamar a reverse, DRF utilizará esta información para construir la URL correcta, por ejemplo, /v1/items/123/.

Etiquetas: DRF Django REST Framework API Versioning URL Versioning Query Parameter Versioning

Publicado el 7-24 22:39