Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Serializing a list of multiple, chained models

Given two different models, with the same parent base class. Is there any way, using either Django Rest Framework Serializers or serpy, to serialize a chained list containing instances of both the child models?

Given some example models:

class BaseModel(models.Model):
   created_at = models.DateField()

   class Meta:
       abstract = True


class ChildModel1(BaseModel):
    field_one = models.TextField()


class ChildModel2(BaseModel):
    field_two = models.TextField()

And an example view:

def get(self, request):

    q1 = ChildModel1.objects.all()
    q2 = ChildModel2.objects.all()

    chained_list = sorted(
        chain(q1, q2),
        key=attrgetter('created_at'))

    serializer = BaseModelSerializer(chained_list, many=True)

The method for chaining the models is taken from the answer to this question.

With my current attempts I get a quite obvious error saying something like:

AttributeError: 'ChildModel1' object has no attribute 'field_two'

I know it is not the best practice to mix two models with some different fields, but in my case I thought it necessary.

Some examples of serializers I have tested:

First example:

class BaseModelSerializer(serializers.ModelSerializer):

   class Meta:
       model = BaseModel

Second example:

class BaseModelSerializer(serpy.Serializer):
   created_at = serpy.StrField()

   field_one = serpy.StrField(required=False)
   field_two = serpy.StrField(required=False)
like image 768
Valdemar Edvard Sandal Rolfsen Avatar asked Jan 19 '17 14:01

Valdemar Edvard Sandal Rolfsen


1 Answers

You can define serializer that will combine two or more serializers together based on model:

class Model1Serializer(serializers.Serializer):
    ...

class Model2Serializer(serializers.Serializer):
    ...

class SummarySerializer(serializers.Serializer):
    """ Serializer that renders each instance with its own specific serializer """

    @classmethod
    def get_serializer(cls, model):
        if model == Model1:
            return Model1Serializer
        elif model == Model2:
            return Model2Serializer

    def to_representation(self, instance):
        serializer = self.get_serializer(instance.__class__)
        return serializer(instance, context=self.context).data

This will work for any models, not only for childs of one class.

like image 83
zymud Avatar answered Sep 22 '22 16:09

zymud