Django REST Framework Views - APIViews

Фреймворк Django REST Framework (DRF) имеет свой собственный набор представлений, которые наследуются от класса View Django. В этой серии из трех частей подробно рассматриваются все возможности DRF view - от простого представления, где многое приходится делать самостоятельно, до ModelViewSet, где вы можете настроить представление и запустить его с помощью всего нескольких строк кода. Поскольку виды построены один поверх другого, в этой серии также объясняется, как они переплетаются.

В этой статье мы рассмотрим, как работают представления DRF, и познакомимся с самыми основными представлениями, APIView.

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

  1. APIViews (эта статья)
  2. Общие представления (GenericVIews)
  3. Наборы представлений (ViewSet)

Цели

К концу этой статьи вы должны уметь:

  1. Объясните, как работают представления DRF
  2. Объясните назначение класса APIView и чем он отличается от класса View в Django
  3. Используйте представления, основанные на функциях и классах
  4. Используйте декораторы политик (для представлений, основанных на функциях) и атрибуты политик (для представлений, основанных на классах)

Представления DRF

Важным компонентом представлений DRF является класс APIView, который является подклассом класса View в Django.

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

Будь то-

  • представления, основанные на функциях
  • представления, основанные на классах
  • миксины
  • общие классы представлений
  • наборы представлений

- все они используют класс APIView.

Как вы можете видеть из приведенного ниже изображения, возможности, которыми вы располагаете в отношении видов DRF, переплетаются и дополняют друг друга. Вы можете рассматривать виды как строительные блоки, которые образуют более крупные строительные блоки. При этом вы, вероятно, будете использовать некоторые строительные блоки, такие как представления API, конкретные представления и (только для чтения) наборы Modelviews, чаще, чем другие, такие как mixins и GenericViewSets. Конечно, все это зависит от потребностей вашего конкретного приложения.

DRF Views Overview

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

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

С помощью ModelViewSets вы можете настроить API с помощью пяти строк кода (три для ваших представлений, две для URL-адресов).

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

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

В документации представления DRF разделены на три части. Статьи в этой серии имеют одинаковую структуру.

  1. Документация для APIViews (часть 1 этой серии)
  2. Документация для общих представлений (часть 2 этой серии)
  3. Документация по наборам представлений (часть 3 этой серии)

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

Наряду с руководством по API, у вас также есть официальное руководство, которое охватывает все три вида:

  1. Учебное пособие по использованию APIViews
  2. Учебное пособие с использованием универсальных представлений
  3. Учебное пособие по использованию ViewSets

Давайте начнем с самого простого представления, APIView, за которым последует объяснение того, как работает это представление.

Представления на основе классов

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

Например, предположим, что у вас есть класс Item для вашего API списка покупок:

class Item(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)
    name = models.CharField(max_length=100)
    done = models.BooleanField()

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

from rest_framework.response import Response
from rest_framework.views import APIView

class DeleteAllItems(APIView):

    def delete(self, request):

        Item.objects.all().delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

И вот представление, в котором перечислены все элементы:

from rest_framework.response import Response
from rest_framework.views import APIView

class ListItems(APIView):

    def get(self, request):
        items = Item.objects.all()
        serializer = ItemSerializer(items, many=True)
        return Response(serializer.data)

Как вы можете видеть, вызов базы данных выполняется внутри функций-обработчиков. Они выбираются в соответствии с HTTP-методом запроса (например, GET -> gt, DELETE -> удалить).

Вскоре мы подробнее расскажем о том, как работают эти представления.

Как вы можете видеть, мы установили сериализатор во втором представлении. Сериализаторы отвечают за преобразование сложных данных (например, наборов запросов и экземпляров моделей) в собственные типы данных Python, которые затем могут быть преобразованы в JSON, XML или другие типы контента.

Вы можете узнать больше о сериализаторах DRF в статье Эффективное использование сериализаторов Django REST Framework.

Атрибуты политики

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

Можно задать следующие атрибуты политики:

Attribute Usage Examples
renderer_classes determines which media types the response returns JSONRenderer, BrowsableAPIRenderer
parser_classes determines which data parsers for different media types are allowed JSONParser, FileUploadParser
authentication_classes determines which authentication schemas are allowed for identifying the user TokenAuthentication, SessionAuthentication
throttle_classes determines if a request should be authorized based on the rate of requests AnonRateThrottle, UserRateThrottle
permission_classes determines if a request should be authorized based on user credentials IsAuthenticated, DjangoModelPermissions
content_negotiation_class selects one of the multiple possible representations of the resource to return to a client (unlikely you'll want to set it up) only custom content negotiation classes

Обязательно ознакомьтесь со статьей Пользовательские классы разрешений в Django REST Framework, чтобы узнать больше о классах разрешений.

В следующем примере мы изменили разрешения и способ отображения ответа с помощью атрибутов политики permission_classes и renderer_classes:

from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView

class ItemsNotDone(APIView):

    permission_classes = [IsAuthenticated]  # policy attribute
    renderer_classes = [JSONRenderer]       # policy attribute

    def get(self, request):

        user_count = Item.objects.filter(done=False).count()
        content = {'not_done': user_count}

        return Response(content)

Представления на основе функций

Существует два способа прямой реализации APIView: с помощью функции или с помощью класса. Если вы пишете представление в виде функции, вам нужно будет использовать @api_view декоратор.

@api_view это декоратор, который преобразует функциональное представление в APIView подкласс (таким образом, предоставляя классы Response и Request). В качестве аргумента он принимает список разрешенных методов для представления.

Интересно, как DRF преобразует представления на основе функций в подклассы APIView?

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

def api_view(http_method_names=None):
    http_method_names = ['GET'] if (http_method_names is None) else http_method_names
    def decorator(func):
        WrappedAPIView = type(
            'WrappedAPIView',
            (APIView,),
            {'__doc__': func.__doc__}
        )

        # ...

        return WrappedAPIView.as_view()

Вот функциональное представление, которое выполняет те же действия, что и ранее описанное классовое представление, для удаления всех элементов:

from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['DELETE'])
def delete_all_items(request):
    Item.objects.all().delete()
    return Response(status=status.HTTP_200_OK)

Здесь мы преобразовали delete_all_items в APIView подкласс с помощью @api_view декоратора. Разрешен только метод DELETE. Другие методы выдадут сообщение "Метод 405 не разрешен".

Несмотря на разницу между тем, как записываются класс и функция, у нас есть доступ к одним и тем же свойствам, поэтому оба фрагмента кода дают одинаковый результат.

Декораторы политик

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

  • @renderer_classes
  • @parser_classes
  • @authentication_classes
  • @throttle_classes
  • @permission_classes

Эти декораторы соответствуют подклассам APIView. Поскольку @api_view декоратор проверяет, используется ли какой-либо из следующих декораторов, их необходимо добавить ниже декоратора api_view.

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

from rest_framework.decorators import api_view, permission_classes, renderer_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])  # policy decorator
@renderer_classes([JSONRenderer])       # policy decorator
def items_not_done(request):
    user_count = Item.objects.filter(done=False).count()
    content = {'not_done': user_count}

    return Response(content)

Как работают представления DRF?

Когда запрос попадает в представление, оно сначала инициализирует объект Request, который является усовершенствованным DRF HttpRequest из Django.

По сравнению с HttpRequest в Django, он имеет следующие преимущества:

  1. Содержимое автоматически анализируется в соответствии с заголовком Content-Type и доступно в виде request.data.
  2. Он поддерживает методы PUT и PATCH (включая загрузку файлов)). ( Django поддерживает только методы GET и POST.)
  3. Временно переопределяя метод в запросе, он проверяет разрешения на другие HTTP-методы.

После создания экземпляра Request представление сохраняет принятую информацию в запросе, используя предоставленный (или используемый по умолчанию) механизм согласования содержимого и средства визуализации. После этого представление выполняет аутентификацию, а затем проверяет разрешения и любые ограничения.

Сама по себе аутентификация не возвращает никаких ошибок. Она просто определяет, кто является пользователем запроса. Эта информация требуется для проверки разрешений и регулирования. При проверке разрешений, если аутентификация не была успешной, возникает исключение NotAuthenticated. Если запрос не разрешен, возникает исключение PermissionDenied. При проверке регулирования, если запрос регулируется, генерируется исключение Throttled, и пользователь получает уведомление о том, как долго ему нужно ждать разрешения запроса.

Проверка прав доступа на самом деле состоит из двух частей: check_permissions и check_object_permissions.

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

Подробнее о разрешениях DRF читайте в статье Разрешения в Django REST Framework.

После проверки подлинности, авторизации/разрешений и регулирования в представлении проверяется, является ли метод запроса одним из следующих:

  • get
  • post
  • put
  • patch
  • delete
  • head
  • option
  • trace

Если это так, он проверяет, соответствует ли метод запроса методу в вашем представлении, и выполняет его. Если какой-либо из методов запрещен или не определен в вызываемом представлении, возникает исключение MethodNotAllowed.

Метод dispatch в классе APIView проверяет метод и выбирает обработчик на основе имени метода:

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

class APIView(View):

    # ...

    def dispatch(self, request, *args, **kwargs):

        # ...

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

Разрешенные методы не определены в DRF, а взяты из Django:

# https://github.com/django/django/blob/stable/3.2.x/django/views/generic/base.py#L36

class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

Наконец, вместо HttpResponse в Django возвращается объект Response. Разница между HttpResponse в Django и Response в DRF заключается в том, что Response инициализируется данными без визуализации, что позволяет отображать содержимое в нескольких типах в зависимости от запроса клиента.

Заключение

В DRF существует несколько типов представлений. Наиболее широко используемые из них:

  1. представления на основе классов, расширяющие APIView класс
  2. конкретные представления
  3. ModelViewSet

Они различаются по тому, насколько легко их настраивать, и по простоте использования. Вы устанавливаете политики (например, регулирование, разрешения) внутри представления, для представлений на основе классов, или с помощью декораторов, для представлений на основе функций.

Расширение APIView дает вам максимальную свободу в настройке того, что происходит в самом представлении.

Глубокое погружение в серию просмотров фреймворка Django REST:

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