Django-rest-framework: PUT-Aufrufe "ersetzen den Status der Zielressource nicht vollständig"

Erstellt am 30. Juni 2016  ·  68Kommentare  ·  Quelle: encode/django-rest-framework

BEARBEITEN: Den aktuellen Status des Problems finden Sie unter https://github.com/encode/django-rest-framework/issues/4231#issuecomment -332935943

===

Ich habe ein Problem beim Implementieren einer Bibliothek für optimistische Parallelität in einer Anwendung, die DRF für die Interaktion mit der Datenbank verwendet. Ich versuche zu:

  • Bestätigen Sie, dass das Verhalten, das ich sehe, auf DRF zurückzuführen ist
  • Bestätigen Sie, dass dies das beabsichtigte Verhalten ist
  • Stellen Sie fest, ob es eine praktische Möglichkeit gibt, dieses Verhalten zu überwinden

Ich habe meiner Django-Anwendung kürzlich optimistische Parallelität hinzugefügt. Um Ihnen die Wiki-Suche zu ersparen:

  • Jedes Modell hat ein Versionsfeld
  • Wenn ein Bearbeiter ein Objekt bearbeitet, erhält er die Version des bearbeiteten Objekts
  • Wenn der Redakteur das Objekt speichert, wird die enthaltene Versionsnummer mit der Datenbank verglichen
  • Wenn die Versionen übereinstimmen, hat der Editor das neueste Dokument aktualisiert und die Speicherung wird durchgeführt
  • Wenn die Versionen nicht übereinstimmen, gehen wir davon aus, dass zwischen dem Laden und Speichern des Editors eine „widersprüchliche“ Bearbeitung übermittelt wurde, und wir lehnen die Bearbeitung ab
  • Wenn die Version fehlt, können wir keine Tests durchführen und sollten die Bearbeitung ablehnen

Ich hatte eine ältere Benutzeroberfläche, die über DRF sprach. Die ältere Benutzeroberfläche verarbeitete keine Versionsnummern. Ich hatte erwartet, dass dies zu Parallelitätsfehlern führen würde, aber das tat es nicht. Wenn ich die Diskussion in #3648 richtig verstehe:

  • DRF führt den PUT mit dem vorhandenen Datensatz zusammen. Dadurch wird eine fehlende Versions-ID mit der aktuellen Datenbank-ID aufgefüllt
  • Da dies immer eine Übereinstimmung liefert, wird das Weglassen dieser Variable immer ein optimistisches Parallelitätssystem zerstören, das über DRF kommuniziert
  • ~Es gibt keine einfachen Optionen (wie das Feld "erforderlich" zu machen), um sicherzustellen, dass die Daten jedes Mal übermittelt werden.~ (Bearbeiten: Sie können das Problem umgehen, indem Sie es erforderlich machen, wie in diesem Kommentar gezeigt)

Schritte zum Reproduzieren

  1. Richten Sie ein Feld für optimistische Parallelität in einem Modell ein
  2. Erstellen Sie eine neue Instanz und aktualisieren Sie mehrmals (um sicherzustellen, dass Sie keine Standardversionsnummer mehr haben).
  3. Senden Sie ein Update (PUT) über DRF ohne die Versions-ID

    Erwartetes Verhalten

Die fehlende Versions-ID sollte nicht mit der Datenbank übereinstimmen und ein Parallelitätsproblem verursachen.

Tatsächliches Verhalten

Die fehlende Versions-ID wird von DRF mit der aktuellen ID gefüllt, damit die Parallelitätsprüfung erfolgreich ist.

Enhancement

Alle 68 Kommentare

Okay, ich kann nicht versprechen, dass ich dieses ziemlich ausführliche Ticket sofort überprüfen kann, da die kommende Version 3.4 Vorrang hat. Aber danke für so ein ausführliches, gut durchdachtes Thema. Dies wird höchstwahrscheinlich auf der Skala von Wochen betrachtet, nicht von Tagen oder Monaten. Wenn Sie Fortschritte machen, selbst weitere Gedanken haben, aktualisieren Sie bitte das Ticket und halten Sie uns auf dem Laufenden.

OK. Ich bin mir ziemlich sicher, dass mein Problem die Kombination von zwei Faktoren ist:

  1. DRF erfordert das Feld im PUT nicht (obwohl es im Modell erforderlich ist), da es einen Standardwert (Version=0) hat.
  2. DRF führt die PUT-Felder mit dem aktuellen Objekt zusammen (ohne den Standard einzufügen)

DRF verwendet daher den aktuellen (Datenbank-)Wert und unterbricht die Parallelitätssteuerung. Die zweite Hälfte des Problems bezieht sich auf die Diskussion in #3648 (ebenfalls oben zitiert) und es gibt eine (vor 3.x) Diskussion in #1445, die immer noch relevant zu sein scheint.

Ich hoffe, dass ein konkreter (und immer häufiger auftretender) Fall, in dem das Standardverhalten pervers ist, ausreicht, um die Diskussion über das "ideale" Verhalten eines ModelSerializer wieder zu eröffnen. Offensichtlich bin ich nur einen Zentimeter tief in DRF, aber meiner Intuition nach ist das folgende Verhalten für ein erforderliches Feld und einen PUT angemessen:

  • Bei Verwendung eines nicht partiellen Serializers sollten wir entweder den Wert erhalten, den Standardwert verwenden oder (wenn kein Standardwert verfügbar ist) einen Validierungsfehler auslösen. Die modellweite Validierung sollte nur für die Eingaben/Standardwerte gelten.
  • Bei Verwendung eines partiellen Serialisierers sollten wir entweder den Wert erhalten oder auf die aktuellen Werte zurückgreifen. Für diese kombinierten Daten sollte eine modellweite Validierung gelten.
  • Ich glaube, der aktuelle "nicht-partielle" Serializer ist wirklich quasi-partiell:

    • Es ist nicht teilweise für Felder, die erforderlich sind und keinen Standardwert haben

    • Es ist teilweise für Felder, die erforderlich sind und einen Standardwert haben (da der Standardwert nicht verwendet wird).

    • Es ist teilweise für Felder, die nicht erforderlich sind

Wir können Aufzählungszeichen (1) oben nicht ändern, sonst werden die Standardwerte nutzlos (wir benötigen die Eingabe, obwohl wir den Standardwert kennen). Das bedeutet, dass wir das Problem beheben müssen, indem wir Nr. 2 oben ändern. Ich stimme Ihrer Argumentation in # 2683 zu, dass:

Modellvorgaben sind Modellvorgaben. Der Serialisierer sollte den Wert weglassen und die Verantwortung an Model.object.create() übertragen, um dies zu handhaben.

Um mit dieser Trennung von Bedenken konsistent zu sein, sollte update eine neue Instanz erstellen (wobei alle Standardwerte an das Modell delegiert werden) und die übermittelten Werte auf diese neue Instanz anwenden. Dies führt zu dem in #3648 angeforderten Verhalten.

Der Versuch, den Migrationspfad zu beschreiben, hilft dabei, hervorzuheben, wie seltsam das aktuelle Verhalten ist. Das Endziel ist

  1. Reparieren Sie den ModelSerializer,
  2. Fügen Sie ein Flag für diesen Quasi-Teilzustand hinzu, und
  3. Machen Sie dieses Flag zum Standard (für Abwärtskompatibilität)

Wie heißt diese Flagge? Der aktuelle Model Serializer ist eigentlich ein partieller Serializer, der (etwas willkürlich) Felder benötigt, die die Bedingung required==True and default==None erfüllen. Wir können das Flag partial nicht explizit verwenden, ohne die Abwärtskompatibilität zu brechen, also brauchen wir ein neues (hoffentlich temporäres) Flag. Mir bleibt quasi_partial , aber meine Unfähigkeit, die willkürliche Anforderung required==True and default==None auszudrücken, ist der Grund, warum es für mich so klar ist, dass dieses Verhalten dringend veraltet sein sollte.

Sie können extra_kwargs in der Meta des Serializers hinzufügen, wodurch version zu einem Pflichtfeld wird.

class ConcurrentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ConcurrentModel
        extra_kwargs = {'version': {'required': True}}

Danke @anoopmalev. Damit bleibe ich im Produktionszweig.

Nachdem ich "darüber geschlafen" habe, merke ich, dass es eine zusätzliche Falte gibt. Alles, was ich gesagt habe, sollte für die Felder des Serializers gelten. Wenn ein Feld nicht im Serializer enthalten ist, sollte es nicht geändert werden. Auf diese Weise sind (und sollten) alle Serilaizer partiell für die nicht eingeschlossenen Felder. Dies ist ein wenig komplizierter als mein "eine neue Instanz erstellen" oben.

Ich glaube, dass dieses Thema auf einen eingeschränkteren Vorschlag reduziert werden muss, um voranzukommen.
Scheint zu weit gefasst zu sein, um in seinem aktuellen Zustand umsetzbar zu sein.
Fürs Erste schließe ich das - wenn jemand es auf eine prägnante, umsetzbare Aussage über gewünschtes Verhalten reduzieren kann, können wir es uns noch einmal überlegen. Bis dahin finde ich es einfach zu breit.

Hier ist ein prägnanter Vorschlag ... für einen nicht-partiellen Serializer:

  1. Behalten Sie für alle Felder, die nicht im Serializer aufgeführt sind (implizit oder explizit) oder als schreibgeschützt gekennzeichnet sind, den vorhandenen Wert bei
  2. Verwenden Sie für alle anderen Felder die erste verfügbare Option:

    1. Mit dem übermittelten Wert füllen

    2. Geben Sie einen Standardwert ein, einschließlich eines Werts, der durch blank und/oder null impliziert wird

    3. Lösen Sie eine Ausnahme aus

Aus Gründen der Klarheit wird die Validierung am Endprodukt dieses Prozesses durchgeführt.

Das heißt, Sie möchten required=True für alle Serializer-Felder festlegen, die keinen Modellstandard für Updates haben?

Habe ich das richtig verstanden?

Ja (und mehr). So verstehe ich die Unterscheidung partial (alle Felder optional) und non-partial (alle Felder erforderlich). Das einzige Mal, dass ein non-partial -Serialisierer kein Feld benötigt, ist das Vorhandensein eines (eng oder breit definierten) Standardwerts, _da der Serialisierer diesen Standard verwenden kann, wenn kein Wert angegeben wird._

Der kursiv gedruckte Abschnitt ist das, was DRF derzeit nicht tut, und die wichtigere Änderung in meinem Vorschlag. Die aktuelle Implementierung überspringt das Feld einfach.

Ich hatte einen zweiten Vorschlag beigemischt, aber es ist wirklich eine andere Frage, wie großzügig Sie mit der Idee eines "Standards" sein wollen. Das aktuelle Verhalten ist insofern "streng", als dass nur ein default als solches behandelt wird. Wenn Sie die Menge der erforderlichen Daten _wirklich_ reduzieren wollten, könnten Sie auch blank=True -Felder optional machen ... vorausgesetzt, dass ein fehlender Wert ein leerer Wert ist.

@claytondaley Ich verwende OOL mit DRF seit 2x auf diese Weise:

class VersionModelSerializer(serializers.ModelSerializer, BaseSerializer):
    _initial_version = 0

    _version = VersionField()

    def __init__(self, *args, **kwargs):
        super(VersionModelSerializer, self).__init__(*args, **kwargs)

        # version field should not be required if there is no object
        if self.instance is None and '_version' in self.fields and\
                getattr(self, 'parent', None) is None:
            self.fields['_version'].read_only = True
            self.fields['_version'].required = False

        # version field is required while updating instance
        if self.instance is not None and '_version' in self.fields:
            self.fields['_version'].required = True

        if self.instance is not None and hasattr(self.instance, '_version'):
            self._initial_version = self.instance._version

    def validate__version(self, value):
        if self.instance is not None:
            if not value and not isinstance(value, int):
                raise serializers.ValidationError(_(u"This field is required"))

        return value
   # more code & helpers

Es funktioniert einfach großartig mit allen Arten von Geschäftslogik und hat nie Probleme verursacht.

Wurde dies bei einem Unfall geschlossen? Ich habe auf die spezifische Frage geantwortet und keinen Grund gehört, was an dem Vorschlag falsch war.

@claytondaley Warum sollte OOL ein Teil von DRF sein? Überprüfen Sie meinen Code – es funktioniert nur in einer großen App finden (1400 Tests). VersionField ist nur ein IntegerField .

Sie haben die OOL in den Serializer fest codiert. Dies ist der falsche Ort, um dies zu tun, da Sie eine Race Condition haben. Parallele Aktualisierungen (mit derselben früheren Version) würden alle am Serializer vorbeigehen ... aber nur eine würde bei der Aktion "Speichern" gewinnen.

Ich verwende django-concurrency , was die OOL-Logik in die Speicheraktion bringt (wo sie hingehört). Grundsätzlich UPDATE... WHERE version = submitted_version . Dies ist atomar, also gibt es keine Race-Bedingung. Es deckt jedoch einen Fehler in der Serialisierungslogik auf:

  • Wenn für ein Feld im Modell default festgelegt ist, legt DRF required=False fest. Die (gültige) Idee ist, dass DRF diesen Standard verwenden kann, wenn kein Wert übermittelt wird.
  • Wenn dieses Feld jedoch fehlt, verwendet DRF nicht den Standardwert. Stattdessen werden die übermittelten Daten mit der aktuellen Version des Objekts zusammengeführt.

Wenn wir das Feld nicht benötigen, tun wir dies, weil wir eine Standardeinstellung haben. DRF erfüllt diesen Vertrag nicht, weil es nicht den Standardwert verwendet ... es verwendet den vorhandenen Wert.

Das zugrunde liegende Problem wurde zuvor besprochen, aber sie hatten keinen schönen, konkreten Fall. OOL ist dieser Idealfall. Der vorhandene Wert eines Versionsfelds übergibt immer OOL, sodass Sie das gesamte OOL-System umgehen können, indem Sie die Version weglassen. Das ist (offensichtlich) nicht das gewünschte Verhalten eines OOL-Systems.

@claytondaley

Sie haben die OOL in den Serializer fest codiert.

Habe ich? Haben Sie neben der Feldanforderung eine OOL-Logik in meinem Serializer gefunden?

Dies ist der falsche Ort, um dies zu tun, da Sie eine Race Condition haben.

Sry, ich kann einfach nicht erkennen, wo hier die Race Condition ist.

Ich verwende Django-Parallelität, die die OOL-Logik in die Speicheraktion einfügt (wo sie hingehört).

Ich verwende auch django-concurrency :) Aber das ist Modellebene, kein Serializer. Auf Serializer-Ebene müssen Sie nur Folgendes tun:

  • Stellen Sie sicher, dass das Feld _version immer erforderlich ist (wenn es sein sollte)
  • Stellen Sie sicher, dass Ihr Serialisierer weiß, wie er mit OOL-Fehlern umgeht (diesen Teil habe ich weggelassen).
  • Stellen Sie sicher, dass Ihr apiview weiß, wie es mit OOL-Fehlern umgeht und HTTP 409 mit einem möglichen Diff-Kontext auslöst

Tatsächlich verwende ich django-concurrency nicht aufgrund eines Problems, das der Autor als "wird nicht behoben" markiert hat: Es umgeht OOL, wenn obj.save(update_fields=['one', 'two', 'tree']) verwendet wird, was ich als schlechte Praxis empfand, also habe ich das Paket geforkt.

Hier ist die fehlende Methode save des Serializers, den ich bereits erwähnt habe. das sollte alle deine Probleme lösen:

    def save(self, **kwargs):
        try:
            self.instance = super(VersionModelSerializer, self).save(**kwargs)
            return self.instance
        except VersionException:
            # Use select_for_update so we have some level of guarantee
            # that object won't be modified at least here at the same time
            # (but it may be modified somewhere else, where select_for_update
            # is not used!)
            with transaction.atomic():
                db_instance = self.instance.__class__.objects.\
                    select_for_update().get(pk=self.instance.pk)
                diff = self._get_serializer_diff(db_instance)

                # re-raise exception, so api client will receive friendly
                # printed diff with writable fields of current serializer
                if diff:
                    raise VersionException(diff)

                # otherwise re-try saving using db_instance
                self.instance = db_instance
                if self.is_valid():
                    return super(VersionModelSerializer, self).save(**kwargs)
                else:
                    # there are errors that could not be displayed to a user
                    # so api client should refresh & retry by itself
                    raise VersionException

        # instance.save() was interrupted by application error
        except ApplicationException as logic_exc:
            if self._initial_version != self.instance._version:
                raise VersionException

            raise logic_exc

Verzeihung. Ich habe Ihren Code nicht gelesen, um herauszufinden, was Sie tun. Ich habe einen Serializer gesehen. Sie können das Problem natürlich umgehen, indem Sie den Serializer hacken, aber Sie sollten es nicht müssen .... weil der Fehler in der DRF-Logik für sich allein steht. Ich verwende nur OOL, um den Punkt zu verdeutlichen.

Und Sie sollten diesen Code mit der neuesten Version von django-concurrency (mit IGNORE_DEFAULT=False ) ausprobieren. django-concurrency ignorierte auch Standardwerte, aber ich habe einen Patch eingereicht. Es gab einen seltsamen Eckfall, den ich aufspüren musste, damit er für normale Fälle funktioniert.

Ich denke, es heißt Erweiterung der Standardfunktionalität, nicht wirklich Hacking. Ich denke, der beste Ort für die Unterstützung solcher Funktionen ist das Paket django-concurrency .

Ich habe die gesamte Problemdiskussion erneut gelesen und fand Ihren Vorschlag zu weit gefasst und würde an vielen Stellen scheitern (aufgrund der magischen Verwendung von Standardwerten aus verschiedenen Quellen unter verschiedenen Bedingungen). DRF 3.x ist jetzt viel einfacher und vorhersehbarer als 2.x, lass es so bleiben :)

Sie können dies nicht in der Modellebene beheben, da es am Serialisierer defekt ist (bevor es zum Modell gelangt). OOL beiseite legen... warum brauchen wir kein Feld, wenn default gesetzt ist?

Ein nicht partieller Serializer „erfordert“ alle Felder (grundsätzlich) und dennoch lassen wir diesen hier. Ist es ein Fehler? Oder haben wir einen logischen Grund?

Wie Sie in meinem Codebeispiel sehen können, ist das Feld _version in allen möglichen Fällen immer korrekt erforderlich.

Übrigens stellte sich heraus, dass ich Modell-Lvl-Code von https://github.com/gavinwahl/django-optimistic-lock geliehen habe und nicht von django-concurrency , was aus fast keinem Grund viel zu komplex ist.

... also ist der Fehler "nicht-partielle Serialisierer haben einige Felder fälschlicherweise auf nicht erforderlich gesetzt". Das ist die Alternative. Denn das ist die (implizite) Verpflichtung, die ein nicht-partieller Serialisierer eingeht.

Ich kann es zitieren :

Standardmäßig müssen Serializern Werte für alle erforderlichen Felder übergeben werden, oder sie lösen Validierungsfehler aus.

Dies sagt nichts über erforderlich aus (außer wenn ein Standardwert angegeben ist).

(und ich verstehe, dass ich von zwei verschiedenen Ebenen spreche, aber der ModelSerializer sollte keine Felder benötigen, wenn er nicht die Verantwortung für diese Entscheidung übernehmen wird.)

Ich glaube, ich habe deinen Punkt verloren..

(und ich verstehe, dass ich von zwei verschiedenen Ebenen spreche, aber der ModelSerializer sollte keine Felder benötigen, wenn er nicht die Verantwortung für diese Entscheidung übernehmen wird.)

Was stimmt damit nicht?

OK, lass mich einen anderen Blickwinkel versuchen.

  • Angenommen, ich habe einen nicht-partiellen Model Serializer (Bearbeiten: alle Standardwerte), der alle Felder in meinem Modell abdeckt.

Sollte ein CREATE oder UPDATE mit denselben Daten jemals ein anderes Objekt erzeugen (abzüglich der ID)

Können Sie Ihre Ideen anhand eines wirklich einfachen Modells und Serializers und einiger Zeilen beschreiben, die ein fehlgeschlagenes / erwartetes Verhalten zeigen?

Ich werde morgen etwas zusammenstellen, da es hier spät wird ... aber je tiefer ich komme, desto mehr Sinn macht #3648 für einen nicht-partiellen Serializer. Warum benötigt ein ModelSerializer in der Zwischenzeit nicht alle Felder im Modell? Vielleicht ist deine Begründung anders als meine.

ModelSerializer überprüft das gebundene Modell und entscheidet, ob es erforderlich sein sollte, nicht wahr?

Ich meine nicht mechanisch wie . Die Grundannahme für einen nicht-partiellen Serializer ist, alles zu verlangen (oben zitiert). Wenn get_field_kwargs von dieser Annahme abweicht (insbesondere hier ), sollte das einen guten Grund haben. Was ist das für ein Grund?

Meine bevorzugte Antwort ist die, die ich immer wieder gebe, "weil es diesen Standard verwenden kann, wenn kein Wert übermittelt wird" (aber dann muss DRF tatsächlich den Standard verwenden). Gibt es eine andere Antwort, die ich vermisse? Ein Grund, warum Felder mit Standardwerten nicht erforderlich sein sollten?

Natürlich bevorzuge ich die "komplette" Lösung. Ich gebe jedoch zu, dass es eine zweite Antwort gibt. Wir könnten diese Felder standardmäßig benötigen. Damit entfällt der (derzeit willkürliche) Sonderfall. Es vereinfacht/reduziert den Code. Es ist intern konsistent. Es geht auf meine Bedenken ein.

Im Grunde macht es den nicht-partiellen Serialisierer wirklich nicht-partiell.

Jetzt weiß ich wenigstens was du meinst. Haben Sie überprüft, wie sich ModelForm in einem solchen Fall verhält? (Kann das nicht selbst auf dem Handy machen)

Django-Dokumente sagen, dass "leer" steuert, ob das Feld erforderlich ist oder nicht. Ich schlage vor, Sie sollten ein separates Ticket für dieses Problem eröffnen, da dieses viele nicht zusammenhängende Kommentare enthält. Meiner Meinung nach könnte Modelserializer wie Modelform funktionieren: Leere Optionssteuerelemente erforderlich, 'null' gibt an, ob None eine akzeptable Eingabe ist, und 'default' hat keine Auswirkung auf diese Logik.

Ich bin bereit, ein zweites Ticket zu eröffnen, aber ich mache mir Sorgen, dass leer einen ähnlichen Code erfordert. Aus der Django-Diskussionsgruppe :

wenn wir ein vorhandenes Modellformular und eine funktionierende Vorlage nehmen, fügen Sie dem Modell ein optionales Zeichenfeld hinzu, fügen aber kein entsprechendes Feld zur HTML-Vorlage hinzu (z eine Änderung, hat nicht bemerkt, dass eine Änderung an einer Vorlage vorgenommen werden musste), wenn dieses Formular gesendet wird, geht Django davon aus, dass der Benutzer einen leeren Zeichenfolgenwert für das fehlende Feld angegeben hat, und speichert diesen im Modell, wobei alle vorhandenen Werte gelöscht werden .

Um konsequent zu sein, wären wir verpflichtet, die zweite Hälfte des Vertrags zu erfüllen, indem wir den fehlenden Wert auf leer setzen. Dies ist etwas weniger problematisch, da ein Leerzeichen ohne Bezugnahme auf ein Modell ausgefüllt werden kann, aber sehr ähnlich ist (und, denke ich, konsistent mit #3648).

@tomchristie kannst du dazu kurz etwas sagen: Warum hängt der required -Zustand von der defaults -Eigenschaft des Modellfelds ab?

Warum hängt der erforderliche Zustand von der Standardeigenschaft des Modellfelds ab?

Einfach dies: Wenn ein Modellfeld einen Standardwert hat, können Sie es weglassen, es als Eingabe bereitzustellen.

Eigentlich stimme ich diesem Verhalten zu. ModelForm, obwohl der Code dasselbe tut (generiertes HTML liefert Standardwerte). Wenn DRF eine andere Logik hätte, wird „Standard“ niemals angewendet. Ich bin fertig mit diesem Problem.

@pySilver eigentlich, hier ist das ModelForm-Verhalten:

# models.py

from django.db import models

class MyModel(models.Model):
    no_default = models.CharField(max_length=100)
    has_default = models.CharField(max_length=100, default="iAmTheDefault")

Zur Verdeutlichung wird Zeug immer noch "teilweise" genannt, weil das _update_ teilweise ist. Ich habe auch ein vollständiges ("vollständiges") Update getestet, aber der Code war unnötig, um das Verhalten zu zeigen:

# in manage.py shell
>>> from django import forms
>>> from django.conf import settings
>>> from form_serializer.models import MyModel
>>>
>>> class MyModelForm(forms.ModelForm):
...     class Meta:
...         model = MyModel
...         fields = ['no_default', 'has_default']
...
>>>
>>> partial = MyModel.objects.create()
>>> partial.id = 2
>>> partial.no_default = "Must replace me"
>>> partial.has_default = "I should be replaced"
>>> partial.save()
>>>
>>>
>>> POST_PARTIAL = {
...     "id": 2,
...     "no_default": "must change me",
... }
>>>
>>>
>>> form_partial = MyModelForm(POST_PARTIAL)
>>> form_partial.is_valid()
False
>>> form_partial._errors
{'has_default': [u'This field is required.']}

ModelForm erfordert diese Eingabe, obwohl es einen Standardwert hat. Dies ist eines der beiden intern konsistenten Verhaltensweisen.

Warum hängt der erforderliche Zustand von der Standardeigenschaft des Modellfelds ab?

Einfach dies: Wenn ein Modellfeld einen Standardwert hat, können Sie es weglassen, es als Eingabe bereitzustellen.

@tomchristie stimme grundsätzlich zu. Aber was ist das erwartete Verhalten?

  • Beim Erstellen erhalte ich den Standardwert (trivial, alle sind sich einig, dass dies richtig ist)
  • Auf Update, was soll ich bekommen?

Es scheint mir, dass ich auch die Standardeinstellung für Update erhalten sollte. Ich verstehe nicht, warum sich ein nicht-partieller Serializer in den beiden Fällen anders verhalten sollte. Nicht teilweise bedeutet, dass ich den "vollständigen" Datensatz sende. Somit sollte der komplette Datensatz ersetzt werden.

Ich würde erwarten, dass der Wert unverändert bleibt, wenn er beim Update nicht bereitgestellt wird. Ich verstehe den Punkt, aber das transparente Überschreiben mit dem Standardwert wäre aus meiner Sicht nicht intuitiv.

(Wenn überhaupt, denke ich, wäre es wahrscheinlich besser, wenn alle Aktualisierungen eine Teilsemantik für alle Felder wären - PUT wäre immer noch idempotent, was der wichtige Aspekt ist, obwohl es möglicherweise umständlich ist, das aktuelle Verhalten zu ändern.)

Ich teile sicherlich nicht Ihre Vorlieben; Ich möchte, dass alle meine Schnittstellen streng sind, es sei denn, ich mache sie absichtlich anders. Ihre Unterscheidung zwischen TEILWEISE und NICHT TEILWEISE liefert jedoch (theoretisch) bereits das, was wir beide wollen.

Ich glaube, dass sich teilweise genau so verhält, wie Sie es wollen:

  • UPDATEs sind zu 100 % teilweise
  • CREATEs (nehme ich an) sind teilweise in Bezug auf default und blank (logische Ausnahmen). In allen anderen Fällen gelten die Modell-/Datenbankeinschränkungen.

Ich versuche nur, Konsistenz im nicht-partiellen Serializer zu erreichen. Wenn Sie den Sonderfall für default eliminieren, werden Ihre vorhandenen nicht-partiellen Serialisierer zu den strikten Serialisierern, die ich haben möchte. Sie erreichen auch Parität mit ModelForm.

Mir ist klar, dass dies zu einer kleinen Diskontinuität innerhalb des Projekts führt, aber dies ist nicht das erste Mal, dass jemand eine solche Änderung vorgenommen hat. Fügen Sie ein „Legacy“-Flag hinzu, das standardmäßig das aktuelle Verhalten verwendet, fügen Sie eine Warnung hinzu (dass sich das Standardverhalten ändern wird) und ändern Sie die Standardeinstellung in einer nachfolgenden Hauptversion.

Noch wichtiger ist, wenn Sie möchten, dass Ihre Serialisierer de facto die neuen für Django sind, werden Sie diese Änderung sowieso vornehmen. Die Anzahl der Leute, die von ModelForm konvertieren, wird die bestehende Benutzerbasis bei weitem übersteigen, und sie werden mindestens diese Änderung erwarten.

Einfügen meiner zwei Cent:
Ich bin geneigt, @claytondaley zuzustimmen. PUT ist ein idempotenter Ressourcenersatz, PATCH ist eine Aktualisierung der vorhandenen Ressource. Nehmen Sie das folgende Beispiel:

class Profiles(models.Model):
    username = models.CharField()
    role = models.CharField(default='member', choices=(
        ('member', 'Member'), 
        ('moderator', 'Moderator'),
        ('admin', 'Admin'), 
    ))

Neue Profile haben sinnvollerweise die Standardmitgliedsrolle. Nehmen wir die folgenden Anfragen:

POST /profiles username=moe
PUT /profiles/1 username=curly
PATCH /profiles/1 username=larry&role=admin
PUT /profiles/1 username=curly

So wie es derzeit aussieht, würden die Profildaten nach dem ersten PUT {'username': 'curly', 'role': 'member'} enthalten. Nach dem zweiten PUT hätten Sie {'username': 'curly', 'role': 'admin'} . Bricht dies nicht die Idempotenz? (Ich bin mir nicht ganz sicher - frage berechtigterweise)

Bearbeiten:
Ich denke, jeder ist auf der gleichen Seite bezüglich der Semantik von PATCH.

Nach dem zweiten PUT hätten Sie {'username': 'curly', 'role': 'admin'}

Ich persönlich wäre überrascht, wenn die Rolle wieder auf die Standardeinstellung umschalten würde (obwohl ich den Grund für diese replace Objektdiskussion verstehe, hatte ich noch nie Probleme in der realen Welt damit)

Ich hatte noch nie echte Probleme damit

Auch hier, aber bisher haben sich unsere Projekte auf PATCH verlassen :)
Das heißt, der Anwendungsfall des OP mit Modellversionierung zur Handhabung der Parallelität macht für mich Sinn. Ich würde erwarten, dass PUT den Standardwert verwendet (wenn der Wert weggelassen wird) und die Parallelitätsausnahme auslöst.

Lassen Sie mich zunächst anerkennen, dass ein Serialisierer nicht unbedingt dem RESTful-RFC folgen muss. Sie sollten jedoch zumindest Modi anbieten, die kompatibel _sind_ -- insbesondere in einem Paket, das REST-Unterstützung bietet.

Mein ursprüngliches Argument war von Grundprinzipien, aber der RFC (Abschnitt 4.3.4) sagt ausdrücklich (Hervorhebung hinzugefügt):

Der grundlegende Unterschied zwischen den POST- und PUT-Methoden wird durch die unterschiedliche Absicht für die beigefügte Darstellung hervorgehoben. Die Zielressource in einer POST-Anforderung soll die eingeschlossene Darstellung gemäß der eigenen Semantik der Ressource handhaben, wohingegen die eingeschlossene Darstellung in einer PUT-Anforderung so definiert ist, dass sie den Status der Zielressource ersetzt.
...
Ein Ursprungsserver, der PUT auf einer bestimmten Zielressource zulässt, MUSS eine 400-Antwort (Bad Request) auf eine PUT-Anforderung senden, die ein Content-Range-Header-Feld enthält (Abschnitt 4.2 von [RFC7233]), da die Nutzlast wahrscheinlich teilweise Inhalt ist das wurde irrtümlicherweise als vollständige Darstellung gesetzt . Teilaktualisierungen von Inhalten sind möglich, indem auf eine separat identifizierte Ressource mit einem Zustand abgezielt wird, der einen Teil der größeren Ressource überlappt, oder indem eine andere Methode verwendet wird, die speziell für Teilaktualisierungen definiert wurde (z. B. die in [RFC5789] definierte PATCH-Methode).

Ein PUT sollte also niemals partiell sein (siehe auch hier ). Der Abschnitt über PUT verdeutlicht jedoch auch:

Das PUT-Verfahren fordert an, dass der Zustand der Zielressource erstellt oder durch den Zustand ersetzt wird, der durch die in der Nutzlast der Anforderungsnachricht eingeschlossene Darstellung definiert ist. Ein erfolgreiches PUT einer gegebenen Repräsentation würde darauf hindeuten, dass ein nachfolgendes GET auf derselben Zielressource dazu führen wird, dass eine äquivalente Repräsentation in einer 200 (OK)-Antwort gesendet wird.

Der Punkt über das GET (obwohl nicht obligatorisch) spricht für meine "Kompromiss" -Lösung. Obwohl das Einfügen von Leerzeichen/Standardwerten praktisch ist, würde es dieses Verhalten nicht bieten. Der Nagel im Sarg ist wahrscheinlich, dass diese Lösung Verwirrung minimiert, da es keine fehlenden Felder gibt, die Zweifel aufkommen lassen.

Offensichtlich ist PATCH eine spezielle Option für Teilaktualisierungen, aber es wird als "Anweisungssatz" und nicht nur als Teil-PUT beschrieben, so dass es mich immer ein wenig nervös macht. Der Abschnitt über POST (4.3.3) besagt tatsächlich:

Das POST-Verfahren fordert an, dass die Zielressource die in der Anforderung eingeschlossene Darstellung gemäß der eigenen spezifischen Semantik der Ressource verarbeitet. Beispielsweise wird POST (unter anderem) für die folgenden Funktionen verwendet:

  • Bereitstellen eines Datenblocks, wie z. B. der in ein HTML-Formular eingegebenen Felder, für einen Datenverarbeitungsprozess;

...

  • Anhängen von Daten an die vorhandene(n) Repräsentation(en) einer Ressource.

Ich denke, es gibt ein Argument für die Verwendung von POST für Teilaktualisierungen, da:

  • konzeptionell ist das Ändern von Daten dem Anhängen nicht unähnlich
  • POST ist berechtigt, seine eigenen Regeln zu verwenden, sodass es sich bei diesen Regeln um eine teilweise Aktualisierung handeln könnte
  • diese Operation kann leicht von einem CREATE durch das Vorhandensein einer ID unterschieden werden

Auch wenn DRF keine vollständige Konformität anstrebt, benötigen wir einen Serializer, der mit der Spezifikation PUT-Operation kompatibel ist (dh das gesamte Objekt ersetzt). Die einfachste (und eindeutig am wenigsten verwirrende) Antwort ist, alle Felder anzufordern. Es schlägt auch vor, dass PUT standardmäßig nicht partiell sein sollte und dass Teilaktualisierungen ein anderes Schlüsselwort (PATCH oder sogar POST) verwenden sollten.

Ich glaube, ich bin gerade auf mein erstes PUT-Problem gestoßen, als ich unsere App auf drf3.4.x migrierte :)

<strong i="6">@cached_property</strong>
    def _writable_fields(self):
        return [
            field for field in self.fields.values()
            if (not field.read_only) or (field.default is not empty)
        ]

Dadurch enthalten meine .validated_data Daten, die ich nicht in der PUT-Anforderung bereitgestellt habe und die ich nicht manuell im Serializer bereitgestellt habe. Werte wurden von default= auf Serializer-Ebene abgerufen. Während ich also bestimmte Felder aktualisieren wollte, überschreibe ich im Grunde auch einige dieser Felder mit Standardwerten aus heiterem Himmel.

Ich freue mich, dass ich einen benutzerdefinierten ModelSerializer verwende, damit ich das Problem einfach beheben kann.

@pySilver Ich verstehe den Inhalt des letzten Kommentars nicht.

@rpkilby "Nehmen wir die folgenden Anfragen ... Unterbricht dies nicht die Idempotenz"

Nein, jede PUT-Anforderung ist insofern idempotent, als dass sie mehrmals wiederholt werden kann, was zu demselben Zustand führt. Das bedeutet nicht, dass, wenn ein anderer Teil des Status in der Zwischenzeit geändert wurde, dieser irgendwie zurückgesetzt wird.

Hier sind einige verschiedene Optionen für das Verhalten von PUT .

  • Felder sind erforderlich, es sei denn, required=False oder sie haben ein default . (Bestehenden)
  • Alle Felder sind erforderlich. (Strengere, enger abgestimmte Semantik der vollständigen Aktualisierung _aber_ umständlich, weil sie tatsächlich strenger ist als die anfängliche Erstellungssemantik für POST)
  • Es sind keine Felder erforderlich (dh nur das PATCH-Verhalten spiegeln)

Klar, dass es keine _absolut_ richtige Antwort gibt, aber ich glaube, wir haben das praktischste Verhalten, wie es derzeit aussieht.

Ich glaube, einige Anwendungsfälle könnten es problematisch finden, wenn es ein Feld gibt, das für POST -Anfragen nicht bereitgestellt werden muss, aber anschließend für PUT -Anfragen. Darüber hinaus ist PUT-as-create selbst eine gültige Operation, also wäre es wieder seltsam, wenn dies eine andere "erforderliche" Semantik als POST hätte.

Wenn jemand dies vorantreiben möchte, würde ich _dringend_ vorschlagen, als Paket eines Drittanbieters zu beginnen, das unterschiedliche Basis-Serializer-Klassen implementiert. Wir können dann aus der Serializer-Dokumentation darauf verlinken. Wenn der Fall gut gemacht ist, können wir in Betracht ziehen, das Standardverhalten irgendwann in der Zukunft anzupassen.

@tomchristie was ich sagen will:

Ich habe einen Serialisierer mit schreibgeschütztem Feld language und einem Modell:

class Book(models.Model):
      title = models.CharField(max_length=100)
      language = models.ChoiceField(default='en', choices=(('pl', 'Polish'), ('en', 'English'))

class BookUpdateSerialzier(serializers.ModelSerializer):
      # language is readonly, I dont want to let users update that field using this serializer
      language = serializers.ChoiceField(default='en', choices=(('pl', 'Polish'), ('en', 'English'), read_only=True)
      class Meta:
          model = MyModel
          fields = ('title', 'language', )

book = Book(title="To be or 42", language="pl")
book.save()

s = BookUpdateSerialzier(book, data={'title': 'Foobar'}, partial=True)
s.is_valid()
assert 'language' in s.validated_data # !!! 
assert 'pl' == s.validated_data # AssertionError... here :(
  • Ich habe language in der Anfrage nicht übergeben und erwarte nicht, dies in validierten Daten zu sehen. Wenn es an update übergeben wird, würde es meine Instanz mit Standardwerten überschreiben, obwohl dem Objekt bereits ein nicht standardmäßiger Wert zugewiesen wurde.
  • Es wäre weniger problematisch, wenn validated_data['language'] book.language wäre.

@pySilver - Yup, das wurde erst heute in https://github.com/tomchristie/django-rest-framework/pull/4346 gelöst.

Zufällig benötigen Sie default= im Serializer-Feld in Ihrem Beispiel nicht, da Sie einen Standardwert für ModelField haben.

@tomchristie Stimmen Sie zumindest zu, dass das aktuelle PUT-Verhalten keine RFC-Spezifikation ist? Und dass meine beiden Vorschläge (require all or inject defaults) es so machen würden?

@tomchristie tolle Neuigkeiten!

Zufällig benötigen Sie in dem Beispiel, das Sie haben, default= nicht für das Serializer-Feld, da Sie einen Standardwert für das ModelField haben.

Ja, ich wollte es nur für die Demo superexplizit machen.

Es wird endlich klar, dass @tomchristie nicht isoliert für/gegen Serializer-Verhalten argumentiert. Ich glaube, seine Einwände stammen (implizit) von der Anforderung, dass ein einzelner Serializer alle REST-Modi unterstützen muss. Dies zeigt sich in seinen Beschwerden darüber, wie ein strikter Serializer einen POST beeinflusst. Da die REST-Modi nicht kompatibel sind, ist die aktuelle Lösung ein Serialisierer, der nicht für einen einzelnen Modus spezifiziert ist.

Wenn das die wahre Wurzel des Einwands ist, lass es uns direkt angehen. Wie kann ein einzelner Serialisierer Spezifikationsverhalten für alle REST-Modi bereitstellen? Meine spontane Antwort ist, dass PARTIAL vs. NON-PARTIAL auf der falschen Ebene implementiert ist:

  • Wir haben partielle und nicht-partielle Serialisierer. Dieser Ansatz bedeutet, dass wir mehrere Serialisierer benötigen, um das Spezifikationsverhalten für alle Modi zu unterstützen.
  • Wir brauchen tatsächlich eine partielle vs. nicht partielle Validierung (oder so etwas in der Art). Die verschiedenen REST-Modi müssen unterschiedliche Validierungsmodi vom Serialisierer anfordern.

Um Bedenken zu trennen, sollte ein Serialisierer den REST-Modus nicht kennen, sodass er nicht als Serialisierer eines Drittanbieters implementiert werden kann (und ich vermute, dass der Serialisierer überhaupt keinen Zugriff auf den Modus hat). Stattdessen sollte DRF eine zusätzliche Information an den Serializer übergeben (ungefähr replace=True für PUT ). Der Serialisierer kann entscheiden, wie dies implementiert wird (alle Felder anfordern oder die Standardwerte einfügen).

Offensichtlich ist dies nur ein grober Vorschlag, aber vielleicht wird er den Stillstand überwinden.

Darüber hinaus ist PUT-as-create selbst eine gültige Operation, also wäre es wieder seltsam, wenn dies eine andere "erforderliche" Semantik als POST hätte.

Ich stimme zu, dass Sie mit einem PUT erstellen können, aber ich bin anderer Meinung, dass die Semantik dieselbe ist. PUT arbeitet an einer bestimmten Ressource:

Das PUT-Verfahren fordert an, dass der Zustand der Zielressource erstellt oder durch den Zustand ersetzt wird, der durch die in der Nutzlast der Anforderungsnachricht eingeschlossene Darstellung definiert ist.

Ich glaube daher, dass sich die Create-Semantik tatsächlich unterscheidet:

  • POSTbis /citizen/ erwartet, dass eine SSN (Sozialversicherungsnummer) generiert wird
  • SETZENbis /citizen/<SSN> aktualisiert die Daten für eine bestimmte Sozialversicherungsnummer. Wenn bei dieser SSN keine Daten vorhanden sind, führt dies zu einer Erstellung.

Da die „id“ im URI von PUT enthalten sein muss, können Sie sie wie erforderlich behandeln. Im Gegensatz dazu ist die "id" in einem POST optional.

Da die „id“ im URI von PUT enthalten sein muss, können Sie sie wie erforderlich behandeln. Im Gegensatz dazu ist die "id" in einem POST optional.

In der Tat. Ich bezog mich speziell auf die Tatsache, dass die vorgeschlagene Änderung von „PUT unbedingt _alle_ Felder erfordern“ bedeuten würde, dass sich PUT-as-create anders verhalten würde als POST-as-create wrt. ob Felder erforderlich sind oder nicht.

Trotzdem komme ich zu dem Wert, eine Option für PUT-is-strict-Verhalten zu haben.

(Erzwingen Sie, dass in diesem Fall _alle_ Felder unbedingt erforderlich sind, erzwingen Sie, dass in PATCH _keine_ Felder erforderlich sind, und verwenden Sie das required= -Flag für POST)

Wie kann ein einzelner Serialisierer Spezifikationsverhalten für alle REST-Modi bereitstellen?

Wir können zwischen Erstellen, Aktualisieren und Teilaktualisierung unterscheiden, je nachdem, wie der Serialisierer instanziiert wird, also denke ich nicht, dass das ein Problem ist.

Sie haben bereits darauf hingewiesen, dass Sie mit PUT oder POST create $ machen können. Sie haben unterschiedliche Semantik und unterschiedliche Anforderungen, sodass create gegenüber dem REST-Modus agnostisch sein muss. Ich denke, die Unterscheidung erfolgt wirklich als Teil von is_valid . Wir fragen nach einem bestimmten Validierungsmodus:

  • keine Feldpräsenzvalidierung (PATCH)
  • Validierung basierend auf required Flags (POST)
  • Strikte Feldpräsenzvalidierung (PUT)

Indem wir die schlüsselwortspezifische Logik aus den CRUD-Operationen heraushalten, reduzieren wir auch die Kopplung zwischen dem Serialisierer und DRF. Wenn die Validierungsmodi konfigurierbar wären, wären sie völlig universell (selbst wenn wir nur 3 spezifische Fälle für unsere 3 Schlüsselwörter implementieren).

Ihr macht einen guten Job, mir diese Funktionalität auszureden. :)

Unterschiedliche "Validierungsmodi" beim Aufruf von .is_valid() ist ein Umbruch, der nicht fliegen wird.

Wir _könnten_ vielleicht ein 'complete=True'-Gegenstück zu der bestehenden 'partial=True'-Einheit kwarg in Betracht ziehen. Das würde problemlos in die derzeitige Funktionsweise passen und würde immer noch den Fall der "strengen Felder" unterstützen.

Ist der Serializer der richtige Ort, um dieses Problem zu lösen? Diese Anforderung ist eng an die REST-Schlüsselwörter gekoppelt, also ist das vielleicht der richtige Ort, um sie durchzusetzen. Um diesen Ansatz zu unterstützen, muss der Serialisierer nur eine Liste von Feldern bereitstellen, die er als Eingaben akzeptiert.

Eher nebenbei ... gibt es irgendwo eine gute Diskussion über Djangos Trennung (Zuweisung) von Bedenken? Ich habe Probleme, mich auf Django-freundliche Antworten zu beschränken, weil ich die Antwort auf Fragen wie "Warum ist die Validierung Teil der Serialisierung" nicht kenne. Die Serialisierungsdokumentation für 1.9 erwähnt nicht einmal die Validierung. Und streng vom ersten Prinzip an scheint es so:

  1. Das Modell sollte für die Validierung der internen Konsistenz verantwortlich sein und
  2. Die "Ansicht" (in diesem Fall der REST-Modus-Prozessor) sollte für die Durchsetzung von Geschäftsregeln (wie dem RFC) in Bezug auf diese Ansicht verantwortlich sein.

Wenn die Verantwortung für die Validierung wegfällt, können Serialisierer zu 100 % partiell (standardmäßig) und auf E/A-Regeln wie „schreibgeschützt“ spezialisiert sein. Ein auf diese Weise erstellter ModelSerializer würde eine Vielzahl von Ansichten unterstützen.

Ist der Serializer der richtige Ort, um dieses Problem zu lösen?

Ja.

Die Serialisierungsdokumentation für 1.9 erwähnt nicht einmal die Validierung.

Djangos integrierte Serialisierung ist für Web-APIS nicht nützlich, sie ist wirklich auf das Dumping und Laden von Fixtures beschränkt.

Sie kennen die architektonischen Annahmen sowohl von Django als auch von DRF besser als ich, also muss ich mich auf Sie verlassen, wenn es um das Wie geht. Sicherlich hat ein init Kwarg das richtige Gefühl dafür ... den Serialisierer "on-demand" neu zu konfigurieren. Die einzige Einschränkung besteht darin, dass sie nicht "on the fly" neu konfiguriert werden können, aber ich gehe davon aus, dass die Instanzen zur einmaligen Verwendung bestimmt sind, sodass dies kein wesentliches Problem darstellt.

Ich werde dies vorerst de-meilensteinen. Wir können nach v3.7 neu bewerten

Bis zu Ihnen, aber ich möchte sicherstellen, dass Sie sich darüber im Klaren sind, dass dies kein Ticket zum Hinzufügen von Parallelitätsunterstützung ist. Das eigentliche Problem besteht darin, dass ein einzelner Serializer in der aktuellen Architektur nicht sowohl PUT als auch POST korrekt validieren kann. Parallelität lieferte nur den "fehlenden Test".

TL;DR Sie können sehen, warum dieses Problem blockiert ist, indem Sie mit Toms vorgeschlagener Lösung beginnen.

Zusammenfassend besteht die vorgeschlagene Lösung darin, alle Felder für eine PUT -Anforderung erforderlich zu machen. Bei diesem Ansatz gibt es (mindestens) zwei Probleme:

  1. Serialisierer denken in Aktionen, nicht in HTTP-Methoden, daher gibt es keine Eins-zu-eins-Zuordnung. Das offensichtliche Beispiel ist create , weil es von PUT und POST geteilt wird. Beachten Sie, dass create-by- PUT standardmäßig deaktiviert ist, daher ist die vorgeschlagene Lösung wahrscheinlich besser als nichts.
  2. Wir müssen nicht alle Felder in einem PUT verlangen (ein Gefühl, das von #3648, #4703 geteilt wird). Wenn ein Nillable-Feld fehlt, wissen wir, dass es None sein kann. Wenn ein Feld mit einem Standard fehlt, wissen wir, dass wir den Standard verwenden können. PUT s haben tatsächlich die gleichen (vom Modell abgeleiteten) Feldanforderungen wie POST .

Das eigentliche Problem ist, wie wir mit fehlenden Daten und dem grundlegenden Vorschlag in #3648, #4703 umgehen, und hier bleibt die richtige Lösung. Wir können alle HTTP-Modi (einschließlich create-by- PUT ) unterstützen, wenn wir ein Konzept wie if_missing_use_default einführen. Mein ursprünglicher Vorschlag stellte es als Ersatz für partial dar, aber es ist einfacher (und möglicherweise notwendig), es als orthogonales Konzept zu betrachten.

wenn wir ein Konzept wie if_missing_use_default einführen.

Es gibt nichts, was jemanden daran hindert, entweder dies oder ein striktes „Alle Felder erforderlich“ als Basis-Serializer-Klasse zu implementieren und dies als Bibliothek eines Drittanbieters zu verpacken.

Meiner Meinung nach könnte ein strenger Modus "alle Felder erfordern" auch in den Kern gelangen, es ist ein sehr klares, offensichtliches Verhalten, und ich kann sehen, warum das nützlich wäre.

Ich bin nicht davon überzeugt, dass "Felder optional sein dürfen, aber alles ersetzen, Modellstandards verwenden, falls vorhanden" - Das scheint ein sehr kontraintuitives Verhalten darzustellen (z. B. "created_at" -Felder, die automatisch enden aktualisieren sich selbst). Wenn wir ein strengeres Verhalten wollen, sollten wir einfach ein strengeres Verhalten haben.

Wie auch immer, der richtige Weg, dies anzugehen, besteht darin, es als Paket eines Drittanbieters zu validieren und dann unsere Dokumente zu aktualisieren, damit wir darauf verlinken können.

Wenn Sie alternativ davon überzeugt sind, dass uns ein Verhalten im Kern fehlt, das unsere Benutzer wirklich brauchen, können Sie gerne eine Pull-Anfrage stellen und das Verhalten und die Dokumentation aktualisieren, damit wir die Vorzüge in Kürze bewerten können konkreter Weg.

Ich freue mich, Pull-Requests als Ausgangspunkt dafür zu nehmen, und noch glücklicher, ein Paket eines Drittanbieters einzuschließen, das dieses Verhalten demonstriert.

um den Wert herum zu kommen, eine Option für PUT-is-strict-Verhalten zu haben.

Das steht noch. Ich denke, wir könnten diesen Aspekt im Kern berücksichtigen, wenn sich jemand genug darum kümmert, um eine Pull-Anfrage in diese Richtung zu stellen. Es müsste ein optionales Verhalten sein.

Das scheint ein sehr kontraintuitives Verhalten darzustellen (z. B. "created_at" -Felder, die sich automatisch selbst aktualisieren).

Ein created_at -Feld sollte read_only sein (oder vom Serializer ausgeschlossen werden). In beiden Fällen bleibt es unverändert (das normale Serializer-Verhalten). In dem kontraintuitiven Fall, dass das Feld im Serializer nicht schreibgeschützt ist, würden Sie das kontraintuitive Verhalten erhalten, es automatisch zu ändern.

Ich freue mich, Pull-Requests als Ausgangspunkt dafür zu nehmen, und noch glücklicher, ein Paket eines Drittanbieters einzuschließen, das dieses Verhalten demonstriert.

Absolut. Die Variante "Standardwerte verwenden" ist ein idealer Fall für ein Drittanbieterpaket, da die Änderung ein trivialer Wrapper um (eine Methode) des vorhandenen Verhaltens ist und (wenn Sie sich für das Standardargument entscheiden) für alle nicht-partiellen Serialisierer funktioniert.

tomchristie hat dies vor 4 Stunden geschlossen

Vielleicht ziehen Sie es in Erwägung, ein Label wie „PR Welcome“ oder „3rd Party Plugin“ hinzuzufügen und gültige/erkannte Probleme wie dieses offen zu lassen. Ich suche oft nach offenen Problemen, um zu sehen, ob ein Problem bereits gemeldet wurde und wie weit es mit der Lösung ist. Geschlossene Issues nehme ich als "ungültig" oder "behoben" wahr. Das Mischen einiger „gültiger, aber geschlossener“ Probleme mit Tausenden von ungültigen/behobenen Problemen lädt nicht zu einer effizienten Suche ein (selbst wenn Sie wussten, dass sie vorhanden sein könnten).

Vielleicht möchten Sie ein Label wie „PR Welcome“ oder „3rd Party Plugin“ hinzufügen.

Das wäre vernünftig genug, aber wir möchten, dass unser Issue-Tracker die aktive oder umsetzbare Arbeit am Projekt selbst widerspiegelt.

Es ist wirklich wichtig, dass wir versuchen, unsere Probleme eng zu fassen. Das Ändern von Prioritäten kann bedeuten, dass wir uns irgendwann dafür entscheiden, Probleme wieder zu öffnen, die wir zuvor geschlossen haben. Im Moment denke ich, dass dies aus dem „Das Kernteam möchte dies in unmittelbarer Zukunft ansprechen“ herausgefallen ist.

Wenn es wiederholt auftaucht und es weiterhin keine Lösung eines Drittanbieters gibt, würden wir es vielleicht neu bewerten.

gültige/anerkannte Probleme wie diese offen lassen.

Etwas mehr Kontext zum Issue-Management-Stil – https://www.dabapps.com/blog/sustainable-open-source-management/

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen