2016-12-14 432 views
0

我正在使用Django Rest框架。我想创建记录,如果它不存在,或者更新(如果它存在)。Django Rest框架 - 如何使用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

Thanks @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-AS-UPSERT。

这意味着:

def put(…):会叫self.upsert(…)以同样的方式def post(…):调用self.create(…)here

def upsert(…)将拨打self.perform_upsert(serializer),类似于this

这里有一个棘手的问题,如果你想要部分更新;在这种情况下,你可能也想sql- UPDATE只有changed 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锁定),从而避免不必要数据库查询。