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

Ответ

В 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)

Ответ 18+ 🔞

А, слушай, смотри, в этом твоём Django Rest Framework, блядь, есть же несколько способов, как можно накостылять сериализацию в ModelViewSet. Это чтобы твой API не просто так, как попало, данные выплёвывал, а красиво, с умом, понимаешь?

1. Разные сериализаторы для разных действий — самый чёткий путь

Это когда тебе, сука, для списка (list) нужен один набор полей, а для детального просмотра (retrieve) — вообще другой, овердохуища полей. Тут надо просто переопределить метод get_serializer_class, и всё, пиздец, магия.

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

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 сериализатора

Это для мелких, но хитрожопых правок. Прямо перед тем, как данные улетят в ответ, ты можешь их подправить, добавить что-то на лету.

Например: Хочешь добавить поле full_name, которого в модели-то и нету, блядь.

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

    def to_representation(self, instance):
        # Берём стандартный вывод, что DRF нам приготовил
        representation = super().to_representation(instance)
        # И хуяк — добавляем своё поле, ебать мои старые костыли!
        representation['full_name'] = f"{instance.first_name} {instance.last_name}"
        return representation

3. Вручную переписываем методы действий (list, retrieve)

А вот это уже полный контроль, блядь. Когда мало просто данные подменить — надо ещё и структуру ответа перелопатить, метаданные свои впихнуть. Тут ты сам становишься богом ответа.

Допустим, в списке хочешь ещё и общее количество объектов вернуть, чтобы фронтендеры не гадали.

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)

Вот и вся философия, ёпта. Выбирай, что тебе больше подходит под задачу, и не выёбывайся. Главное — чтобы работало и не разъебывало базу почём зря.