Django REST Framework Views - Generic Views

Часто используемые представления, которые тесно связаны с моделями баз данных (например, создание экземпляра модели, его удаление, перечисление экземпляров и т.д.), уже предварительно встроены в представления Django REST Framework (DRF). Эти элементы повторно используемого поведения называются Универсальными представлениями.

Вы можете использовать общие представления в качестве строительных блоков - GenericAPIView и миксины - для создания собственных представлений. Или вы можете использовать подключаемые и воспроизводимые конкретные представления, которые уже объединили GenericAPIView и соответствующие миксины.

Серия просмотров фреймворка Django REST:

  1. Просмотры приложений
  2. Общие представления (эта статья!)
  3. Наборы для просмотра

Цели

К концу этой статьи вы должны быть в состоянии объяснить:

  1. Что такое GenericAPIView, что такое миксины и как они объединяются для создания конкретных представлений
  2. Какие микшеры вы используете и на что они способны
  3. Как создать пользовательский микшер и как им пользоваться
  4. Какие универсальные представления вы можете использовать и что они делают

Общие представления

Общие представления - это набор часто используемых шаблонов.

Они построены поверх класса APIView, который мы представили в предыдущей статье этой серии.

Их цель состоит в том, чтобы вы могли быстро создавать представления API, которые точно соответствуют вашим моделям баз данных, не повторяясь.

Они состоят из GenericAPIView, смешивания и конкретных видов:

  1. GenericAPIView это более загруженная версия APIView. Сама по себе она не очень полезна, но может использоваться для создания действий многократного использования.
  2. Миксины - это элементы обычного поведения. Они бесполезны без GenericAPIView.
  3. Конкретные представления объединяются GenericAPIView с соответствующими миксинами для создания представлений, часто используемых в API.

DRF использует разные названия для конкретных представлений. В документации и комментариях к коду их можно найти как конкретные классы представлений, конкретные общие представления или конкретные представления.

Поскольку названия очень похожи, их легко перепутать. Общие представления - это слово, которое обозначает как смешанные, так и конкретные представления. При использовании общих представлений конкретные представления - это, вероятно, тот уровень, с которым вы будете работать.

Общий обзор

GenericAPIView является базовым классом для всех других универсальных представлений. Он предоставляет методы, такие как get_object/get_queryset и get_serializer. Несмотря на то, что он предназначен для использования в сочетании с микшерами (поскольку он используется в общих представлениях), его можно использовать отдельно:

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

class RetrieveDeleteItem(GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

    def get(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    def delete(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

При расширении GenericAPIView, queryset и serializer_class должны быть установлены значения. В качестве альтернативы вы можете перезаписать get_queryset()/get_serializer_class().

Поскольку существует несколько миксинов, предназначенных для использования с GenericAPIView, я не рекомендую изобретать велосипед и использовать его сам по себе.

Миксины

Микшины обеспечивают общее поведение. Их нельзя использовать отдельно; они должны быть объединены с GenericAPIView для создания функционального представления. Хотя классы mixin предоставляют действия по созданию/извлечению/обновлению/удалению, вам все равно необходимо привязать соответствующие действия к методам.

Доступные миксины:

Mixin Usage
CreateModelMixin Create a model instance
ListModelMixin List a queryset
RetrieveModelMixin Retrieve a model instance
UpdateModelMixin Update a model instance
DestroyModelMixin Delete a model instance

Вы можете использовать только один из них или комбинировать их для достижения желаемого результата.

Вот пример того, как выглядит миксинг:

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

Как вы можете видеть, RetrieveModelMixin предоставляет функцию (действие), называемую retrieve, которая извлекает объект из базы данных и возвращает его в сериализованном виде.

ListModelMixin и CreateModelMixin

ListModelMixin реализует действие, которое возвращает сериализованное представление набора запросов (необязательно с разбивкой на страницы).

CreateModelMixin реализует действие, которое создает и сохраняет новый экземпляр модели.

Часто они используются вместе для создания конечной точки API для создания списка:

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class CreateListItems(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

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

В CreateListItems мы использовали serializer_class и queryset, предоставленные GenericAPIView.

Мы сами определили get и post методы, которые использовали list и create действия, предоставляемые миксинами:

  • CreateModelMixin обеспечивает create действие
  • ListModelMixin обеспечивает list действие

Привязка действий к методам

Вы несете ответственность за привязку действий к методам.

Теоретически это означает, что вы могли бы связать методы POST с действиями list и методы GET с действиями create, и все бы "вроде как" сработало.

Например:

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class CreateList(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

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

Это приведет к следующим результатам...

Используя метод GET:

DRF Browsable API

Используя метод POST:

DRF Browsable API

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: То, что это возможно, не означает, что я советую вам это делать. Единственной целью этого было показать вам, как работают методы привязки и действия.

Извлекаем modelmixin, обновляем modelmixin и уничтожаем modelmixin

Извлекаем modelmixin, Обновляем modelmixin и Уничтожаем modelmixin все они работают с одним экземпляром модели .

RetrieveModelMixin и UpdateModelMixin оба возвращают сериализованные представления объекта, в то время как DestroyModelMixin, в случае успеха, возвращает HTTP_204_NO_CONTENT.

Вы можете использовать один из них или комбинировать их по своему усмотрению.

В этом примере мы объединили все три в единую конечную точку для каждого действия, которое возможно для подробного просмотра:

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class RetrieveUpdateDeleteItem(
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    GenericAPIView
):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

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

Предусмотренные действия:

  • RetrieveModelMixin обеспечивает retrieve действие
  • UpdateModelMixin обеспечивает update и partial_update действия
  • DestroyModelMixin обеспечивает destroy действие

Таким образом, с помощью конечной точки RetrieveUpdateDeleteItem пользователь может извлекать, обновлять или удалять отдельный элемент.

Вы также можете ограничить просмотр определенными действиями:

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class RetrieveUpdateItem(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

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

В этом примере мы опустили DestroyModelMixin и использовали только update действие из UpdateModelMixin.

Группировка миксинов

Хорошей идеей является наличие единого представления для обработки всех экземпляров - перечисления всех экземпляров и добавления нового экземпляра - и другого представления для обработки одного экземпляра - извлечения, обновления и удаления отдельных экземпляров.

Тем не менее, вы можете комбинировать миксины так, как считаете нужным. Например, вы могли бы объединить RetrieveModelMixin и CreateModelMixin миксины:

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class RetrieveCreate(mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

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

Это создает конечную точку для извлечения одного экземпляра и добавления новых экземпляров:

DRF Browsable API

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Опять же, то, что это возможно, не означает, что это хорошая идея. Приведенный выше пример предназначен исключительно для образовательных целей. Я не советую использовать его в производственном коде.

Пользовательские миксины

В реальных приложениях велика вероятность того, что вам потребуется какое-то пользовательское поведение, причем в нескольких местах. Вы можете создать пользовательский микшер, чтобы вам не нужно было повторять свой код и включать его в свой класс представления.

Допустим, вы хотите использовать другой сериализатор в зависимости от метода запроса. Вы могли бы добавить в представление несколько инструкций if, но это может быстро привести к путанице. Кроме того, через два месяца вы добавите еще одну модель, и вам нужно будет повторить то же самое.

В этом случае рекомендуется создать пользовательский миксинг, чтобы сопоставить сериализаторы с методами запроса:

class SerializerByMethodMixin:
    def get_serializer_class(self, *args, **kwargs):

        return self.serializer_map.get(self.request.method, self.serializer_class)

Здесь мы заменили метод get_serializer_class из класса GenericAPIView.

Может быть, вы хотите переопределить другие методы, такие как get_queryset или get_object? Взгляните на код внутри класса GenericAPIView, где создатели DRF указывают, какие методы вы, возможно, захотите переопределить.

Теперь вам просто нужно добавить SerializerByMethodMixin в свой вид и установить атрибут serializer_map:

class ListCreateItems(SerializerByMethodMixin, ListCreateAPIView):

    queryset = Item.objects.all()
    serializer_map = {
        'GET': GetItemSerializer,
        'POST': PostItemSerializer,
    }

Убедитесь, что вы включили микширование в качестве первого параметра, чтобы его методы не были переопределены (сначала более высокий приоритет).

Пользовательский базовый класс

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

Например:

class BaseCreateListView((MixinSingleOrListSerializer, ListCreateAPIView)):
    pass

Конкретные виды

Конкретные представления выполняют большую часть работы, которую нам необходимо выполнить самостоятельно при использовании APIView. Они используют миксины в качестве основных строительных блоков, объединяют строительные блоки с помощью GenericAPIView и привязывают действия к методам.

Взгляните на код для одного из конкретных классов представлений, ListCreateAPIView:

# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/generics.py#L232

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):

    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. Они также определяют каждый из соответствующих методов и привязывают к ним соответствующие действия.

Если вам не нужно сильно настраиваемое поведение, то это наилучшее представление, если вы используете универсальные представления.

Существует девять классов, каждый из которых предоставляет комбинацию действий, которая может вам понадобиться:

Class Usage Method handler Extends mixin
CreateAPIView create-only post CreateModelMixin
ListAPIView read-only for multiple instances get ListModelMixin
RetrieveAPIView read-only for single instance get RetrieveModelMixin
DestroyAPIView delete-only for single instance delete DestroyModelMixin
UpdateAPIView update-only for single instance put, patch UpdateModelMixin
ListCreateAPIView read-write for multiple instances get, post CreateModelMixin, ListModelMixin
RetrieveUpdateAPIView read-update for single instance get, put, patch RetrieveModelMixin, UpdateModelMixin
RetrieveDestroyAPIView read-delete for single instance get, delete RetrieveModelMixin, DestroyModelMixin
RetrieveUpdateDestroyAPIView read-update-delete for single instance get, put, patch, delete RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

Вот еще одна полезная таблица, которая показывает, к какому классу привязан обработчик конкретного метода:

Class get post put/patch delete
CreateAPIView      
ListAPIView      
RetrieveAPIView      
DestroyAPIView      
UpdateAPIView      
ListCreateAPIView    
RetrieveUpdateAPIView    
RetrieveDestroyAPIView    
RetrieveUpdateDestroyAPIView  

Всем классам, которые расширяются из конкретного представления, требуется:

  • набор запросов
  • класс сериализатора

Кроме того, вы можете указать атрибуты политики, как описано в первой статье этой серии.

Далее мы рассмотрим примеры каждого из девяти конкретных представлений в действии.

Создать просмотр

Расширив здесь CreateAPIView, мы создали конечную точку, в которой пользователь может создать новый элемент:

from rest_framework.generics import CreateAPIView
from rest_framework.permissions import IsAdminUser

class CreateItem(CreateAPIView):
    permission_classes = [IsAdminUser]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Мы добавили атрибут политики, чтобы конечная точка была доступна только администратору.

Просмотр списка

Здесь мы расширили ListAPIView, чтобы создать конечную точку, в которой перечислены все "незавершенные" элементы:

from rest_framework.generics import ListAPIView

class ItemsNotDone(ListAPIView):

    queryset = Item.objects.all().filter(done=False)
    serializer_class = ItemSerializer

Наш набор запросов фильтруется на основе поля done. Он не содержит никаких дополнительных политик, поэтому доступен любому пользователю.

Имейте в виду, что каждое из этих представлений должно быть включено в URL-адреса по отдельности:

# urls.py

from django.urls import path
from .views import ListItems

urlpatterns = [
   path('all-items', ListItems.as_view())
]

RetrieveAPIView

В то время как ListAPIView возвращает список всех элементов, RetrieveAPIView предназначен для извлечения одного элемента:

from rest_framework.generics import RetrieveAPIView

class SingleItem(RetrieveAPIView):

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Пример urls.py для RetrieveAPIView (и других представлений для одного экземпляра):

from django.urls import path
from .views import SingleItem
>
urlpatterns = [
   path('items/<pk>', SingleItem.as_view())
]

Уничтожить просмотр

Расширение DestroyAPIView создает конечную точку с единственной целью удаления одного элемента:

from rest_framework.generics import DestroyAPIView
from rest_framework.permissions import IsAuthenticated


class DeleteItem(DestroyAPIView):
    permission_classes = [IsAuthenticated]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Доступ ограничен только для прошедших проверку подлинности пользователей.

Обновить просмотр

Конечная точка для обновления отдельного элемента:

from rest_framework.generics import UpdateAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import UserRateThrottle


class UpdateItem(UpdateAPIView):

    permission_classes = [IsAuthenticated]
    throttle_classes = [UserRateThrottle]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

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

ListCreateAPIView

ListCreateAPIView является первым конкретным классом представления с более чем одной ответственностью, перечисляющим все элементы и создающим новый элемент:

from rest_framework.generics import ListCreateAPIView

class ListCreateItems(ListCreateAPIView):

    authentication_classes = [TokenAuthentication]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

В этом примере пользователь проходит проверку подлинности с помощью токена.

Получить обновлениеapiview

Расширение RetrieveUpdateAPIView создает конечную точку для извлечения и обновления одного элемента:

from rest_framework.generics import RetrieveUpdateAPIView

class RetrieveUpdateItem(RetrieveUpdateAPIView):
    renderer_classes = [JSONRenderer]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Здесь мы использовали атрибут policy для возврата данных в формате JSON.

Извлеченный файл Destroyapiview

Расширив здесь RetrieveDestroyAPIView, мы создали конечную точку для извлечения и удаления одного элемента:

from rest_framework.generics import RetrieveDestroyAPIView

class RetrieveDeleteItem(RetrieveDestroyAPIView):

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Извлеките обновленный файл Destroyapiview

Здесь, расширив RetrieveUpdateDestroyAPIView, мы создали конечную точку, в которой доступны все возможные действия для одного элемента: извлечение, (частично) обновление и удаление.

from rest_framework.generics import RetrieveUpdateDestroyAPIView

class RetrieveUpdateDeleteItem(RetrieveUpdateDestroyAPIView):

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

Заключение

Общие представления предлагают множество готовых решений.

Если у вас нет каких-либо особых требований, то лучше всего использовать конкретные виды (например, RetrieveDestroyAPIView). Если вам нужно что-то менее строгое, вы можете использовать конкретные классы в качестве строительных блоков - GenericAPIView и миксины (например, UpdateModelMixin). Это все равно сэкономит некоторую работу по сравнению с простым использованием класса APIView.

DRF Generic Views Overview

Серия просмотров фреймворка Django REST:

  1. Просмотры приложений
  2. Общие представления (эта статья!)
  3. Наборы для просмотра
Вернуться на верх