2014-01-15 194 views
16

我對Django REST框架並不十分熟悉,並且嘗試了很多東西,但無法使我的PATCH請求正常工作。如何使用DJANGO REST框架製作PATCH請求

我有一個Model序列化程序。這與我用於添加新條目的條目完全相同,理想情況下我希望在更新條目時重新使用該條目。

class TimeSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = TimeEntry 
     fields = ('id', 'project', 'amount', 'description', 'date') 

    def __init__(self, user, *args, **kwargs): 
     # Don't pass the 'fields' arg up to the superclass 
     super(TimeSerializer, self).__init__(*args, **kwargs) 
     self.user = user 

    def validate_project(self, attrs, source): 
     """ 
     Check that the project is correct 
     """ 
     ..... 

    def validate_amount(self, attrs, source): 
     """ 
     Check the amount in valid 
     """ 
     ..... 

我試圖使用基於類視圖:

class UserViewSet(generics.UpdateAPIView): 
    """ 
    API endpoint that allows timeentries to be edited. 
    """ 
    queryset = TimeEntry.objects.all() 
    serializer_class = TimeSerializer 

我的網址是:

url(r'^api/edit/(?P<pk>\d+)/$', UserViewSet.as_view(), name='timeentry_api_edit'), 

我的JS調用是:

var putData = { 'id': '51', 'description': "new desc" } 
$.ajax({ 
    url: '/en/hours/api/edit/' + id + '/', 
    type: "PATCH", 
    data: putData, 
    success: function(data, textStatus, jqXHR) { 
     // .... 
    } 
} 

在這種情況下,我本來想讓我的描述更新,但是我得到錯誤在領域是必需的(對於'項目'和所有其他)。驗證失敗。如果添加到AJAX調用所有的領域,它仍然失敗時,它必須檢索'項目'。

我試着也使我自己的看法:

@api_view(['PATCH']) 
@permission_classes([permissions.IsAuthenticated]) 
def edit_time(request): 

if request.method == 'PATCH': 
    serializer = TimeSerializer(request.user, data=request.DATA, partial=True) 
    if serializer.is_valid(): 
     time_entry = serializer.save() 
    return Response(status=status.HTTP_201_CREATED) 
return Response(status=status.HTTP_400_BAD_REQUEST) 

這並沒有爲同一原因(字段驗證失敗了)部分更新工作,即使我已經沒有工作發送了所有的字段。它創建一個新條目而不是編輯現有條目。

我想重新使用相同的序列化器和驗證,但我願意接受任何其他建議。 另外,如果有人有一段工作代碼(ajax code-> api view-> serializer)會很好。

+0

這是一個老問題,但我很好奇,看看會發生什麼,如果你:(1)將請求類型更改爲POST和(2)添加值爲「PATCH」的_method字段。見[這裏](http://www.django-rest-framework.org/topics/browser-enhancements)。 Django rest框架應該照顧部分更新,所以它應該是Just Work。 – Jamie

+0

我更改爲使用PUT而不是PATCH,只發送所有內容,因爲我有權訪問所有數據並以此工作。我記得我還發現了一些東西,並修改了我的代碼。也許現在它可以與PATCH一起使用。對不起,我沒有更多信息。這是一段時間以前。 – Vlad

+0

那麼你能發佈你的工作代碼嗎? @Vlad – Rexford

回答

4

patch方法適用於我在DRF中使用viewset。我改變你的代碼:

class UserViewSet(viewsets.ModelViewSet): 
    queryset = TimeEntry.objects.all() 
    serializer_class = TimeSerializer 

    def perform_update(self, serializer): 
     user_instance = serializer.instance 
     request = self.request 
     serializer.save(**modified_attrs) 
     return Response(status=status.HTTP_200_OK) 
4

請確保您有「補丁」http_method_names。或者你可以寫這樣的:

@property 
def allowed_methods(self): 
    """ 
    Return the list of allowed HTTP methods, uppercased. 
    """ 
    self.http_method_names.append("patch") 
    return [method.upper() for method in self.http_method_names 
      if hasattr(self, method)] 

正如documentation說:

默認情況下,串行器必須傳遞值所有必填字段,否則將提高驗證錯誤。您可以使用部分參數以允許部分更新。

覆蓋update方法在您的視圖:

def update(self, request, *args, **kwargs): 
    instance = self.get_object() 
    serializer = TimeSerializer(instance, data=request.data, partial=True) 
    serializer.is_valid(raise_exception=True) 
    serializer.save(customer_id=customer, **serializer.validated_data) 
    return Response(serializer.validated_data) 

或者只是重寫方法partial_update在您的視圖:

def partial_update(self, request, *args, **kwargs): 
    kwargs['partial'] = True 
    return self.update(request, *args, **kwargs) 

串行器調用更新方法ModelSerializer的(見sources) :

def update(self, instance, validated_data): 
    raise_errors_on_nested_writes('update', self, validated_data) 

    # Simply set each attribute on the instance, and then save it. 
    # Note that unlike `.create()` we don't need to treat many-to-many 
    # relationships as being a special case. During updates we already 
    # have an instance pk for the relationships to be associated with. 
    for attr, value in validated_data.items(): 
     setattr(instance, attr, value) 
    instance.save() 

    return instance 

更新將validated_data值推送給給定實例。請注意,更新不應該假定所有字段都可用。這有助於處理部分更新(PATCH請求)。

7
class DetailView(APIView): 
    def get_object(self, pk): 
     return TestModel.objects.get(pk=pk) 

    def patch(self, request, pk): 
     testmodel = self.get_object(pk) 
     serializer = TestModelSerializer(testmodel, data=request.data, partial=True) # set partial=True to update a data partially 
     if serializer.is_valid(): 
      serializer.save() 
      return JsonReponse(code=201, data=serializer.data) 
     return JsonResponse(code=400, data="wrong parameters") 

Documentation
你不需要寫partial_update或覆蓋update方法。只需使用patch方法。

+0

有趣的是,我從來沒有見過'APIView.patch()'之前使用過,也沒有提到過任何文檔。你在生產中使用'patch'沒有任何問題?你還在視圖中放置了一個'put'嗎? – dtgq

+0

是的,我在我的工作中使用補丁,刪除,放置方法,它工作正常。 – ramwin

+0

「補丁」方法首次在'UpdateAPIView'中可用,唯一的作用是調用'self.partial_update(...)'鏈接到DRF 3.5.3源代碼https://github.com/tomchristie/django -rest框架/ BLOB/3.5.3/rest_framework/generics.py#L221 – andi

相關問題