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