Teilaktualisierung des Django Rest Framework

72

Ich versuche, partial_updatemit Django Rest Framework zu implementieren, aber ich brauche einige Klarstellungen, weil ich nicht weiterkomme.

  1. Warum müssen wir partiell = True angeben?
    Nach meinem Verständnis könnten wir das Demo-Objekt innerhalb der partial_updateMethode leicht aktualisieren . Was ist der Zweck davon?

  2. Was ist in der serialisierten Variablen enthalten?
    Was steckt in der serializedVariablen in der partial_updateMethode? Ist das ein Demo-Objekt? Welche Funktion heißt hinter den Kulissen?

  3. Wie würde man die Implementierung hier beenden?

Viewset

class DemoViewSet(viewsets.ModelViewSet):
    serializer_class = DemoSerializer

    def partial_update(self, request, pk=None):
        serialized = DemoSerializer(request.user, data=request.data, partial=True)
        return Response(status=status.HTTP_202_ACCEPTED)

Serializer

class DemoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Demo
        fields = '__all__'

    def update(self, instance, validated_data):
        print 'this - here'
        demo = Demo.objects.get(pk=instance.id)
        Demo.objects.filter(pk=instance.id)\
                           .update(**validated_data)
        return demo
Intelis
quelle
Hier finden Sie weitere Informationen zu den Methoden in Ansichten, Serialisierern usw. cdrf.co/3.9/rest_framework.generics/UpdateAPIView.html
khashashin

Antworten:

111

Ich hatte die gleichen Fragen wie Sie zuvor, aber als ich mich mit dem Quellcode von rest_framework befasste, erhielt ich die folgenden Ergebnisse, hoffe, es hilft:

Zu Frage 1. Warum müssen wir partiell = True angeben?

Diese Frage bezieht sich auf HTTP-Verben .

PUT : Die PUT-Methode ersetzt alle aktuellen Darstellungen der Zielressource durch die Anforderungsnutzlast.

PATCH : Mit der PATCH-Methode werden teilweise Änderungen an einer Ressource vorgenommen.

Im Allgemeinen partialwird verwendet, um zu überprüfen, ob die Felder im Modell für die Feldvalidierung benötigt werden, wenn der Client Daten an die Ansicht sendet.

Zum Beispiel haben wir ein BookModell wie diese, pls Anmerkung beide die nameund author_nameFelder sind obligatorisch (nicht null & nicht leer).

class Book(models.Model):
    name = models.CharField('name of the book', max_length=100)
    author_name = models.CharField('the name of the author', max_length=50)

# Create a new instance for testing
Book.objects.create(name='Python in a nut shell', author_name='Alex Martelli')

Bei einigen Szenarien können wir zu aktualisieren Teil der Felder in dem Modell nur benötigen, zB wir Update nur müssen nameFeld in der Book. In diesem Fall sendet der Client nur das nameFeld mit dem neuen Wert an die Ansicht. Die vom Kunden übermittelten Daten können folgendermaßen aussehen:

{"pk": 1, name: "PYTHON IN A NUT SHELL"}

Möglicherweise haben Sie jedoch bemerkt, dass unsere Modelldefinition nicht author_nameleer sein darf. Also müssen wir partial_updatestatt verwenden update. Das restliche Framework führt daher keine Feldvalidierungsprüfung für die Felder durch, die in den Anforderungsdaten fehlen.

Zu Testzwecken können Sie zwei Ansichten für beide updateund erstellen partial_update, und Sie werden besser verstehen, was ich gerade gesagt habe.

Beispiel:

views.py
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import UpdateModelMixin
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book


class BookUpdateView(GenericAPIView, UpdateModelMixin):
    '''
    Book update API, need to submit both `name` and `author_name` fields
    At the same time, or django will prevent to do update for field missing
    '''
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

class BookPartialUpdateView(GenericAPIView, UpdateModelMixin):
    '''
    You just need to provide the field which is to be modified.
    '''
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def put(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
urls.py
urlpatterns = patterns('',
    url(r'^book/update/(?P<pk>\d+)/$', BookUpdateView.as_view(), name='book_update'),
    url(r'^book/update-partial/(?P<pk>\d+)/$', BookPartialUpdateView.as_view(), name='book_partial_update'),
)

Zu übermittelnde Daten

{"pk": 1, name: "PYTHON IN A NUT SHELL"}

Wenn Sie den obigen JSON an das /book/update/1/senden, wird der folgende Fehler mit HTTP_STATUS_CODE = 400 angezeigt:

{
  "author_name": [
    "This field is required."
  ]
}

Wenn Sie jedoch den obigen json an /book/update-partial/1/senden, erhalten Sie HTTP_STATUS_CODE = 200 mit der folgenden Antwort:

{
  "id": 1,
  "name": "PYTHON IN A NUT SHELL",
  "author_name": "Alex Martelli"
}

Zu Frage 2. Was ist in der serialisierten Variablen enthalten?

serializedist ein Objekt, das die Modellinstanz als serialisierbares Objekt umschließt. und Sie können diese serialisierte verwenden, um eine einfache JSON-Zeichenfolge mit zu generieren serialized.data.

Zu Frage 3. Wie würde man die Implementierung hier beenden?

Ich denke, Sie können sich selbst antworten, wenn Sie die obige Antwort gelesen haben, und Sie sollten wissen, wann updateund wann Sie sie verwenden müssen partial_update.

Wenn Sie noch Fragen haben, können Sie diese gerne stellen. Ich habe gerade einen Teil der Quell-Oden des Ruhe-Frameworks gelesen und habe einige Begriffe möglicherweise nicht sehr gut verstanden. Bitte weisen Sie darauf hin, wenn es falsch ist ...

Enix
quelle
Wenn ich den Code für das teilweise_Update versuche,
erhalte
Hey, ich möchte die Daten in den Feldern zum Zeitpunkt der Aktualisierung erhalten, die ich zum Zeitpunkt der Erstellung in den Restbrowser einfüge. Außerdem verwende ich Ihre partielle Methode, erhalte aber immer noch Fehler, die für Felder erforderlich sind. Ich weiß es nicht Wenn es kommt, weil im Browser zum Zeitpunkt der Aktualisierung im verschachtelten Serializer keine Daten in den Feldern angezeigt werden. Tatsächlich werden mir Benutzerdaten angezeigt, aber keine Profildaten bei der Aktualisierung.
Nikhil Bhardwaj
Warum wird bei einer PATCH-Instanz auf pre_save die Instanz mit diesen Instanzdatenbankdaten gefüllt? Dies sollte nicht beim Speichern erfolgen?
mrroot5
Wie wäre es mit dem update_fieldsParameter? Es sollten nur die zu aktualisierenden Felder enthalten sein.
Enix
36

Für teilweise Aktualisierung - PATCH http-Methode

Für die vollständige Aktualisierung - PUT http-Methode

Wenn Sie ein Update mit DRF durchführen, sollten Sie Anforderungsdaten senden, die Werte für alle (erforderlichen) Felder enthalten. Dies ist zumindest dann der Fall, wenn die Anforderung über die PUT-http-Methode erfolgt. Soweit ich weiß, möchten Sie ein oder zumindest nicht alle Modellinstanzfelder aktualisieren. In diesem Fall stellen Sie eine Anfrage mit der PATCH http-Methode. Das Django Rest Framework (DRF) kümmert sich sofort darum.

Beispiel (mit Token-Authentifizierung):

curl -i -X PATCH -d '{"name":"my favorite banana"}' -H "Content-Type: application/json" -H 'Authorization: Token <some token>'  http://localhost:8000/bananas/
Gooshan
quelle
4

Nur eine kurze Anmerkung, da anscheinend noch niemand darauf hingewiesen hat:

serialized = DemoSerializer(request.user, data=request.data, partial=True)

Das erste Argument von DemoSerializer sollte eine Demo-Instanz sein, kein Benutzer (zumindest wenn Sie DRF 3.6.2 wie ich verwenden).

Ich weiß nicht, was Sie versuchen, aber dies ist ein funktionierendes Beispiel:

def partial_update(self, request, *args, **kwargs):
    response_with_updated_instance = super(DemoViewSet, self).partial_update(request, *args, **kwargs)
    Demo.objects.my_func(request.user, self.get_object())
    return response_with_updated_instance

Ich mache die teilweise Aktualisierung und dann andere Dinge, die my_func aufrufen und den aktuellen Benutzer und die bereits aktualisierte Demo-Instanz übergeben.

Hoffe das hilft.

Sergio Morstabilini
quelle
Ich möchte die Daten zum Zeitpunkt der Aktualisierung in den Feldern anzeigen. Wissen Sie, wie wir dies bei der verschachtelten Serialisierung tun? Andernfalls erhalte ich null oder leere Felder. Wenn ich also ein Feld bearbeite, wird es mir angezeigt ein Fehler, dass diese Felder nicht leer oder null sein können .... bitte helfen .... Danke
Nikhil Bhardwaj
4

So einfach, überschreiben Sie einfach die Init- Methode Ihres Serializers wie folgt:

def __init__(self, *args, **kwargs):
    kwargs['partial'] = True
    super(DemoSerializer, self).__init__(*args, **kwargs)
Alihaydar Gubatov
quelle
1
Ich frage mich, warum diese Antwort nicht mehr positive Stimmen erhält. es funktionierte wie ein Zauber für mich
Lingxiao
0

Ich hatte ein Problem, bei dem meine Multi-Attribut- / Feldvalidierung in einem rest_framework-Serializer mit einem POST / resources / request arbeitete, aber mit einem PATCH / resources / request fehlschlug. Im PATCH-Fall ist dies fehlgeschlagen, da nur nach Werten im angegebenen Diktat gesucht wurde attrsund nicht auf Werte in zurückgegriffen wurde self.instance. Das Hinzufügen einer Methode get_attr_or_default, um diesen Fallback durchzuführen, scheint funktioniert zu haben:

class EmailSerializer(serializers.ModelSerializer):

    def get_attr_or_default(self, attr, attrs, default=''):
        """Return the value of key ``attr`` in the dict ``attrs``; if that is
        not present, return the value of the attribute ``attr`` in
        ``self.instance``; otherwise return ``default``.
        """
        return attrs.get(attr, getattr(self.instance, attr, ''))

    def validate(self, attrs):
        """Ensure that either a) there is a body or b) there is a valid template
        reference and template context.
        """
        existing_body = self.get_attr_or_default('body', attrs).strip()
        if existing_body:
            return attrs
        template = self.get_attr_or_default('template', attrs)
        templatecontext = self.get_attr_or_default('templatecontext', attrs)
        if template and templatecontext:
            try:
                render_template(template.data, templatecontext)
                return attrs
            except TemplateRendererException as err:
                raise serializers.ValidationError(str(err))
        raise serializers.ValidationError(NO_BODY_OR_TEMPLATE_ERROR_MSG)
jrwdunham
quelle
-5

Du hast vergessen serializer.save()

Sie können es folgendermaßen beenden. . .

class DemoViewSet(viewsets.ModelViewSet):
    serializer_class = DemoSerializer

    def partial_update(self, request, pk=None):
        serializer = DemoSerializer(request.user, data=request.data, partial=True)
        serializer.save()
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data)

Außerdem sollten Sie die Aktualisierungsmethode im Serializer nicht überschreiben müssen.

Jam Risser
quelle
10
Ich denke, zuerst müssen Sie Ihren Serializer validieren und dann speichern. Also wird serializer.is_valid () zuerst kommen, dann serializer.save ()
CodeTarsier