Как в Django Rest Framework кастомизировать представление данных в ModelViewSet?

«Как в Django Rest Framework кастомизировать представление данных в ModelViewSet?» — вопрос из категории Django, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Django Rest Framework (DRF) существует несколько способов кастомизировать сериализацию данных в ModelViewSet, что позволяет гибко управлять выводом API.

1. Использование разных сериализаторов для разных действий

Это самый чистый и рекомендуемый способ, когда для разных действий (например, list и retrieve) требуются разные наборы полей. Для этого переопределяется метод get_serializer_class.

Пример: Показать краткую информацию в списке и полную в детальном представлении.

class UserListSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email')

class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'first_name', 'last_name', 'is_staff')

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()

    def get_serializer_class(self):
        if self.action == 'list':
            return UserListSerializer
        # Для 'retrieve', 'create', 'update' и др. будет использован UserDetailSerializer
        return UserDetailSerializer

2. Переопределение метода to_representation в сериализаторе

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

Пример: Добавить вычисляемое поле или изменить формат существующего.

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'first_name', 'last_name')

    def to_representation(self, instance):
        # Получаем стандартное представление
        representation = super().to_representation(instance)
        # Добавляем новое поле 'full_name'
        representation['full_name'] = f"{instance.first_name} {instance.last_name}"
        return representation

3. Переопределение методов действий (list, retrieve)

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

Пример: Добавить в ответ на list общее количество объектов.

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserListSerializer

    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)

        # Формируем кастомный ответ
        custom_data = {
            'count': queryset.count(),
            'results': serializer.data
        }
        return Response(custom_data)