Django Rest Framework - Як додати власне поле в ModelSerializer


89

Я створив ModelSerializerі хочу додати власне поле, яке не є частиною моєї моделі.

Я знайшов опис, щоб додати сюди додаткові поля, і спробував наступне:

customField = CharField(source='my_field')

Коли я додаю це поле і викликаю свою validate()функцію, це поле не є частиною attrдикту. attrмістить усі зазначені поля моделі, крім зайвих. Тож я не можу отримати доступ до цього поля під час перезаписаної перевірки, чи не так?

Коли я додаю це поле до списку полів таким чином:

class Meta:
    model = Account
    fields = ('myfield1', 'myfield2', 'customField')

тоді я отримую помилку, оскільки customFieldце не є частиною моєї моделі - що правильно, бо я хочу додати її саме для цього серіалізатора.

Чи є спосіб додати власне поле?


Не могли б ви розширити текст "Але коли мого поля немає в списку полів моделі, зазначеному в серіалізаторі, це не є частиною validate () attr-словника.", Я не впевнений, що це дуже зрозуміло.
Tom Christie

Також "він скаржиться - правильно - що у мене немає поля customField у моїй моделі.", Не могли б ви бути явними щодо винятку, який ви бачите - дякую! :)
Том Крісті

Я оновив свою публікацію і сподіваюся, що зараз вона зрозуміліша. Я просто хочу знати, як я можу додати поле, яке не є частиною моєї моделі ...
Рон,


Відповіді:


63

Ви робите правильно, за винятком того, що CharField(та інші введені поля) призначені для записуваних полів.

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

customField = Field(source='get_absolute_url')

4
дякую, але я хочу поле для запису. Я передаю певний токен користувача, який ідентифікує мого користувача. але в моїй моделі у мене є користувач, а не маркер. тому я хочу передати маркер і "перетворити" його в об'єкт користувача за допомогою запиту.
Рон

наступне - джерело має націлювати атрибут моделі, так? у моєму випадку у мене немає атрибута, на який можна вказувати.
Рон,

Я не розумію користувача / маркера коментаря. Але якщо ви хочете включити в серіалізатор поле, яке .validate()видаляється перед відновленням в екземпляр моделі, ви можете скористатися методом для видалення атрибута. Дивіться: django-rest-framework.org/api-guide/serializers.html#validation Що б робити те , що вам потрібно, хоча я не дуже розумію , прецедентів, або чому ви хочете записується поле , який прив'язаний до властивість лише для читанняget_absolute_url ?
Tom Christie

забудьте про те, що get_absolute_urlя просто скопіював та вставив його з документів. Я просто хочу нормальне поле для запису, до якого я можу отримати доступ у validate(). Я просто здогадався, що мені знадобиться sourceатрибут ...
Рон

Це має більше сенсу. :) Значення має передаватися для перевірки, тому я б двічі перевірив, як ви створюєте інстанцію серіалізатора, і чи справді це значення надається.
Tom Christie

82

Насправді є рішення, не торкаючись моделі. Ви можете використовувати такі, SerializerMethodFieldякі дозволяють підключити будь-який метод до вашого серіалізатора.

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

    def get_foo(self, obj):
        return "Foo id: %i" % obj.pk

6
Як згадував ОП у цьому коментарі , вони хочуть SerializerMethodField
записати

14

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

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

3
Що робити, якщо я хочу, щоб його можна було записувати?
Csaba Toth

1
@Csaba: Вам просто потрібно писати призначені для користувача економії і видалення гачків для додаткового контенту: дивіться в розділі «Зберегти і видалення гачки» в розділі «Методи» ( тут ) Вам потрібно писати призначені для користувача perform_create(self, serializer), perform_update(self, serializer), perform_destroy(self, instance).
Ліндаусон,

13

тут відповідь на ваше запитання. ви повинні додати до свого облікового запису моделі:

@property
def my_field(self):
    return None

тепер ви можете використовувати:

customField = CharField(source='my_field')

джерело: https://stackoverflow.com/a/18396622/3220916


6
Я використовував такий підхід, коли це має сенс, але не здорово додавати додатковий код до моделей для речей, які насправді використовуються лише для певних викликів API.
Енді Бейкер,

1
Ви можете написати проксі-модель для
ashwoods

10

Щоб показати self.author.full_name, я отримав помилку з Field. Це працювало з ReadOnlyField:

class CommentSerializer(serializers.HyperlinkedModelSerializer):
    author_name = ReadOnlyField(source="author.full_name")
    class Meta:
        model = Comment
        fields = ('url', 'content', 'author_name', 'author')

6

З останньою версією Django Rest Framework вам потрібно створити метод у своїй моделі з назвою поля, яке потрібно додати.

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

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

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

3

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

Здається, вам дійсно потрібно написати власний простий серіалізатор.

class PassThroughSerializer(serializers.Field):
    def to_representation(self, instance):
        # This function is for the direction: Instance -> Dict
        # If you only need this, use a ReadOnlyField, or SerializerField
        return None

    def to_internal_value(self, data):
        # This function is for the direction: Dict -> Instance
        # Here you can manipulate the data if you need to.
        return data

Тепер ви можете використовувати цей серіалізатор для додавання користувацьких полів до ModelSerializer

class MyModelSerializer(serializers.ModelSerializer)
    my_custom_field = PassThroughSerializer()

    def create(self, validated_data):
        # now the key 'my_custom_field' is available in validated_data
        ...
        return instance

Це також працює, якщо модель MyModelнасправді має властивість, що викликається, my_custom_fieldале ви хочете ігнорувати її валідатори.


отже, це не працює, якщо my_custom_field не є властивістю MyModel, так? Я отримав помилку. Поле серіалізатора може бути названо неправильно і не відповідатиме жодному атрибуту чи ключу в MyModelекземплярі.
Сандіп Балагоп,

2

Прочитавши всі відповіді тут, мій висновок полягає в тому, що зробити це чисто неможливо. Вам потрібно грати брудно і робити щось хакешське, наприклад, створювати поле write_only, а потім перевизначити методи validateand to_representation. Це те, що мені вдалося:

class FooSerializer(ModelSerializer):

    foo = CharField(write_only=True)

    class Meta:
        model = Foo
        fields = ["foo", ...]

    def validate(self, data):
        foo = data.pop("foo", None)
        # Do what you want with your value
        return super().validate(data)

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data["foo"] = whatever_you_want
        return data
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.