Учебник 3: Представления на основе классов¶
Мы также можем писать наши представления API, используя представления на основе классов, а не на основе функций. Как мы увидим, это мощный паттерн, который позволяет нам повторно использовать общую функциональность и помогает нам сохранить наш код DRY.
Переписывание нашего API с использованием представлений на основе классов¶
Мы начнем с переписывания корневого представления в представление на основе классов. Все, что для этого нужно, это немного рефакторинга 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):
"""
List all snippets, or create a new snippet.
"""
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.py
.
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
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)
Итак, мы закончили. Если вы запустите сервер разработки, все должно работать как прежде.
Использование миксинов¶
Одним из главных преимуществ использования представлений на основе классов является то, что они позволяют нам легко составлять многократно используемые фрагменты поведения.
Операции создания/получения/обновления/удаления, которые мы использовали до сих пор, будут довольно похожими для всех создаваемых нами представлений API, основанных на моделях. Эти части общего поведения реализованы в классах-миксинах REST framework.
Давайте посмотрим, как мы можем компоновать представления с помощью классов-миксинов. Вот наш модуль 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)
Сейчас мы рассмотрим, что именно здесь происходит. Мы строим наше представление, используя GenericAPIView
, и добавляем ListModelMixin
и CreateModelMixin
.
Базовый класс обеспечивает основную функциональность, а классы-миксины обеспечивают действия .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()
.
Использование общих представлений на основе классов¶
Используя классы-миксины, мы переписали представления, чтобы использовать немного меньше кода, чем раньше, но мы можем пойти еще на один шаг дальше. REST framework предоставляет набор уже смешанных общих представлений, которые мы можем использовать, чтобы еще больше сократить наш модуль 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
Вау, это довольно лаконично. Мы получили огромное количество бесплатно, и наш код выглядит как хороший, чистый, идиоматический Django.
Далее мы перейдем к :doc:`part 4 of the tutorial <4-authentication-and-permissions>`** , где мы рассмотрим, как мы можем работать с аутентификацией и разрешениями для нашего API.