Я пытаюсь получить копию модели, созданной 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.
Любое понимание ценится!
С небольшой дальнейшей отладкой я понял это. Ради потомства, вот мой рабочий create()
метод:
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
instance = response.data
return Response({'status': 'success', 'pk': instance['pk']})
Вы действительно не можете получить объект с помощью 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: ну, это немного печальный, что perform_create
не возвращает созданный им экземпляр, хотя это всего лишь оператор return
. Так что, возможно, было бы более элегантно просто исправить это, и в этом случае мы, таким образом, можем значительно упростить вышеизложенное. Однако идея заключается в том, что вы получаете объект, а не его сериализацию, поскольку эта сериализация может «упустить» важные детали и т. д.
Ах, хороший момент. Я мог видеть, как эти «отсутствующие» поля сводят меня с ума, если мне когда-нибудь придется вернуться к этому методу. Отметив это как ответ. Спасибо!
Я знаю, что отвечаю на этот вопрос очень поздно. Но если кто-то застрял в такой проблеме, ответ, предоставленный @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']})
Обратите внимание, что
response.data
на самом деле не экземпляр создаваемой модели, аrest_framework.utils.serializer_helpers.ReturnDict
.