Django REST Framework: Hinzufügen eines zusätzlichen Felds zu ModelSerializer

147

Ich möchte ein Modell serialisieren, möchte jedoch ein zusätzliches Feld einfügen, in dem einige Datenbanksuchen für die zu serialisierende Modellinstanz durchgeführt werden müssen:

class FooSerializer(serializers.ModelSerializer):
  my_field = ... # result of some database queries on the input Foo object
  class Meta:
        model = Foo
        fields = ('id', 'name', 'myfield')

Was ist der richtige Weg, um dies zu tun? Ich sehe, dass Sie zusätzlichen "Kontext" an den Serializer übergeben können. Ist die richtige Antwort, um das zusätzliche Feld in einem Kontextwörterbuch zu übergeben? Bei diesem Ansatz wäre die Logik zum Abrufen des von mir benötigten Felds nicht in der Serializer-Definition enthalten, was ideal ist, da jede serialisierte Instanz benötigt wird my_field. An anderer Stelle in der Dokumentation zu DRF-Serialisierern heißt es : "Zusätzliche Felder können jeder Eigenschaft entsprechen oder im Modell aufgerufen werden können." Sind es zusätzliche Felder, über die ich spreche? Sollte ich in Fooder Modelldefinition eine Funktion definieren, die einen my_fieldWert zurückgibt , und im Serializer my_field mit diesem aufrufbaren Element verbinden? Wie sieht das aus?

Vielen Dank im Voraus, gerne klären Sie die Frage, falls erforderlich.

Neil
quelle

Antworten:

224

Ich denke, SerializerMethodField ist das, wonach Sie suchen:

class FooSerializer(serializers.ModelSerializer):
  my_field = serializers.SerializerMethodField('is_named_bar')

  def is_named_bar(self, foo):
      return foo.name == "bar" 

  class Meta:
    model = Foo
    fields = ('id', 'name', 'my_field')

http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield

JP
quelle
19
Ist es möglich, solchen Feldern eine Validierung hinzuzufügen? Meine Frage ist: Wie akzeptiere ich benutzerdefinierte POST-Werte, die validiert werden können und im post_save () -Handler verarbeitet werden?
Alp
20
Beachten Sie, dass SerializerMethodField schreibgeschützt ist, sodass dies für eingehende POST / PUT / PATCH nicht funktioniert.
Scott A
24
In DRF 3 wird es in field_name = serializers.SerializerMethodField()unddef get_field_name(self, obj):
Chemical Programmer
1
Was ist, foowenn ein SerializerMethodField def? Wenn Sie CreateAPIView verwenden, wird das foo gespeichert und kann dann die Methode is_named_bar () verwendet werden?
244boy
Wenn Sie also basierend auf dieser Antwort beispielsweise 12 zusätzliche Felder an Ihren Serializer übergeben möchten, müssen Sie 12 spezifische Methoden für jedes Feld definieren, das nur foo.field_custom zurückgibt.
AlxVallejo
41

Mit diesem Ansatz können Sie Ihre Modellmethode in Eigenschaft ändern und im Serializer verwenden.

class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .

class FooSerializer(ModelSerializer):
    my_field = serializers.ReadOnlyField(source='my_field')

    class Meta:
        model = Foo
        fields = ('my_field',)

Bearbeiten: Mit den neuesten Versionen des Rest Frameworks (ich habe 3.3.3 ausprobiert) müssen Sie nicht zur Eigenschaft wechseln. Die Modellmethode funktioniert einwandfrei.

Wasil Sergejczyk
quelle
Danke @Wasil! Ich bin mit der Verwendung von Eigenschaften in Django-Modellen nicht vertraut und kann keine gute Erklärung dafür finden, was dies bedeutet. Können Sie erklären? Was ist der Sinn des @property Dekorateurs hier?
Neil
2
Dies bedeutet, dass Sie diese Methode wie eine Eigenschaft aufrufen können: Das heißt, Sie erhalten variable = model_instance.my_fielddas gleiche Ergebnis wie variable = model_instance.my_field()ohne den Dekorateur. auch: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk
1
Dies funktioniert zumindest in Django 1.5.1 / djangorestframework == 2.3.10 nicht. Der ModelSerializer erhält nicht die richtige Eigenschaft, selbst wenn explizit auf das Meta-Attribut "Felder" verwiesen wird.
Ygneo
9
Sie müssen das Feld zum Serializer hinzufügen, da es kein echtes Modelfield ist: my_field = serializers.Field (source = 'my_field')
Marius
2
source='my_field' wird nicht mehr benötigt und eine Ausnahme
auslösen
13

Bei der letzten Version von Django Rest Framework müssen Sie in Ihrem Modell eine Methode mit dem Namen des Felds erstellen, das Sie hinzufügen möchten. Keine Notwendigkeit @propertyund source='field'Fehler auslösen.

class Foo(models.Model):
    . . .
    def foo(self):
        return 'stuff'
    . . .

class FooSerializer(ModelSerializer):
    foo = serializers.ReadOnlyField()

    class Meta:
        model = Foo
        fields = ('foo',)
Guillaume Vincent
quelle
Was ist, wenn ich ein requestObjekt in def foo (self) haben möchte, das den Wert von foo ändern könnte? (Beispiel eine request.user-basierte Suche)
saran3h
1
Was ist, wenn der Wert von foo von der Anfrage kommt?
Saran3h
11

Meine Antwort auf eine ähnliche Frage ( hier ) könnte nützlich sein.

Wenn Sie eine Modellmethode wie folgt definiert haben:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Sie können das Ergebnis des Aufrufs dieser Methode wie folgt zu Ihrem Serializer hinzufügen:

class MyModelSerializer(serializers.ModelSerializer):
    model_method_field = serializers.CharField(source='model_method')

ps Da das benutzerdefinierte Feld in Ihrem Modell nicht wirklich ein Feld ist, möchten Sie es normalerweise schreibgeschützt machen, wie folgt:

class Meta:
    model = MyModel
    read_only_fields = (
        'model_method_field',
        )
Lindauson
quelle
10

Wenn Sie in Ihrem zusätzlichen Feld lesen und schreiben möchten, können Sie einen neuen benutzerdefinierten Serializer verwenden, der die Serializer erweitert. Serializer, und ihn wie folgt verwenden

class ExtraFieldSerializer(serializers.Serializer):
    def to_representation(self, instance): 
        # this would have the same as body as in a SerializerMethodField
        return 'my logic here'

    def to_internal_value(self, data):
        # This must return a dictionary that will be used to
        # update the caller's validation data, i.e. if the result
        # produced should just be set back into the field that this
        # serializer is set to, return the following:
        return {
          self.field_name: 'Any python object made with data: %s' % data
        }

class MyModelSerializer(serializers.ModelSerializer):
    my_extra_field = ExtraFieldSerializer(source='*')

    class Meta:
        model = MyModel
        fields = ['id', 'my_extra_field']

Ich benutze dies in verwandten verschachtelten Feldern mit einer benutzerdefinierten Logik

Marco Silva
quelle
2

Dies hat bei mir funktioniert. Wenn wir nur ein zusätzliches Feld in ModelSerializer hinzufügen möchten, können wir dies wie folgt tun und dem Feld kann nach einigen Berechnungen der Suche ein Wert zugewiesen werden. Oder in einigen Fällen, wenn wir die Parameter in der API-Antwort senden möchten.

In model.py

class Foo(models.Model):
    """Model Foo"""
    name = models.CharField(max_length=30, help_text="Customer Name")



   **

In serializer.py

** **.

class FooSerializer(serializers.ModelSerializer):
    retrieved_time = serializers.SerializerMethodField()

    @classmethod
    def get_retrieved_time(self, object):
        """getter method to add field retrieved_time"""
        return None

  class Meta:
        model = Foo
        fields = ('id', 'name', 'retrieved_time ')

** **.

Hoffe das könnte jemandem helfen

** **.

Vinay Kumar
quelle
0

Wie Chemical Programer in diesem Kommentar sagte , können Sie es in der neuesten DRF einfach so machen:

class FooSerializer(serializers.ModelSerializer):
    extra_field = serializers.SerializerMethodField()

    def get_extra_field(self, foo_instance):
        return foo_instance.a + foo_instance.b

    class Meta:
        model = Foo
        fields = ('extra_field', ...)

DRF-Dokumentquelle

trici0pa
quelle