2016-12-14 3 views
0

Я использую Django Rest Framework. Я хочу создать запись, если она не существует, или обновить, если она существует.Django Rest Framework - Как использовать update_or_create

Что я сделал:

class MyModelList(generics.ListCreateAPIView): 
    queryset = MyModel.objects.all() 
    serializer_class = MyModeSerializer 
    permission_classes = (permissions.IsAuthenticated,) 

    def perform_create(self, serializer): 
     my_model, created = MyModel.objects.update_or_create(user_id=self.request.data['user_id'], 
                  defaults={ 
                   'reg_id': self.request.data['reg_id'] 
                  }) 

Запись создан или обновлен. Но я получаю сообщение об ошибке 'OrderedDict' object has no attribute 'pk'

ответ

2

Во-первых, это плохая идея, потому что вы нарушаете стандарт REST API, который ожидает создания через POST и обновления через PUT и PATCH.

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

Я сделал бы это таким образом.

class MyModelList(generics.ListCreateAPIView): 
    queryset = MyModel.objects.all() 
    serializer_class = MyModeSerializer 
    permission_classes = (permissions.IsAuthenticated,) 

    def create(self, request, *args, **kwargs): 
     mymodel=None 
     id=request.data.get("id") 
     if id: 
      mymodel=self.get_object(id) 

     if mymodel: 
      return self.update(request, *args, **kwargs) 
     else: 
      return self.create(request, *args, **kwargs) 
+0

это ссылки 'self.create' внутри' self.create' ... – YPCrumble

0

Спасибо @Bitonator. Это мое окончательное решение:

class MyModelList(generics.ListCreateAPIView): 
    queryset = MyModel.objects.all() 
    serializer_class = MyModeSerializer 
    permission_classes = (permissions.IsAuthenticated,) 

    def create(self, request, *args, **kwargs): 
     myMode, created = MyModel.objects.update_or_create(user_id=request.data['user_id'], 
                  defaults={ 
                  'reg_id': request.data['reg_id'] 
                  }) 

     # require context={'request': request} because i'm using HyperlinkModelSerializer 
     serializer = MyModelSerializer(myModel, data=request.data, context={'request': request}) 
     if serializer.is_valid(): 
      serializer.save() 

     if created: 
      return Response(serializer.data, status.HTTP_201_CREATED) 
     else: 
      return Response(serializer.data, status.HTTP_200_OK) 
+0

Работает тоже. Единственная проблема, которую я вижу с вашим кодом, заключается в том, что он не будет соблюдать разрешения на уровне объекта. См. Http://www.django-rest-framework.org/api-guide/permissions/#object-level-permissions для получения более подробной информации. – Bitonator

0

Я согласен с this заключением соответствующего обсуждения и реализации PUT-а-upsert.

Это означает, что:

def put(…): назвали бы self.upsert(…) таким же образом def post(…): называет self.create(…)here.

def upsert(…) будет звонить по номеру self.perform_upsert(serializer), аналогичен this.

Трудный пункт здесь, если вы хотите частичное обновление; в этом случае вы также можете захотеть только sql- UPDATEchanged fields; и другие осложнения.

Таким образом, для частичного обновления def perform_upsert(serializer): позвонит serializer.upsert().

Другое осложнение здесь, если вы хотите nested create, то есть с отношениями.

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

В результате сериализатору будет выглядеть следующим образом:

class MyModelSerializer(serializers.ModelSerializer): 

    … 

    _key_attrs = ('username',) # the effective primary key, such as auth_user's username. 

    def upsert(self): 
     assert not self.errors 
     validated_data = self.validated_data 
     effective_key = {key: validated_data.get(key) for key in self._key_attrs} 
     instance, _ = model.objects.update_or_create(defaults=validated_data, **effective_key) 
     return instance 

и если вам нужно что-то более сложное, то вы должны смотреть на the code of update_or_create правильно управлять сделку (с for update замком) и избежать ненужных запросы к базе данных.

Смежные вопросы