Una vez que el enrutamiento ha determinado qué controlador usar para una solicitud, su controlador es responsable de dar sentido a la solicitud y producir la salida adecuada.
El marco Django REST le permite combinar la lógica para un conjunto de vistas relacionadas en una sola clase, llamada ViewSet
. En otros marcos, también puede encontrar implementaciones conceptualmente similares denominadas algo como ‘Recursos’ o ‘Controladores’.
A ViewSet
la clase es simplemente un tipo de vista basada en clases, que no proporciona ningún controlador de método tal como .get()
o .post()
, y en su lugar proporciona acciones como .list()
y .create()
.
Los manejadores de métodos para un ViewSet
sólo están vinculados a las acciones correspondientes en el momento de finalizar la vista, utilizando el .as_view()
método.
Por lo general, en lugar de registrar explícitamente las vistas en un conjunto de vistas en la urlconf, registrará el conjunto de vistas con una clase de enrutador, que determina automáticamente la urlconf por usted.
Ejemplo
Definamos un conjunto de vistas simple que se pueda usar para enumerar o recuperar todos los usuarios del sistema.
from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 from myapps.serializers import UserSerializer from rest_framework import viewsets from rest_framework.response import Response class UserViewSet(viewsets.ViewSet): """ A simple ViewSet for listing or retrieving users. """ def list(self, request): queryset = User.objects.all() serializer = UserSerializer(queryset, many=True) return Response(serializer.data) def retrieve(self, request, pk=None): queryset = User.objects.all() user = get_object_or_404(queryset, pk=pk) serializer = UserSerializer(user) return Response(serializer.data)
Si es necesario, podemos vincular este conjunto de vistas en dos vistas separadas, así:
user_list = UserViewSet.as_view({'get': 'list'}) user_detail = UserViewSet.as_view({'get': 'retrieve'})
Por lo general, no haríamos esto, sino que registraríamos el conjunto de vistas con un enrutador y permitiríamos que la urlconf se generara automáticamente.
from myapp.views import UserViewSet from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'users', UserViewSet, basename='user') urlpatterns = router.urls
En lugar de escribir sus propios conjuntos de vistas, a menudo querrá utilizar las clases base existentes que proporcionan un conjunto de comportamiento predeterminado. Por ejemplo:
class UserViewSet(viewsets.ModelViewSet): """ A viewset for viewing and editing user instances. """ serializer_class = UserSerializer queryset = User.objects.all()
Hay dos ventajas principales de utilizar un ViewSet
clase sobre el uso de un View
clase.
- La lógica repetida se puede combinar en una sola clase. En el ejemplo anterior, solo necesitamos especificar el
queryset
una vez y se utilizará en varias vistas. - Al usar enrutadores, ya no tenemos que lidiar con el cableado de la URL conf.
Ambos vienen con una compensación. El uso de vistas regulares y confs de URL es más explícito y le brinda más control. Los ViewSets son útiles si desea comenzar a funcionar rápidamente, o cuando tiene una API grande y desea aplicar una configuración de URL coherente en todo momento.
Acciones ViewSet
Los enrutadores predeterminados incluidos con el marco REST proporcionarán rutas para un conjunto estándar de acciones de estilo de creación / recuperación / actualización / destrucción, como se muestra a continuación:
class UserViewSet(viewsets.ViewSet): """ Example empty viewset demonstrating the standard actions that will be handled by a router class. If you're using format suffixes, make sure to also include the `format=None` keyword argument for each action. """ def list(self, request): pass def create(self, request): pass def retrieve(self, request, pk=None): pass def update(self, request, pk=None): pass def partial_update(self, request, pk=None): pass def destroy(self, request, pk=None): pass
Introspección de las acciones de ViewSet
Durante el envío, los siguientes atributos están disponibles en el ViewSet
.
-
basename
– la base que se utilizará para los nombres de URL que se crean. -
action
– el nombre de la acción actual (p. ej.,list
,create
). -
detail
– booleano que indica si la acción actual está configurada para una lista o vista detallada. -
suffix
– el sufijo de pantalla para el tipo de vista – refleja eldetail
atributo. -
name
: el nombre para mostrar del conjunto de vistas. Este argumento es mutuamente excluyente desuffix
. -
description
– la descripción de visualización para la vista individual de un conjunto de vistas.
Puede inspeccionar estos atributos para ajustar el comportamiento según la acción actual. Por ejemplo, puede restringir los permisos a todo excepto a los list
acción similar a esta:
def get_permissions(self): """ Instantiates and returns the list of permissions that this view requires. """ if self.action == 'list': permission_classes = [IsAuthenticated] else: permission_classes = [IsAdmin] return [permission() for permission in permission_classes]
Marcar acciones adicionales para el enrutamiento
Si tiene métodos ad-hoc que deberían ser enrutables, puede marcarlos como tales con el @action
decorador. Al igual que las acciones normales, las acciones adicionales pueden estar destinadas a un solo objeto o a una colección completa. Para indicar esto, configure el detail
argumento para True
o False
. El enrutador configurará sus patrones de URL en consecuencia. por ejemplo, el DefaultRouter
configurará acciones detalladas para contener pk
en sus patrones de URL.
Un ejemplo más completo de acciones adicionales:
from django.contrib.auth.models import User from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from myapp.serializers import UserSerializer, PasswordSerializer class UserViewSet(viewsets.ModelViewSet): """ A viewset that provides the standard actions """ queryset = User.objects.all() serializer_class = UserSerializer @action(detail=True, methods=['post']) def set_password(self, request, pk=None): user = self.get_object() serializer = PasswordSerializer(data=request.data) if serializer.is_valid(): user.set_password(serializer.data['password']) user.save() return Response({'status': 'password set'}) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(detail=False) def recent_users(self, request): recent_users = User.objects.all().order_by('-last_login') page = self.paginate_queryset(recent_users) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(recent_users, many=True) return Response(serializer.data)
Además, el decorador puede tomar argumentos adicionales que se establecerán solo para la vista enrutada. Por ejemplo:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf]) def set_password(self, request, pk=None): ...
los action
el decorador enrutará GET
solicitudes de forma predeterminada, pero también puede aceptar otros métodos HTTP configurando el methods
argumento. Por ejemplo:
@action(detail=True, methods=['post', 'delete']) def unset_password(self, request, pk=None): ...
Las dos nuevas acciones estarán disponibles en las URL ^users/{pk}/set_password/$
y ^users/{pk}/unset_password/$
Para ver todas las acciones adicionales, llame al .get_extra_actions()
método.
Enrutamiento de métodos HTTP adicionales para acciones adicionales
Las acciones adicionales pueden asignar métodos HTTP adicionales para separar ViewSet
métodos. Por ejemplo, los métodos de activación / desactivación de contraseña anteriores podrían consolidarse en una única ruta. Tenga en cuenta que las asignaciones adicionales no aceptan argumentos.
@action(detail=True, methods=['put'], name='Change Password') def password(self, request, pk=None): """Update the user's password.""" ... @password.mapping.delete def delete_password(self, request, pk=None): """Delete the user's password.""" ...
Inversión de URL de acción
Si necesita obtener la URL de una acción, utilice la .reverse_action()
método. Este es un envoltorio de conveniencia para reverse()
, pasando automáticamente la vista request
objeto y anteponiendo el url_name
con el .basename
atributo.
Tenga en cuenta que el basename
es proporcionado por el enrutador durante ViewSet
registro. Si no está utilizando un enrutador, debe proporcionar el basename
argumento a la .as_view()
método.
Usando el ejemplo de la sección anterior:
>>> view.reverse_action('set-password', args=['1']) 'http://localhost:8000/api/users/1/set_password'
Alternativamente, puede utilizar el url_name
atributo establecido por el @action
decorador.
>>> view.reverse_action(view.set_password.url_name, args=['1']) 'http://localhost:8000/api/users/1/set_password'
los url_name
argumento a favor .reverse_action()
debe hacer coincidir el mismo argumento con el @action
decorador. Además, este método se puede utilizar para revertir las acciones predeterminadas, como list
y create
.
Referencia de API
ViewSet
los ViewSet
la clase hereda de APIView
. Puede utilizar cualquiera de los atributos estándar, como permission_classes
, authentication_classes
para controlar la política de API en el conjunto de vistas.
los ViewSet
la clase no proporciona ninguna implementación de acciones. Para utilizar un ViewSet
class anulará la clase y definirá las implementaciones de acción explícitamente.
GenericViewSet
los GenericViewSet
la clase hereda de GenericAPIView
y proporciona el conjunto predeterminado de get_object
, get_queryset
métodos y otro comportamiento básico de vista genérico, pero no incluye ninguna acción de forma predeterminada.
Para utilizar un GenericViewSet
class anulará la clase y mezclará las clases mixin requeridas, o definirá las implementaciones de acción explícitamente.
ModelViewSet
los ModelViewSet
la clase hereda de GenericAPIView
e incluye implementaciones para varias acciones, mezclando el comportamiento de las diversas clases de mixin.
Las acciones proporcionadas por el ModelViewSet
clase son .list()
, .retrieve()
, .create()
, .update()
, .partial_update()
, y .destroy()
.
Ejemplo
Porque ModelViewSet
se extiende GenericAPIView
, normalmente deberá proporcionar al menos el queryset
y serializer_class
atributos. Por ejemplo:
class AccountViewSet(viewsets.ModelViewSet): """ A simple ViewSet for viewing and editing accounts. """ queryset = Account.objects.all() serializer_class = AccountSerializer permission_classes = [IsAccountAdminOrReadOnly]
Tenga en cuenta que puede utilizar cualquiera de los atributos estándar o anulaciones de métodos proporcionados por GenericAPIView
. Por ejemplo, para usar un ViewSet
que determina dinámicamente el conjunto de consultas en el que debe operar, puede hacer algo como esto:
class AccountViewSet(viewsets.ModelViewSet): """ A simple ViewSet for viewing and editing the accounts associated with the user. """ serializer_class = AccountSerializer permission_classes = [IsAccountAdminOrReadOnly] def get_queryset(self): return self.request.user.accounts.all()
Sin embargo, tenga en cuenta que al retirar el queryset
propiedad de su ViewSet
, cualquier enrutador asociado no podrá derivar el nombre base de su modelo automáticamente, por lo que tendrá que especificar el basename
kwarg como parte del registro de su enrutador.
También tenga en cuenta que, aunque esta clase proporciona el conjunto completo de acciones de creación / lista / recuperación / actualización / destrucción de forma predeterminada, puede restringir las operaciones disponibles utilizando las clases de permisos estándar.
ReadOnlyModelViewSet
los ReadOnlyModelViewSet
la clase también hereda de GenericAPIView
. Al igual que con ModelViewSet
también incluye implementaciones para varias acciones, pero a diferencia de ModelViewSet
solo proporciona las acciones de ‘solo lectura’, .list()
y .retrieve()
.
Ejemplo
Al igual que con ModelViewSet
, normalmente deberá proporcionar al menos el queryset
y serializer_class
atributos. Por ejemplo:
class AccountViewSet(viewsets.ReadOnlyModelViewSet): """ A simple ViewSet for viewing accounts. """ queryset = Account.objects.all() serializer_class = AccountSerializer
De nuevo, como con ModelViewSet
, puede utilizar cualquiera de los atributos estándar y anulaciones de métodos disponibles para GenericAPIView
.
Es posible que deba proporcionar un ViewSet
clases que no tienen el conjunto completo de ModelViewSet
acciones, o que personalicen el comportamiento de alguna otra manera.
Ejemplo
Para crear una clase de conjunto de vistas base que proporcione create
, list
y retrieve
operaciones, heredar de GenericViewSet
y mezclar las acciones necesarias:
from rest_framework import mixins class CreateListRetrieveViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ A viewset that provides `retrieve`, `create`, and `list` actions. To use it, override the class and set the `.queryset` and `.serializer_class` attributes. """ pass
Creando tu propia base ViewSet
clases, puede proporcionar un comportamiento común que se puede reutilizar en varios conjuntos de vistas en su API.