webentwicklung-frage-antwort-db.com.de

Wie mache ich eine PATCH-Anfrage mit Django? REST Rahmen

Ich bin nicht sehr erfahren mit Django REST und habe viele Dinge ausprobiert, kann aber meine PATCH-Anfrage nicht zum Laufen bringen.

Ich habe einen Model-Serializer. Dies ist das gleiche, das ich für das Hinzufügen eines neuen Eintrags verwende. Idealerweise würde ich es gerne wieder verwenden, wenn ich einen Eintrag aktualisiere.

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
        """
        .....

Ich habe versucht, eine Klassenansicht zu verwenden:

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

Meine URLs sind:

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

Mein JS-Anruf ist:

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

In diesem Fall hätte ich mir gewünscht, dass meine Beschreibung aktualisiert wurde, aber ich bekomme Fehler, dass die Felder erforderlich sind (für 'Projekt' und alles andere). Die Validierung schlägt fehl. Wenn zum Aufruf von AJAX alle Felder hinzugefügt werden, schlägt es immer noch fehl, wenn das 'Projekt' abgerufen werden muss.

Ich habe auch versucht, meine eigene Meinung zu machen:

@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) 

Aus dem gleichen Grund funktionierte dies nicht für die teilweise Aktualisierung (die Überprüfung der Felder schlug fehl) und es funktionierte nicht, selbst wenn ich alle Felder gesendet habe. Es erstellt einen neuen Eintrag, anstatt den vorhandenen zu bearbeiten. 

Ich möchte den gleichen Serializer und die gleichen Überprüfungen erneut verwenden, bin aber offen für andere Vorschläge. Auch wenn jemand einen Arbeitscode hat (Ajax-Code-> API-Ansicht-> Serialisierer), wäre das toll.

18
Vlad
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")

Dokumentation
Sie müssen den partial_update nicht schreiben oder die update-Methode überschreiben. Verwenden Sie einfach die patch-Methode.

9
ramwin

Stellen Sie sicher, dass Sie "PATCH" in http_method_names haben. Alternativ kannst du es so schreiben:

@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)]

Wie in Dokumentation angegeben :

Standardmäßig müssen Serialisierer Werte für alle erforderlichen Felder übergeben, da sonst Validierungsfehler auftreten. Sie können das Teilargument verwenden, um Teilaktualisierungen zuzulassen.

Überschreiben Sie die update-Methode in Ihrer Ansicht:

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)

Oder überschreiben Sie einfach die Methode partial_update in Ihrer Ansicht:

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

Der Serializer ruft update Methode von ModelSerializer auf (siehe 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

Update gibt die Werte von validated_data an die angegebene Instanz weiter. Beachten Sie, dass beim Update nicht davon ausgegangen werden soll, dass alle Felder verfügbar sind. Dies hilft bei Teilaktualisierungen (PATCHrequest).

7
M.Void

Die patch-Methode wird für mich mithilfe von viewset in DRF bearbeitet. Ich ändere deinen Code:

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
seenu s

Ich bin auch auf dieses Problem gestoßen. Ich habe es gelöst, indem ich die Methode get_serializer_method neu definiert und benutzerdefinierte Logik hinzugefügt habe, um das teilweise Update zu handhaben. Python 3

 class ViewSet(viewsets.ModelViewSet):
     def get_serializer_class(self):
         if self.action == "partial_update":
             return PartialUpdateSerializer

Hinweis: Möglicherweise müssen Sie die Funktion partial_update im Serializer überschreiben. Wie so:

class PartialUpdateSerializer(serializers.Serializer):
    def partial_update(self, instance, validated_data):
       *custom logic*
       return super().update(instance, validated_data)
0
Paul Tuckett

Verwenden Sie stattdessen ModelViewSet und überschreiben Sie die perform_update-Methode von UpdateModelMixin.

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

    def perform_update(self, serializer):
        serializer.save()
        # you may also do additional things here
        # e.g.: signal other components about this update

Das ist es. Gib bei dieser Methode nichts zurück. UpdateModelMixin hat die update-Methode implementiert, um aktualisierte Daten als Antwort für Sie zurückzugeben, und löscht auch _prefetched_objects_cache. Siehe den Quellcode hier .

0
Raymond

Eine andere Möglichkeit besteht darin, die Anfrage per URL zu stellen. Zum Beispiel habe ich ein Modell wie dieses

      class Author(models.Model):
        FirstName = models.CharField(max_length=70)
        MiddleName = models.CharField(max_length=70)
        LastName = models.CharField(max_length=70)
        Gender = models.CharField(max_length=1, choices = GENDERS)
        user = models.ForeignKey(User, default = 1, on_delete = models.CASCADE, related_name='author_user')
        IsActive = models.BooleanField(default=True)
        class Meta:
          ordering = ['LastName']

Und eine Ansicht wie diese

      class Author(viewsets.ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer

Geben Sie also http://127.0.0.1:8000/author/ ein, um Autoren abzurufen oder zu veröffentlichen. Wenn ich eine PATCH-Anfrage stellen möchte, können Sie von Ihrem Client auf http://127.0.0.1:8000/author/ID_AUTHOR verweisen. In angle2 können Sie beispielsweise so etwas haben

       patchRequest(item: any): Observable<Author> {
        return this.http.patch('http://127.0.0.1:8000/author/1', item);
       }

Angenommen, Sie haben Ihr CORS konfiguriert und haben das gleiche Modell auf der Vorder- und Rückseite. Hoffe, es kann nützlich sein.

0