Django Rest Framework - Как добавить настраиваемое поле в ModelSerializer

89

Я создал ModelSerializerи хочу добавить настраиваемое поле, которое не является частью моей модели.

Я нашел описание для добавления дополнительных полей сюда и попробовал следующее:

customField = CharField(source='my_field')

Когда я добавляю это поле и вызываю свою validate()функцию, это поле не является частью attrdict. attrсодержит все указанные поля модели, кроме дополнительных полей. Значит, я не могу получить доступ к этому полю в моей перезаписанной проверке, можно?

Когда я добавляю это поле в список полей следующим образом:

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

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

Есть ли способ добавить настраиваемое поле?

Рон
источник
Не могли бы вы развить тему «Но когда мое поле отсутствует в списке полей модели, указанном в сериализаторе, оно не является частью словаря атрибутов validate ()». Я не уверен, что это очень ясно.
Том Кристи
Также «он жалуется - правильно - что у меня нет поля customField в моей модели.», Не могли бы вы явным образом указать на наблюдаемое вами исключение - спасибо! :)
Том Кристи
Я обновил свой пост и надеюсь, что теперь он стал понятнее. Я просто хочу знать, как я могу добавить поле, которое не является частью моей модели ...
Рон

Ответы:

63

Вы поступаете правильно, за исключением того, что CharField(и другие типизированные поля) предназначены для полей с возможностью записи.

В этом случае вам просто нужно простое поле только для чтения, поэтому вместо этого просто используйте:

customField = Field(source='get_absolute_url')
Том Кристи
источник
4
спасибо, но мне нужно записываемое поле. Я передаю определенный токен пользователя, который идентифицирует моего пользователя. но в моей модели у меня есть пользователь, а не токен. поэтому я хочу передать токен и «преобразовать» его в пользовательский объект через запрос.
Рон
следующее: источник должен нацеливаться на атрибут модели, верно? в моем случае у меня нет атрибута, на который можно было бы указать.
Рон
Я не понимаю бит пользователя / токена в комментарии. Но если вы хотите включить в сериализатор поле, которое удаляется перед восстановлением в экземпляр модели, вы можете использовать этот .validate()метод для удаления атрибута. См .: django-rest-framework.org/api-guide/serializers.html#validation. Это сделает то, что вам нужно, хотя я не совсем понимаю вариант использования или почему вам нужно поле с возможностью записи , привязанное к свойство только для чтенияget_absolute_url ?
Том Кристи
забудьте о том, что get_absolute_urlя просто скопировал и вставил его из документации. Мне просто нужно обычное поле с возможностью записи, к которому я могу получить доступ в validate(). Я просто догадался, что мне понадобится sourceатрибут ...
Рон
В этом больше смысла. :) Значение должно быть передано для проверки, поэтому я дважды проверю, как вы создаете экземпляр сериализатора, и действительно ли это значение предоставляется.
Том Кристи
82

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

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

    def get_foo(self, obj):
        return "Foo id: %i" % obj.pk
Айдахо
источник
6
Как упомянул OP в этом комментарии , им нужно поле 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).
Lindauson
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',)
Гийом Винсент
источник
4

Я искал решение для добавления настраиваемого поля с возможностью записи в сериализатор модели. Я нашел этот, который не был рассмотрен в ответах на этот вопрос.

Похоже, вам действительно нужно написать собственный простой сериализатор.

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

Прочитав здесь все ответы, я пришел к выводу, что это невозможно сделать чисто. Вы должны играть грязные и делать что - то hadkish как создать write_only поле , а затем переопределить validateи 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
Ариэль
источник