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

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

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

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

Типовые представления, предоставляемые DRF, позволяют быстро создавать представления API, которые тесно связаны с моделями вашей базы данных.

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

Примеры

Обычно при использовании общих представлений вы переопределяете представление и устанавливаете несколько атрибутов класса.

from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
from rest_framework.permissions import IsAdminUser

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

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

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

    def list(self, request):
        # Note the use of `get_queryset()` instead of `self.queryset`
        queryset = self.get_queryset()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

Для очень простых случаев вы можете передать любые атрибуты класса с помощью метода .as_view(). Например, ваша URLconf может включать что-то вроде следующей записи:

path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')

API Reference

GenericAPIView

Этот класс расширяет класс APIView DRF, добавляя часто требуемое поведение для стандартных представлений списка и детализации.

Каждое из конкретных типовых представлений создается путем объединения GenericAPIView с одним или несколькими классами-миксинами.

Атрибуты

Основные настройки:

Следующие атрибуты управляют основным поведением представления.

  • queryset - Набор queryset, который должен использоваться для возврата объектов из этого представления. Как правило, вы должны либо установить этот атрибут, либо переопределить метод get_queryset(). Если вы переопределяете метод представления, важно вызвать get_queryset(), а не обращаться к этому свойству напрямую, так как queryset будет оценен один раз, и эти результаты будут кэшироваться для всех последующих запросов.

  • serializer_class - Класс сериализатора, который должен использоваться для проверки и десериализации входных данных, а также для сериализации выходных данных. Как правило, вы должны либо установить этот атрибут, либо переопределить метод get_serializer_class().

  • lookup_field - Поле модели, которое должно использоваться для выполнения поиска объектов в отдельных экземплярах модели. По умолчанию используется значение 'pk'. Обратите внимание, что при использовании API с гиперссылками вам нужно убедиться, что и представления API, и классы сериализатора устанавливают поля поиска, если вам нужно использовать пользовательское значение.

  • lookup_url_kwarg - Именованный аргумент URL, который должен использоваться для поиска объекта. URL conf должен включать именованный аргумент, соответствующий этому значению. Если значение не установлено, по умолчанию используется то же значение, что и lookup_field.

Пагинация:

Следующие атрибуты используются для управления пагинацией при использовании представлений списка.

  • pagination_class - Класс пагинации, который должен использоваться при пагинации результатов списка. По умолчанию имеет то же значение, что и параметр DEFAULT_PAGINATION_CLASS, который является 'rest_framework.pagination.PageNumberPagination'. Установка pagination_class=None отключит пагинацию в этом представлении.

Фильтрация:

  • filter_backends - Список классов бэкендов фильтра, которые должны использоваться для фильтрации набора запросов. По умолчанию имеет то же значение, что и параметр DEFAULT_FILTER_BACKENDS.

Методы

Базовые методы:

get_queryset(self).

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

Этот метод всегда следует использовать вместо прямого обращения к self.queryset, поскольку self.queryset оценивается только один раз, и эти результаты кэшируются для всех последующих запросов.

Может быть переопределена для обеспечения динамического поведения, например, возврата набора запросов, специфичного для пользователя, делающего запрос.

Например:

def get_queryset(self):
    user = self.request.user
    return user.accounts.all()

Примечание: Если класс serializer_class, используемый в общем представлении, охватывает несколько отношений, что приводит к проблеме n+1, вы можете оптимизировать ваш набор запросов в этом методе, используя select_related и prefetch_related. Для получения дополнительной информации о проблеме n+1 и случаях использования упомянутых методов обратитесь к разделу related в документации django.


get_object(self).

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

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

Например:

def get_object(self):
    queryset = self.get_queryset()
    filter = {}
    for field in self.multiple_lookup_fields:
        filter[field] = self.kwargs[field]

    obj = get_object_or_404(queryset, **filter)
    self.check_object_permissions(self.request, obj)
    return obj

Обратите внимание, что если ваш API не включает разрешения на уровне объекта, вы можете исключить self.check_object_permissions, и просто вернуть объект из поиска get_object_or_404.

filter_queryset(self, queryset).

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

Например:

def filter_queryset(self, queryset):
    filter_backends = [CategoryFilter]

    if 'geo_route' in self.request.query_params:
        filter_backends = [GeoRouteFilter, CategoryFilter]
    elif 'geo_point' in self.request.query_params:
        filter_backends = [GeoPointFilter, CategoryFilter]

    for backend in list(filter_backends):
        queryset = backend().filter_queryset(self.request, queryset, view=self)

    return queryset

get_serializer_class(self).

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

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

Например:

def get_serializer_class(self):
    if self.request.user.is_staff:
        return FullAccountSerializer
    return BasicAccountSerializer

Хуки для сохранения и удаления:

Следующие методы предоставляются mixin-классами и обеспечивают легкое переопределение поведения сохранения или удаления объекта.

  • perform_create(self, serializer) - Вызывается CreateModelMixin при сохранении нового экземпляра объекта.

  • perform_update(self, serializer) - Вызывается UpdateModelMixin при сохранении существующего экземпляра объекта.

  • perform_destroy(self, instance) - Вызывается DestroyModelMixin при удалении экземпляра объекта.

Эти крючки особенно полезны для установки атрибутов, которые подразумеваются в запросе, но не являются частью данных запроса. Например, вы можете установить атрибут объекта на основе пользователя запроса или на основе именованного аргумента URL.

def perform_create(self, serializer):
    serializer.save(user=self.request.user)

Эти точки переопределения также особенно полезны для добавления поведения, которое происходит до или после сохранения объекта, например, отправки подтверждения по электронной почте или регистрации обновления.

def perform_update(self, serializer):
    instance = serializer.save()
    send_email_confirmation(user=self.request.user, modified=instance)

Вы также можете использовать эти крючки для обеспечения дополнительной проверки, вызывая ValidationError(). Это может быть полезно, если вам нужно применить логику валидации в момент сохранения базы данных. Например:

def perform_create(self, serializer):
    queryset = SignupRequest.objects.filter(user=self.request.user)
    if queryset.exists():
        raise ValidationError('You have already signed up')
    serializer.save(user=self.request.user)

Другие методы:

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

  • get_serializer_context(self) - Возвращает словарь, содержащий любой дополнительный контекст, который должен быть предоставлен сериализатору. По умолчанию включает ключи 'request', 'view' и 'format'.

  • get_serializer(self, instance=None, data=None, many=False, partial=False) - Возвращает экземпляр сериализатора.

  • get_paginated_response(self, data) - Возвращает объект Response в стиле paginated.

  • paginate_queryset(self, queryset) - Пагинация набора запросов, если требуется, возвращает либо объект страницы, либо None, если пагинация не настроена для этого представления.

  • filter_queryset(self, queryset) - Получив набор запросов, отфильтровать его с помощью используемых бэкендов фильтрации, возвращая новый набор запросов.


Миксины

Mixin-классы предоставляют действия, которые используются для обеспечения базового поведения представления. Обратите внимание, что mixin-классы предоставляют методы действий, а не определяют методы обработчиков, такие как .get() и .post(), напрямую. Это позволяет более гибко компоновать поведение.

Mixin-классы могут быть импортированы из rest_framework.mixins.

ListModelMixin

Предоставляет метод .list(request, *args, **kwargs), который реализует перечисление набора запросов.

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

CreateModelMixin

Предоставляет метод .create(request, *args, **kwargs), который реализует создание и сохранение нового экземпляра модели.

Если объект создан, возвращается ответ 201 Created с сериализованным представлением объекта в качестве тела ответа. Если представление содержит ключ с именем url, то заголовок Location ответа будет заполнен этим значением.

Если данные запроса, предоставленные для создания объекта, были недействительными, будет возвращен ответ 400 Bad Request, а в теле ответа будет содержаться информация об ошибке.

RetrieveModelMixin

Предоставляет метод .retrieve(request, *args, **kwargs), который реализует возврат существующего экземпляра модели в ответ.

Если объект может быть получен, то возвращается ответ 200 OK с сериализованным представлением объекта в качестве тела ответа. В противном случае будет возвращен ответ 404 Not Found.

UpdateModelMixin

Предоставляет метод .update(request, *args, **kwargs), который реализует обновление и сохранение существующего экземпляра модели.

Также предоставляет метод .partial_update(request, *args, **kwargs), который похож на метод update, за исключением того, что все поля для обновления будут необязательными. Это позволяет поддерживать HTTP-запросы PATCH.

Если объект обновлен, возвращается ответ 200 OK с сериализованным представлением объекта в качестве тела ответа.

Если данные запроса, предоставленные для обновления объекта, были недействительными, будет возвращен ответ 400 Bad Request, в теле которого будет содержаться информация об ошибке.

DestroyModelMixin

Предоставляет метод .destroy(request, *args, **kwargs), который реализует удаление существующего экземпляра модели.

Если объект удален, возвращается ответ 204 No Content, в противном случае возвращается ответ 404 Not Found.


Классы специфичных представлений

Следующие классы являются специфичными общими представлениями. Если вы используете общие представления, то обычно вы работаете именно на этом уровне, если только вам не нужно сильно измененное поведение.

Классы представления могут быть импортированы из rest_framework.generics.

CreateAPIView

Используется только для создания конечных точек.

Предоставляет обработчик метода post.

Расширяет: GenericAPIView, CreateModelMixin

ListAPIView

Используется для конечных точек только для чтения для представления коллекции экземпляров модели.

Предоставляет обработчик метода get.

Расширяет: GenericAPIView, ListModelMixin

RetrieveAPIView

Используется для конечных точек только для чтения для представления одного экземпляра модели.

Предоставляет обработчик метода get.

Расширяет: GenericAPIView, RetrieveModelMixin

DestroyAPIView

Используется для только для удаления конечных точек для одного экземпляра модели.

Предоставляет обработчик метода delete.

Расширяет: GenericAPIView, DestroyModelMixin

UpdateAPIView

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

Предоставляет обработчики методов put и patch.

Расширяет: GenericAPIView, UpdateModelMixin

ListCreateAPIView

Используется для конечных точек чтения-записи для представления коллекции экземпляров модели.

Предоставляет обработчики методов get и post.

Расширяет: GenericAPIView, ListModelMixin, CreateModelMixin

RetrieveUpdateAPIView

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

Предоставляет обработчики методов get, put и patch.

Расширяет: GenericAPIView, RetrieveModelMixin, UpdateModelMixin

RetrieveDestroyAPIView

Используется для конечных точек чтения или удаления для представления одного экземпляра модели.

Предоставляет обработчики методов get и delete.

Расширяет: GenericAPIView, RetrieveModelMixin, DestroyModelMixin

RetrieveUpdateDestroyAPIView

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

Предоставляет обработчики методов get, put, patch и delete.

Расширяет: GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin


Настройка общих представлений

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

Создание пользовательских миксинов

Например, если вам нужно искать объекты на основе нескольких полей в URL conf, вы можете создать mixin-класс, подобный следующему:

class MultipleFieldLookupMixin:
    """
    Apply this mixin to any view or viewset to get multiple field filtering
    based on a `lookup_fields` attribute, instead of the default single field filtering.
    """
    def get_object(self):
        queryset = self.get_queryset()             # Get the base queryset
        queryset = self.filter_queryset(queryset)  # Apply any filter backends
        filter = {}
        for field in self.lookup_fields:
            if self.kwargs.get(field): # Ignore empty fields.
                filter[field] = self.kwargs[field]
        obj = get_object_or_404(queryset, **filter)  # Lookup the object
        self.check_object_permissions(self.request, obj)
        return obj

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

class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    lookup_fields = ['account', 'username']

Использование пользовательских миксинов - хороший вариант, если у вас есть пользовательское поведение, которое необходимо использовать.

Создание пользовательских базовых классов

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

class BaseRetrieveView(MultipleFieldLookupMixin,
                       generics.RetrieveAPIView):
    pass

class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
                                    generics.RetrieveUpdateDestroyAPIView):
    pass

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


PUT as create

До версии 3.0 миксины DRF рассматривали PUT как операцию обновления или создания, в зависимости от того, существовал ли уже объект или нет.

Разрешение PUT в качестве операций создания является проблематичным, поскольку оно обязательно раскрывает информацию о существовании или несуществовании объектов. Также не очевидно, что прозрачное разрешение повторного создания ранее удаленных экземпляров обязательно является лучшим поведением по умолчанию, чем простое возвращение ответов 404.

Оба стиля "PUT as 404" и "PUT as create" могут быть действительны в различных обстоятельствах, но начиная с версии 3.0 мы теперь используем поведение 404 по умолчанию, поскольку оно проще и очевиднее.

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


Пакеты сторонних производителей

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

Django Rest Multiple Models

Django Rest Multiple Models предоставляет общее представление (и миксин) для отправки нескольких сериализованных моделей и/или наборов запросов через один запрос API.

Last updated