Django REST框架:向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连接到该可调用项吗?看起来像什么?

在此先感谢您,如有必要,我们很乐意澄清问题。

Answers:


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()处理程序中进行处理?
阿尔卑斯山2014年

20
请注意,SerializerMethodField是只读的,因此这不适用于传入的POST / PUT / PATCH。
Scott A

24
在DRF 3中,将其更改为field_name = serializers.SerializerMethodField()def get_field_name(self, obj):
Chemical Programmer

1
什么foo时候定义一个SerializerMethodField?使用CreateAPIView时,是否已存储foo,然后可以使用is_named_bar()方法?
244boy17年

因此,基于此答案,如果您想将12个额外的字段传递给序列化程序,则需要为每个仅返回foo.field_custom的字段定义12个特定方法。
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',)

编辑:使用rest框架的最新版本(我尝试过3.3.3),您无需更改为属性。模型方法将正常工作。


谢谢@Wasil!我对Django模型中属性的使用不熟悉,因此找不到很好的解释。你可以解释吗?@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中。即使在“字段” Meta属性中明确引用,ModelSerializer也不会获得属性。
ygneo 2014年

9
您需要将字段添加到序列化器中,因为它不是真正的模型字段:my_field = serializers.Field(source ='my_field')
Marius

2
source='my_field' 不再需要并引发例外
Guillaume Vincent

13

在最新版本的Django Rest Framework中,您需要使用要添加的字段名称在模型中创建一个方法。无需@propertysource='field'引发错误。

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

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

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

如果我想request在def foo(self)中包含可以修改foo值的对象怎么办?(例如一个基于request.user的查找)
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

如果您想在额外的字段上进行读写,则可以使用新的自定义序列化程序,该序列化程序扩展了serializers.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

这对我 有用。或者在某些情况下,如果我们想在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 ')

**

希望这可以帮助某人

**


0

化学程序员此评论中所述,在最新的DRF中,您可以像这样进行操作:

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文档来源

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.