【问题标题】:Reuse existing Django rest framework serializer重用现有的 Django REST 框架序列化程序
【发布时间】:2020-05-07 15:55:24
【问题描述】:

我目前正在使用 Django Rest Framework,并且正在寻找一种方法来重用已定义的序列化器中的某些属性。为了说明起见,我将公开一些涉及的序列化器:

完成的序列化器:

class ProductSerializer(serializers.ModelSerializer):
    subscribed = serializers.SerializerMethodField()
    other_field = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = [
            'id',
            'name',
            'subscribed',
            'other_field',
            'x',
            'y',
            'z'
        ]

    def get_subscribed(self, product: Product):
        return product.is_user_subscribed(self.context['request'].user)

简化的序列化器:

class ProductSimplifiedSerializer(serializers.ModelSerializer):
    subscribed = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = [
            'id',
            'name',
            'subscribed'
        ]

    def get_subscribed(self, product: Product):
        return product.is_user_subscribed(self.context['request'].user)

正如您在上面看到的,这些序列化程序几乎相同,但其中一个是对象的简化版本,因为我不想在某些部分检索不必要的信息。这里的问题是,在这种情况下,我们有一个需要维护两次的方法序列化程序。也许将来,我想添加另一个字段,我需要将它添加到两者中。那么,如何实现一个包含所有字段但我可以重用它并从中提取特定字段的基于序列化程序?

我已经想到了这些选项: 1. 使用 Base Serializer 实例: 草稿:

def to_representation(self, instance)
    desired_format = self.context['format']
    if desired_format == 'simplified':
        fields = ['fields_for_simplified']
    elif desired_format == 'regular':
        fields = ['fields_for_regular']

    for field in fields:
        # make the representation

使用这种方法,我不知道它是一个好主意还是可能

  1. 使用 SerializerField 草稿:
class UserSubscribed(serializer.SerializerField)
    def to_representation(self, instance):
        return 'representation'

class ProductSimplifiedSerializer(serializers.ModelSerializer):
    user_subscribed = UserSubscribed()

    class Meta:
        model = Product
        fields = [
            'id',
            'name',
            'user_subscribed'
        ]

我认为最后一个更好,问题是这个 _user_subscribed_ 不是 Product 实例的属性,因此它失败了,我不知道如何实现。

您对这种情况有何建议?任何建议将不胜感激。

谢谢!

【问题讨论】:

    标签: django rest django-rest-framework serialization


    【解决方案1】:

    您可以将简单的子类化并在完整的子类中添加字段。

    class ProductSimplifiedSerializer(serializers.ModelSerializer):
        subscribed = serializers.SerializerMethodField()
    
        class Meta:
            model = Product
            fields = [
                'id',
                'name',
                'subscribed'
            ]
    
        def get_subscribed(self, product: Product):
            return product.is_user_subscribed(self.context['request'].user)
    
    
    class ProductSerializer(ProductSimplifiedSerializer):
        other_field = serializers.SerializerMethodField()
    
        class Meta(ProductSimplifiedSerializer.Meta):
            fields = ProductSimplifiedSerializer.Meta.fields + [
                'other_field',
                'x',
                'y',
                'z'
            ]
    

    这里的诀窍是你也可以继承Meta 类。您在 Meta 子类中定义的任何属性都会覆盖父属性,因此您必须手动使用父属性。

    【讨论】:

      【解决方案2】:

      您可以为序列化程序类创建 mixin,它可以让您指定将在序列化程序中“使用”哪些字段,其他任何字段都将被忽略。

      class DynamicFieldsMixin(object):
          def __init__(self, *args, **kwargs):
              # Don't pass the 'fields' arg up to the superclass
              fields = kwargs.pop('fields', None)
      
              # Instantiate the superclass normally
              super().__init__(*args, **kwargs)
      
              if fields is not None:
                  # Drop any fields that are not specified in the `fields` argument.
                  allowed = set(fields)
                  existing = set(self.fields)
                  for field_name in existing - allowed:
                      self.fields.pop(field_name)
      

      然后你可以像这样使用你的序列化器:

      class ProductSerializer(DynamicFieldsMixin, serializers.ModelSerializer)
      
      serializer = ProductSerializer(fields=("id", "name", "subscribed"))
      

      【讨论】: