【问题标题】:Django REST Framework: adding additional field to ModelSerializerDjango REST Framework:向 ModelSerializer 添加附加字段
【发布时间】:2013-08-26 03:13:18
【问题描述】:

我想序列化一个模型,但想包含一个额外的字段,该字段需要对要序列化的模型实例进行一些数据库查找:

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

这样做的正确方法是什么?我看到序列化程序的you can pass in extra "context",是在上下文字典中传递附加字段的正确答案吗?

使用这种方法,获取我需要的字段的逻辑将不会与序列化程序定义自包含,这是理想的,因为每个序列化实例都需要my_field。在 DRF 序列化程序文档的其他地方,says“额外字段可以对应于模型上的任何属性或可调用”。我说的是“额外字段”吗?

我是否应该在Foo 的模型定义中定义一个返回my_field 值的函数,并在序列化程序中将my_field 连接到该可调用对象?那是什么样子的?

如有必要,很高兴澄清问题。

【问题讨论】:

    标签: django rest django-rest-framework


    【解决方案1】:

    我认为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

    【讨论】:

    • 是否可以向这些字段添加验证?我的问题是:如何接受可以在 post_save() 处理程序中验证和处理的自定义 POST 值?
    • 请注意,SerializerMethodField 是只读的,因此这不适用于传入的 POST/PUT/PATCH。
    • 在 DRF 3 中,改为field_name = serializers.SerializerMethodField()def get_field_name(self, obj):
    • 当定义一个 SerializerMethodField 时,foo 是什么?使用 CreateAPIView 时,foo 是否已经存储然后可以使用 is_named_bar() 方法?
    • "foo" 这里应该是 "instance",因为它是当前被序列化程序“看到”的实例。
    【解决方案2】:

    您可以通过这种方法将模型方法更改为属性并在序列化程序中使用它。

    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 装饰器的意义何在?
    • 这意味着您可以像调用属性一样调用此方法:即variable = model_instance.my_field 在没有装饰器的情况下与variable = model_instance.my_field() 给出相同的结果。还有:stackoverflow.com/a/6618176/2198571
    • 这不起作用,至少在 Django 1.5.1 / djangorestframework==2.3.10 中。即使在“字段”元属性中明确引用,ModelSerializer 也没有获得属性。
    • 您需要将字段添加到序列化程序,因为它不是真实模型字段:my_field = serializers.Field(source='my_field')
    • source='my_field' 不再需要并引发异常
    【解决方案3】:

    使用最新版本的 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',)
    

    【讨论】:

    • 如果我想在 def foo(self) 中有request 对象,它可以修改 foo 的值怎么办? (例如基于 request.user 的查找)
    • 如果 foo 的值来自请求怎么办?
    【解决方案4】:

    如果你想在你的额外字段上读写,你可以使用一个新的自定义序列化器,它扩展了 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']
    

    我在具有一些自定义逻辑的相关嵌套字段中使用它

    【讨论】:

      【解决方案5】:

      我对类似问题 (here) 的回复可能会有用。

      如果您有以下方式定义的模型方法:

      class MyModel(models.Model):
          ...
      
          def model_method(self):
              return "some_calculated_result"
      

      您可以像这样将调用所述方法的结果添加到您的序列化程序中:

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

      附言由于自定义字段实际上并不是模型中的字段,因此您通常希望将其设为只读,如下所示:

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

      【讨论】:

        【解决方案6】:

        这对我有用。 如果我们只想在ModelSerializer 中添加一个额外的字段,我们可以 像下面那样做,并且该字段也可以在之后分配一些 val 一些查找的计算。或者在某些情况下,如果我们想发送 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 ')
        

        希望这可以帮助某人。

        【讨论】:

        • @classmethod 不需要
        • 那么你能简单地在你的序列化器中执行查询吗?
        【解决方案7】:
        class Demo(models.Model):
            ...
            @property
            def property_name(self):
                ...
        

        如果你想使用相同的属性名:

        class DemoSerializer(serializers.ModelSerializer):
            property_name = serializers.ReadOnlyField()
            class Meta:
                model = Product
                fields = '__all__' # or you can choose your own fields
        

        如果您想使用不同的属性名称,只需更改:

        new_property_name = serializers.ReadOnlyField(source='property_name')
        

        【讨论】:

          【解决方案8】:

          如果您想为每个对象动态添加字段,您可以使用 to_represention。

          class FooSerializer(serializers.ModelSerializer):
            class Meta:
                  model = Foo
                  fields = ('id', 'name',)
            
            def to_representation(self, instance):
                representation = super().to_representation(instance)
                if instance.name!='': #condition
                   representation['email']=instance.name+"@xyz.com"#adding key and value
                   representation['currency']=instance.task.profile.currency #adding key and value some other relation field
                   return representation
                return representation
          

          通过这种方式,您可以为每个 obj 动态添加键和值 希望你喜欢

          【讨论】:

            【解决方案9】:

            正如Chemical Programerthis comment 中所说,在最新的 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 docs source

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-06-27
              • 1970-01-01
              • 2013-01-13
              • 1970-01-01
              • 1970-01-01
              • 2020-11-26
              • 2017-03-21
              相关资源
              最近更新 更多