September 6, 2019

DRF - Views на основе классов

Django rest framework - Class based views

Мы также можем написать наши views API, используя views на основе классов. Как мы увидим, это мощный шаблон, который позволяет нам повторно использовать обычные функции и помогает нам сохранять принцип DRY.

Перепишем наш API с использованием views на основе классов

Мы начнем с переписывания корневого представления как представления на основе классов. Все это включает в себя небольшой рефакторинг views.py.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class SnippetList(APIView):
    """
    Показать все сниппеты или создать новый.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Это довольно похоже на предыдущий случай, но мы получили лучшее разделение между методами HTTP. Нам также нужно обновить views Snippet в views.py.

class SnippetDetail(APIView):
    """
    Получить, обновить или удалить сниппет.
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Выглядит хорошо. Опять же, это все еще очень похоже на функциональное представление.

Нам также нужно будет немного реорганизовать наш snippets/urls.py:

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

Если вы запустите сервер разработки, все должно работать так же, как и раньше.

Использование миксинов

Одним из главных преимуществ использования views на основе классов является то, что он позволяет нам легко составлять многократно используемые фрагменты поведения.

Операции создания / извлечения / обновления / удаления, которые мы использовали до сих пор, будут очень похожи для любых представлений API на основе модели. Эти кусочки общего поведения реализованы в смешанных классах инфраструктуры REST. Давайте посмотрим, как мы можем составить представления с помощью классов mixin.

Вот снова наш модуль views.py:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

Давайте остановимся на минутку, чтобы выяснить, что именно здесь происходит.

Мы создаем наше views, используя GenericAPIView, и добавляем в ListModelMixin и CreateModelMixin. Базовый класс обеспечивает основные возможности, а классы mixin предоставляют функции .list () и .create (). Затем мы явно привязываем методы get и post к соответствующим функциям. Пока все достаточно просто.

class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Ситуация аналогичная. Мы снова используем класс GenericAPIView для обеспечения основных возможностей и добавляем миксины для предоставления функций .retrieve (), .update () и .destroy ().

Использование generic (общих представлений)

Используя mixin, мы переписали views так, чтобы использовать немного меньше кода, чем раньше, но мы можем пойти еще дальше.

Платформа REST предоставляет набор уже смешанных универсальных представлений, которые мы можем использовать, чтобы еще больше урезать наш модуль views.py.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

Вау, это довольно лаконично. Мы получили огромное количество функционала, и наш код выглядит хорошо и чисто.

Далее мы перейдем к четвертой части, где мы рассмотрим, как мы можем работать с аутентификацией и разрешениями для нашего API.