Django REST Framework: dodanie dodatkowego pola do ModelSerializer


147

Chcę serializować model, ale chcę dołączyć dodatkowe pole, które wymaga wykonywania niektórych wyszukiwań bazy danych w wystąpieniu modelu do serializacji:

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

Jaki jest właściwy sposób, aby to zrobić? Widzę, że można przekazać w dodatkowym „kontekście” do serializatora, czy poprawna odpowiedź jest przekazywana w dodatkowym polu w słowniku kontekstowym? Przy takim podejściu logika uzyskiwania pola, którego potrzebuję, nie byłaby niezależna od definicji serializatora, co jest idealne, ponieważ każda serializowana instancja będzie potrzebować my_field. W innym miejscu dokumentacji serializatorów DRF jest napisane , że „dodatkowe pola mogą odpowiadać dowolnej właściwości lub wywoływać w modelu”. Czy mówię o dodatkowych polach? Czy powinienem zdefiniować funkcję w Foodefinicji modelu, która zwraca my_fieldwartość, aw serializatorze podłączyć my_field do tego wywoływalnego? Jak to wygląda?

Z góry dziękuję, w razie potrzeby chętnie wyjaśnię pytanie.

Odpowiedzi:


224

Myślę, że SerializerMethodField jest tym, czego szukasz:

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


19
czy można dodać walidację do takich pól? Moje pytanie brzmi: jak zaakceptować niestandardowe wartości POST, które można zweryfikować i przetworzyć w module obsługi post_save ()?
Alp

20
Zauważ, że SerializerMethodField jest tylko do odczytu, więc nie zadziała dla przychodzących POST / PUT / PATCH.
Scott A

24
W DRF 3 zmienia się na field_name = serializers.SerializerMethodField()idef get_field_name(self, obj):
Programator chemiczny

1
co fookiedy def a SerializerMethodField? kiedy używasz CreateAPIView, czy foo zostały zapisane, a następnie czy możesz użyć metody is_named_bar ()?
244boy

Tak więc, w oparciu o tę odpowiedź, jeśli chcesz przekazać powiedzmy 12 dodatkowych pól do swojego serializatora, musisz zdefiniować 12 konkretnych metod dla każdego pola, które po prostu zwraca foo.field_custom?
AlxVallejo

41

Możesz zmienić metodę modelu na właściwość i używać jej w serializatorze za pomocą tego podejścia.

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',)

Edycja: Z ostatnimi wersjami frameworka REST (próbowałem 3.3.3), nie musisz zmieniać właściwości. Metoda modelu będzie działać dobrze.


Dzięki @Wasil! Nie jestem zaznajomiony z używaniem właściwości w modelach Django i nie mogę znaleźć dobrego wyjaśnienia, co to oznacza. Możesz wytłumaczyć? Jaki jest tutaj sens dekoratora @property?
Neil

2
oznacza to, że możesz nazwać tę metodę jak właściwość, tj. variable = model_instance.my_fielddaje taki sam wynik jak variable = model_instance.my_field()bez dekoratora. także: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
To nie działa, przynajmniej w Django 1.5.1 / djangorestframework == 2.3.10. ModelSerializer nie uzyskuje właściwości, nawet jeśli jest wyraźnie określony w atrybucie meta „pola”.
ygneo

9
musisz dodać pole do serializatora, ponieważ nie jest to prawdziwe modelfield: my_field = serializers.Field (source = 'my_field')
Marius

2
source='my_field' nie jest już wymagane i zgłoś wyjątek
Guillaume Vincent

13

W ostatniej wersji Django Rest Framework musisz stworzyć w swoim modelu metodę z nazwą pola, które chcesz dodać. Nie ma potrzeby @propertyi source='field'zgłosi błąd.

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

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

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

co jeśli chcę mieć requestobiekt wewnątrz def foo (self), który mógłby modyfikować wartość foo? (przykład wyszukiwania na podstawie request.user)
saran3h

1
A co jeśli wartość foo pochodzi z żądania?
saran3h

11

Moja odpowiedź na podobne pytanie ( tutaj ) może być przydatna.

Jeśli masz metodę modelu zdefiniowaną w następujący sposób:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Możesz dodać wynik wywołania tej metody do swojego serializatora w następujący sposób:

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

ps Ponieważ pole niestandardowe nie jest tak naprawdę polem w Twoim modelu, zazwyczaj chcesz ustawić je jako tylko do odczytu, na przykład:

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

10

jeśli chcesz czytać i pisać w swoim dodatkowym polu, możesz użyć nowego niestandardowego serializatora, który rozszerza serializatory.Serializer i używać go w ten sposób

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']

używam tego w powiązanych polach zagnieżdżonych z pewną niestandardową logiką


2

U mnie to zadziałało .Jeśli chcemy po prostu dodać dodatkowe pole w ModelSerializer, możemy to zrobić jak poniżej, a także pole może mieć przypisaną wartość po kilku obliczeniach wyszukiwania. Lub w niektórych przypadkach, jeśli chcemy wysłać parametry w odpowiedzi API.

W model.py

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



   **

W 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 ')

**

Mam nadzieję, że to może komuś pomóc

**


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.