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.
- 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
- 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/.
- 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")
- 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/.