Django REST Framework: додавання додаткового поля до ModelSerializer


147

Я хочу серіалізувати модель, але хочу включити додаткове поле, для якого потрібно серіалізувати деякі пошукові бази даних на прикладі моделі:

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

Який правильний спосіб це зробити? Я бачу, що ви можете передати додатковий "контекст" до серіалізатора, це правильна відповідь для передачі у додаткове поле у ​​контекстному словнику? При такому підході логіка отримання потрібного мені поля не повинна містити визначення серіалізатора, що ідеально, оскільки знадобиться кожен серіалізований екземпляр my_field. В іншому випадку в документації на серіалізатори DRF написано, що "додаткові поля можуть відповідати будь-якій властивості або називатися в моделі". Чи є зайві поля, про що я говорю? Чи повинен я визначити функцію у Fooвизначенні моделі, яка повертає my_fieldзначення, і в серіалізаторі я підключу my_field до цього виклику? Як це виглядає?

Заздалегідь дякую, із задоволенням уточнюйте питання, якщо потрібно.

Відповіді:


224

Я думаю, що ви шукаєте SerializerMethodField:

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
чи можна додати перевірку до таких полів? моє запитання: як прийняти власні значення POST, які можна перевірити та обробити в обробнику post_save ()?
Альп

20
Зауважте, що SerializerMethodField доступний лише для читання, тому це не працюватиме для вхідних POST / PUT / PATCH.
Скотт А

24
У DRF 3 його змінено на field_name = serializers.SerializerMethodField()таdef get_field_name(self, obj):
Програміст з хімічних речовин

1
Що робити, fooколи визначається SerializerMethodField? під час використання CreateAPIView, чи зберігається foo, то може використовувати метод is_named_bar ()?
244бой

Отже, виходячи з цієї відповіді, якщо ви хочете передати, скажімо, 12 додаткових полів у свій серіалізатор, вам потрібно визначити 12 конкретних методів для кожного поля, що тільки повертає foo.field_custom?
AlxVallejo

41

Ви можете змінити метод моделі на властивість та використовувати його в серіалізаторі при такому підході.

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

Редагувати: З останніми версіями системи відпочинку (я спробував 3.3.3) вам не потрібно змінювати властивість. Модельний метод просто буде добре працювати.


Дякую @Wasil! Я не знайомий з використанням властивостей у моделях Джанго і не можу знайти хорошого пояснення того, що це означає. Ти можеш пояснити? У чому сенс декоратора @property?
Ніл

2
це означає, що ви можете назвати цей метод як властивість: тобто variable = model_instance.my_fieldдає такий же результат, як і variable = model_instance.my_field()без декоратора. також: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
Це не працює, принаймні у Django 1.5.1 / djangorestframework == 2.3.10. ModelSerializer не отримує властивості, навіть якщо експліцитно вказано в "полі" атрибута Meta.
ygneo

9
вам потрібно додати поле до серіалізатора, оскільки це не справжнє модельне поле: my_field = serializers.Field (source = 'my_field')
Маріус

2
source='my_field' не вимагати більше і підняти виняток
Гійом Вінсент

13

З останньою версією Django Rest Framework вам потрібно створити метод у своїй моделі з назвою поля, яке ви хочете додати. Немає необхідності @propertyі source='field'викликати помилку.

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

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

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

що робити, якщо я хочу мати requestоб'єкт def def (foo), який міг би змінити значення foo? (наприклад, пошук на основі запиту на користувачі)
saran3h

1
Що робити, якщо значення foo надходить від запиту?
saran3h

11

Моя відповідь на подібне запитання ( тут ) може бути корисною.

Якщо у вас метод моделі визначений наступним чином:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Ви можете додати результат виклику зазначеного методу до свого серіалізатора так:

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

ps Оскільки користувацьке поле насправді не є полем у вашій моделі, зазвичай ви хочете зробити його лише для читання, наприклад:

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

10

якщо ви хочете читати і писати у вашому додатковому полі, ви можете використовувати новий спеціальний серіалізатор, який розширює серіалізатори.Serializer, і використовувати його так

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

Я використовую це у пов'язаних вкладених полях з певною власною логікою


2

Це працювало для мене. Якщо ми хочемо просто додати додаткове поле в ModelSerializer, ми можемо зробити це як нижче, а також поле може бути призначене валь після деяких розрахунків пошуку. Або в якомусь випадку, якщо ми хочемо надіслати параметри у відповіді API.

У model.py

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



   **

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

**

Сподіваюся, це може комусь допомогти

**


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.