drf-extensions icon indicating copy to clipboard operation
drf-extensions copied to clipboard

AbstractModelSerializer

Open cancan101 opened this issue 9 years ago • 5 comments

DRF will not auto generate a Serializer for an Abstract class. What do you think about adding a AbstractModelSerializer? This is my first pass of an implementation:

class AbstractModelSerializer(serializers.ModelSerializer):
    def get_fields(self):
        """
        Return the dict of field names -> field instances that should be
        used for `self.fields` when instantiating the serializer.
        """
        assert hasattr(self, 'Meta'), (
            'Class {serializer_class} missing "Meta" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )
        assert hasattr(self.Meta, 'model'), (
            'Class {serializer_class} missing "Meta.model" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )

        declared_fields = copy.deepcopy(self._declared_fields)
        model = getattr(self.Meta, 'model')
        depth = getattr(self.Meta, 'depth', 0)

        if depth is not None:
            assert depth >= 0, "'depth' may not be negative."
            assert depth <= 10, "'depth' may not be greater than 10."

        # Retrieve metadata about fields & relationships on the model class.
        info = get_field_info(model)
        field_names = self.get_field_names(declared_fields, info)

        # Determine any extra field arguments and hidden fields that
        # should be included
        extra_kwargs = self.get_extra_kwargs()
        extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
            field_names, declared_fields, extra_kwargs
        )

        # Determine the fields that should be included on the serializer.
        fields = OrderedDict()
        for field_name in field_names:
            # If the field is explicitly declared on the class then use that.
            if field_name in declared_fields:
                fields[field_name] = declared_fields[field_name]
                continue

            # Determine the serializer field class and keyword arguments.
            field_class, field_kwargs = self.build_field(
                field_name, info, model, depth
            )

            # Include any kwargs defined in `Meta.extra_kwargs`
            extra_field_kwargs = extra_kwargs.get(field_name, {})
            field_kwargs = self.include_extra_kwargs(
                field_kwargs, extra_field_kwargs
            )

            # Create the serializer field.
            fields[field_name] = field_class(**field_kwargs)

        # Add in any hidden fields.
        fields.update(hidden_fields)

        return fields

    def get_unique_together_validators(self):
        return []

    def get_unique_for_date_validators(self):
        return []

    def get_default_field_names(self, declared_fields, model_info):
        """
        Return the default list of field names that will be used if the
        `Meta.fields` option is not specified.
        """
        return (
            list(declared_fields.keys()) +
            list(model_info.fields.keys()) +
            list(model_info.forward_relations.keys())
        )

def get_field_info(model):
    """
    Given a model class, returns a `FieldInfo` instance, which is a
    `namedtuple`, containing metadata about the various field types on the model
    including information about their relationships.
    """
    opts = model._meta.concrete_model._meta

    pk = None
    fields = _get_fields(opts)
    forward_relations = _get_forward_relationships(opts)
    reverse_relations = _get_reverse_relationships(opts)
    fields_and_pk = fields
    relationships = _merge_relationships(forward_relations, reverse_relations)

    return FieldInfo(pk, fields, forward_relations, reverse_relations,
                     fields_and_pk, relationships)

cancan101 avatar May 12 '15 04:05 cancan101

New maintainer here, what do you think the benefits of serializing abstract models?

auvipy avatar Dec 06 '15 15:12 auvipy

@cancan101 ping

auvipy avatar Mar 30 '16 20:03 auvipy

One case: If I want to map between different representations of data, I find it convenient to declare an abstract model and then use the Serializer to deserialize on one side and serialize on the other.

Another: Base model is abstract and I want to serialize it.

cancan101 avatar Mar 30 '16 20:03 cancan101

+1 Some cases I need to manipulate temporary data but not really go into the database. Use an abstract model to represent these data improve the readability and consistency as a Django app. It will be helpful if I don't need to declare the data structure again in a serializer.

w121211 avatar Apr 08 '16 08:04 w121211

Does this code still work?

tOlorun avatar Aug 02 '18 05:08 tOlorun