Как я могу получить модель, созданную super().create() в ModelViewSet?

Я пытаюсь получить копию модели, созданной super().create(), в методе create() моего ModelViewSet. Какой самый простой способ сделать это?

У меня есть ModelViewSet, который принимает общий запрос POST, и я знаю, что это хорошо, потому что в итоге я получаю новую запись в своей БД. Что я хочу сделать, так это получить только что созданный объект, чтобы я мог вернуть его pk клиенту. Однако следующее не будет работать:

class ItemViewSet(viewsets.ModelViewSet):
    model = Item
    # ...
    def create(self, request, *args, **kwargs):
        super().create(request, *args, **kwargs)  # Successfully creates instance
        instance = self.get_object()  # Throws error
        return Response({'status': 'success', 'pk': instance.pk})

Как и в случае с другими методами DRF ModelViewSet, я ожидаю, что self.get_object() создаст мне созданный экземпляр, хотя обычно это можно использовать только в «детальном маршруте». Вместо этого я получаю следующую ошибку: AssertionError: Expected view CultivarStockViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.

Любое понимание ценится!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
3 395
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

С небольшой дальнейшей отладкой я понял это. Ради потомства, вот мой рабочий create() метод:

def create(self, request, *args, **kwargs):
    response = super().create(request, *args, **kwargs)
    instance = response.data
    return Response({'status': 'success', 'pk': instance['pk']})

Обратите внимание, что response.data на самом деле не экземпляр создаваемой модели, а rest_framework.utils.serializer_helpers.ReturnDict.

Ovidiu 22.01.2021 12:06
Ответ принят как подходящий

Вы действительно не можете получить объект с помощью get_object [классный документ], так как Django использует параметры для выполнения фильтрации. Действительно, реализация get_object выглядит так:

def get_object(self):
    # ...
    queryset = self.filter_queryset(self.get_queryset())

    # Perform the lookup filtering.
    lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

    # ...

    filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
    obj = get_object_or_404(queryset, **filter_kwargs)

    # May raise a permission denied
    self.check_object_permissions(self.request, obj)

    return obj

эти self.kwargs недоступны, поэтому вызов get_object завершается ошибкой.

Однако мы можем исправить функцию create [классный документ] и использовать здесь serializer.instance [drf-doc]:

class ItemViewSet(viewsets.ModelViewSet):
    model = Item
    # ...

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        return Response({'status': 'success', 'pk': serializer.instance.pk})

+1 за тщательность. Однако это решение немного более запутанно для меня и может быть для следующего парня. Он также углубляется в базовую реализацию и, возможно, требует больше поддержки по мере развития фреймворка. Есть ли что-то, что я упускаю, что делает этот ответ лучшим?

JohnnyHammersticks 29.05.2019 00:24

@JohnnyHammersticks: ну, это немного печальный, что perform_create не возвращает созданный им экземпляр, хотя это всего лишь оператор return. Так что, возможно, было бы более элегантно просто исправить это, и в этом случае мы, таким образом, можем значительно упростить вышеизложенное. Однако идея заключается в том, что вы получаете объект, а не его сериализацию, поскольку эта сериализация может «упустить» важные детали и т. д.

Willem Van Onsem 29.05.2019 00:26

Ах, хороший момент. Я мог видеть, как эти «отсутствующие» поля сводят меня с ума, если мне когда-нибудь придется вернуться к этому методу. Отметив это как ответ. Спасибо!

JohnnyHammersticks 29.05.2019 00:29

Я знаю, что отвечаю на этот вопрос очень поздно. Но если кто-то застрял в такой проблеме, ответ, предоставленный @JohnnyHammersticks, работает хорошо. Но проблема в том, что это решение создаст объект два раза (в моем случае у меня также есть функция perform_create). Лучший способ справиться с этой ситуацией следующий:

def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)

    if serializer.is_valid():
        instance = serializer.data
        pk = data['id']
        return Response({'status': 'success', 'pk': instance['pk']})

Другие вопросы по теме