Продолжаю переводить официальное руководство по django-rest-framework. Статья Tutorial 3: Class-based Views или базовый класс Views.
Мы также можем писать представления API, используя представления на основе классов, а не на основе функций. Как мы увидим, это мощный шаблон, который позволяет нам повторно использовать общие функции и помогает нам сохранить наш код чистым DRY (не повторяйся).
Начнем с перезаписи основного представления как представления на основе классов. Все это включает немного рефакторинга views.py.
# view
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.
# view
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 когда мы используем класс на основе представлений.
# 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//', views.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
Ладно, мы закончили. Если вы запустите сервер разработки, все должно работать как и прежде.
Одним из больших преимуществ использования представлений на основе классов является то, что он позволяет нам легко создавать многоразовые куски поведения. Операции create/retrieve/update/delete, которые мы использовали до сих пор, будут очень похожи на любые созданные нами представления API с поддержкой моделей. Эти биты общего поведения реализованы в классах mixin Rest framework. Давайте посмотрим, как мы можем составить представления с помощью классов mixin. Вот наши views.py опять модуль.
# 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.
Базовый класс предоставляет основные функциональные возможности, а классы mixin предоставляют .list() и .create() действия. Затем мы явно привязываем методы get и post к соответствующим действиям. Пока все достаточно просто.
# views.py
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 для предоставления основных функций и добавляем mixins для .retrieve(), .update() и .destroy() действий.
Использование универсального базового класса в представлениях
Using generic class-based views
Использование универсальных представлений на основе классов. Используя классы mixin, мы переписали представления, чтобы использовать немного меньше кода, чем раньше, но мы можем сделать еще один шаг вперед. Rest framework предоставляет набор уже смешанных общих представлений, которые можно использовать для обрезки views.py модуля еще больше.
# 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.