django rest framework abstract class serializer

后端 未结 4 976
名媛妹妹
名媛妹妹 2021-02-07 05:01

I have some models like these:

class TypeBase(models.Model):
    name = models.CharField(max_length=20)
    class Meta:
        abstract=True

class PersonType(T         


        
相关标签:
4条回答
  • 2021-02-07 05:21

    Just iterating a bit over @adki's answer:

    1. it is possible to skip model for TypeBaseSerializer;
    2. derived serializers can refer to TypeBaseSerializer.Meta, so you would change them in a single place.
    class TypeBaseSerializer(serializers.Serializer):
        class Meta:
            fields = ('id', 'name', 'created')
            abstract = True
    
        def func(...):
        # ... some logic
    
    class PersonTypeSerializer(TypeBaseSerializer):
        class Meta:
            model = PersonType
            fields = TypeBaseSerializer.Meta.fields + ('age', 'date_of_birth')
    
    class CompanyTypeSerializer(TypeBaseSerializer):
        class Meta:
            model = CompanyType
            fields = TypeBaseSerializer.Meta.fields
    
    0 讨论(0)
  • 2021-02-07 05:30

    As already mentioned in Sebastian Wozny's answer, you can't use a ModelSerializer with an abstract base model.

    Also, there is nothing such as an abstract Serializer, as some other answers have suggested. So setting abstract = True on the Meta class of a serializer will not work.

    However you need not use use a ModelSerializer as your base/parent serializer. You can use a Serializer and then take advantage of Django's multiple inheritance. Here is how it works:

    class TypeBaseSerializer(serializers.Serializer):
        # Need to re-declare fields since this is not a ModelSerializer
        name = serializers.CharField()
        id = serializers.CharField()
    
        class Meta:
            fields = ['id', 'name']
    
        def someFunction(self):
            #... will be available on child classes ...
            pass
    
    class PersonTypeSerializer(TypeBaseSerializer, serializers.ModelSerializer):
    
        class Meta:
            model = PersonType
            fields = TypeBaseSerializer.Meta.fields + ['another_field']
    
    
    class CompanyTypeSerializer(TypeBaseSerializer, serializers.ModelSerializer):
    
        class Meta:
            model = CompanyType
            fields = TypeBaseSerializer.Meta.fields + ['some_other_field']
    

    So now since the fields name and id are declared on the parent class (TypeBaseSerializer), they will be available on PersonTypeSerializer and since this is a child class of ModelSerializer those fields will be populated from the model instance.

    You can also use SerializerMethodField on the TypeBaseSerializer, even though it is not a ModelSerializer.

    class TypeBaseSerializer(serializers.Serializer):
        # you will have to re-declare fields here since this is not a ModelSerializer
        name = serializers.CharField()
        id = serializers.CharField()
        other_field = serializers.SerializerMethodField()
    
        class Meta:
            fields = ['id', 'name', 'other_field']
    
        def get_other_field(self, instance):
            # will be available on child classes, which are children of ModelSerializers
            return instance.other_field
    
    0 讨论(0)
  • 2021-02-07 05:39

    You can't use a ModelSerializer with an abstract base model. From restframework.serializers:

    if model_meta.is_abstract_model(self.Meta.model):
            raise ValueError(
                'Cannot use ModelSerializer with Abstract Models.'
            )
    

    I wrote a serializer_factory function for a similar problem:

    from collections import OrderedDict
    from restframework.serializers import ModelSerializer
    def serializer_factory(mdl, fields=None, **kwargss):
    """ Generalized serializer factory to increase DRYness of code.
    
    :param mdl: The model class that should be instanciated
    :param fields: the fields that should be exclusively present on the serializer
    :param kwargss: optional additional field specifications
    :return: An awesome serializer
    """
    
        def _get_declared_fields(attrs):
            fields = [(field_name, attrs.pop(field_name))
                      for field_name, obj in list(attrs.items())
                      if isinstance(obj, Field)]
            fields.sort(key=lambda x: x[1]._creation_counter)
            return OrderedDict(fields)
    
        # Create an object that will look like a base serializer
        class Base(object):
            pass
    
        Base._declared_fields = _get_declared_fields(kwargss)
    
        class MySerializer(Base, ModelSerializer):
            class Meta:
                model = mdl
    
            if fields:
                setattr(Meta, "fields", fields)
    
        return MySerializer
    

    You can then use the factory to produce serializers as needed:

    def typebase_serializer_factory(mdl):
        myserializer = serializer_factory(
            mdl,fields=["id","name"],
            #owner=HiddenField(default=CurrentUserDefault()),#Optional additional configuration for subclasses 
          )
        return myserializer
    

    Now instanciate different subclass serializers:

    persontypeserializer = typebase_serializer_factory(PersonType)
    companytypeserializer = typebase_serializer_factory(CompanyType)
    
    0 讨论(0)
  • 2021-02-07 05:43

    I think the following approach is more cleaner. You can set "abstract" field to true for the base serializer and add your common logic for all child serializers.

    class TypeBaseSerializer(serializers.ModelSerializer):
        class Meta:
            model = TypeBase
            fields = ('id', 'name')
            abstract = True
    
        def func(...):
        # ... some logic
    

    And then create child serializers and use them for data manipulation.

    class PersonTypeSerializer(TypeBaseSerializer):
        class Meta:
            model = PersonType
            fields = ('id', 'name')
    
    
    class CompanyTypeSerializer(TypeBaseSerializer):
        class Meta:
            model = CompanyType
            fields = ('id', 'name')
    

    Now you can use the both of these serializers normally for every model.

    But if you really want to have one serializers for both the models, then create a container model and a serializer for him too. That is much cleaner :)

    0 讨论(0)
提交回复
热议问题