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.

Documentación de Ruby on Rails

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 el detail atributo.
  • name : el nombre para mostrar del conjunto de vistas. Este argumento es mutuamente excluyente de suffix.
  • 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 GenericAPIViewy 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 GenericViewSety 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.

viewsets.py