Nested annotation fields in Django REST Framework serializer


Robin

I'm trying to view nested annotated (aggregated/calculated) fields in a Django REST Framework serializer. This allows for cleaner handling of annotated fields. This post is similar to Aggregate (and other annotated) fields in Django Rest Framework serializers , but I would like to use a similar technique for nesting. Below that method you can see how no nesting works and how nesting doesn't seem to work.

I know this can be achieved manually (using Django View) or using methods that overload the database I'm not interested in. However, maybe there is a performant solution to this problem.

The following works (not nested)

role model

class IceCreamCompany(models.Model):
    name = models.CharField(max_length=255)


class IceCreamTruck(models.Model):
    company = models.ForeignKey('IceCreamCompany', related_name='trucks')
    capacity = models.IntegerField()


class IceCreamTruckDriver(models.Model):
    name = models.CharField(max_length=255)
    first_name = models.CharField(max_length=255)
    truck = models.ForeignKey('IceCreamTruck', related_name='drivers')

serializer

class IceCreamTruckDriverSerializer(serializers.ModelSerializer):

    class Meta:
        model = IceCreamTruckDriver
        fields = ('name', 'first_name')


class IceCreamTruckSerializer(serializers.ModelSerializer):
    drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)

    class Meta:
        model = IceCreamTruck
        fields = ('capacity', 'drivers')


class IceCreamCompanySerializer(serializers.ModelSerializer):
    trucks = IceCreamTruckSerializer(many=True, read_only=True)
    amount_of_trucks = serializers.IntegerField()

    class Meta:
        model = IceCreamCompany
        fields = ('name', 'trucks', 'amount_of_trucks')

Viewset

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
                           .annotate(amount_of_trucks=Count('trucks'))\
                           .all()

    serializer_class = IceCreamCompanySerializer

result

"results": [
        {
            "name": "Pete Ice Cream",
            "trucks": [
                {
                    "capacity": 35,
                    "drivers": [
                        {
                            "name": "Damian",
                            "first_name": "Ashley"
                        },
                        {
                            "name": "Wilfrid",
                            "first_name": "Lesley"
                        }
                    ]
                },
                {
                    "capacity": 30,
                    "drivers": [
                        {
                            "name": "Stevens",
                            "first_name": "Joseph"
                        }
                    ]
                },
                {
                    "capacity": 30,
                    "drivers": []
                }
            ],
            "amount_of_trucks": 3
        }
    ]

The following is invalid (nested)

same model

serializer

class IceCreamTruckDriverSerializer(serializers.ModelSerializer):

    class Meta:
        model = IceCreamTruckDriver
        fields = ('name', 'first_name')


class IceCreamTruckSerializer(serializers.ModelSerializer):
    drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)
    amount_of_drivers = serializers.IntegerField()

    class Meta:
        model = IceCreamTruck
        fields = ('capacity', 'drivers', 'amount_of_drivers')


class IceCreamCompanySerializer(serializers.ModelSerializer):
    trucks = IceCreamTruckSerializer(many=True, read_only=True)

    class Meta:
        model = IceCreamCompany
        fields = ('name', 'trucks')

Viewset

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
                           .annotate(trucks__amount_of_drivers=Count('trucks__drivers'))\
                           .all()

    serializer_class = IceCreamCompanySerializer

result

AttributeError at /ice/
Got AttributeError when attempting to get a value for field `amount_of_drivers` on serializer `IceCreamTruckSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `IceCreamTruck` instance.
Original exception text was: 'IceCreamTruck' object has no attribute 'amount_of_drivers'.
Robin

I got the answer using the Django REST Google group with read_only=True inside the IntegerField, which helped to get rid of the error, but this field doesn't show up anymore. Maybe my annotation is wrong. Anyway, I ended up using custom views in Django as I ended up needing more data. However, you can get data in other ways:

A very elegant solution is to remove the annotation function and use SerializerMethodField which gives me the result.

But: this does raise a lot of questions!

same model

serializer

class IceCreamTruckDriverSerializer(serializers.ModelSerializer):

    class Meta:
        model = IceCreamTruckDriver
        fields = ('name', 'first_name')


class IceCreamTruckSerializer(serializers.ModelSerializer):
    drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)
    amount_of_drivers = serializers.SerializerMethodField()

    def get_amount_of_drivers(self, obj):
        return obj.drivers.count()

    class Meta:
        model = IceCreamTruck
        fields = ('capacity', 'drivers', 'amount_of_drivers')


class IceCreamCompanySerializer(serializers.ModelSerializer):
    trucks = IceCreamTruckSerializer(many=True, read_only=True)

    class Meta:
        model = IceCreamCompany
        fields = ('name', 'trucks')

Viewset

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers').all()

    serializer_class = IceCreamCompanySerializer

result

"results": [
        {
            "name": "Pete Ice Cream",
            "trucks": [
                {
                    "capacity": 35,
                    "drivers": [
                        {
                            "name": "Damian",
                            "first_name": "Ashley"
                        },
                        {
                            "name": "Wilfrid",
                            "first_name": "Lesley"
                        }
                    ],
                    "amount_of_drivers": 2
                },
                {
                    "capacity": 30,
                    "drivers": [
                        {
                            "name": "Stevens",
                            "first_name": "Joseph"
                        }
                    ],
                    "amount_of_drivers": 1
                },
                {
                    "capacity": 30,
                    "drivers": [],
                    "amount_of_drivers": 0
                }
            ]
        }
    ]

It's also possible to use the following functionality inside the model: Django Rest Framework Ordering on SerializerMethodField (visible in the code itself), but I didn't select it so I didn't have to modify the model too much. This also generates too many queries.

Related


Nested annotation fields in Django REST Framework serializer

Robin I'm trying to view nested annotated (aggregated/calculated) fields in a Django REST Framework serializer. This allows for cleaner handling of annotated fields. This post is similar to Aggregate (and other annotated) fields in Django Rest Framework serial

Nested annotation fields in Django REST Framework serializer

Robin I'm trying to view nested annotated (aggregated/calculated) fields in a Django REST Framework serializer. This allows for cleaner handling of annotated fields. This post is similar to Aggregate (and other annotated) fields in Django Rest Framework serial

Nested annotation fields in Django REST Framework serializer

Robin I'm trying to view nested annotated (aggregated/calculated) fields in a Django REST Framework serializer. This allows for cleaner handling of annotated fields. This post is similar to Aggregate (and other annotated) fields in Django Rest Framework serial

Nested serializer fields Django Rest Framework

Isaac Hattilima I have two models (user and province) and serializer. The user creates a province and in the province model I have the user id. The challenge is when creating the nested serializer, I only want the user's first_name and last_name, but it gives

Exclude fields from Django Rest Framework serializer

Minuddin Ahmed Rana: In the serializer below, I have a nested serializer[ ContainerSerializer] field and I want to exclude a field from (container), ContainerSerializerbut I don't want to change anything ContainerSerializer. How can I do this? class BLcontaine

Additional Serializer Fields in Django REST Framework 3

Enmagko condition I am creating a simple endpoint that allows to create users. I need a field (ie confirm_password) that is not in my User model. I would run a validation that compares this field to another field in the model, and then never use the other fiel

Merge two fields in Django Rest Framework serializer

Rafael Gil I am using Django Rest Framework authentication system with default user table. In that table it splits first name and last name in two different char fields. Is it possible to join these two fields in the serializer? like this: class UserSerializer

Django rest framework only creates serializer fields

Neils I have a Django model that acts as a request description. It was created to be requested by a REST client, to record the current state of the task, and to record historical requests received by the client. The model has fields that can be used to fine-tu

How to customize fields in Django Rest Framework serializer

Sun Yun I want to create a new record in the database, I set up the model like this: class FooModel(models.Model) subject_key= models.CharField(max_length=2) subject= modeels.CharField(max_length=100) What I do is: client should provide field "subject

Django Rest Framework serializer - return related fields

Nick Dart I have a model that has a one-to-one relationship with the main model: class User(models.Model): id = models.BigIntegerField(primary_key=True) username = models.CharField(max_length=100, blank=True) class AggregatedStats(models.Model): u

Additional Serializer Fields in Django REST Framework 3

Enmagko condition I am creating a simple endpoint that allows to create users. I need a field (ie confirm_password) that is not in my User model. I would run a validation that compares this field with another field in the model, and then never use the other fi

Django Rest Framework - filter serializer fields

Zheng Haiyun I have a question about Django REST complete framework. After the products are rendered to the remote client, each product will archive a file containing the filtered data. For example, the model might look like this. class Product(models.Model):

Exclude fields from Django Rest Framework serializer

Minuddin Ahmed Rana: In the serializer below, I have a nested serializer[ ContainerSerializer] field and I want to exclude a field from (container), ContainerSerializerbut I don't want to change anything ContainerSerializer. How can I do this? class BLcontaine

Django Rest Framework serializer and fields in related tables

Nalt I have the following models: class Workflow(models.Model): name = models.CharField(max_length=200) class Task(models.Model): name = models.CharField(max_length=200) class TaskParameter(models.Model): default_value = models.CharField(max_le

Additional Serializer Fields in Django REST Framework 3

Enmagko condition I am creating a simple endpoint that allows to create users. I need a field (ie confirm_password) that is not in my User model. I would run a validation that compares this field to another field in the model, and then never use the other fiel

Merge two fields in Django Rest Framework serializer

Rafael Gil I am using Django Rest Framework authentication system with default user table. In that table it splits first name and last name in two different char fields. Is it possible to join these two fields in the serializer? like this: class UserSerializer

Merge two fields in Django Rest Framework serializer

Rafael Gil I am using Django Rest Framework authentication system with default user table. In that table it splits first name and last name in two different char fields. Is it possible to join these two fields in the serializer? like this: class UserSerializer

Django rest framework only creates serializer fields

Neils I have a Django model that acts as a request description. It was created to be requested by a REST client, to record the current state of the task, and to record historical requests received by the client. The model has fields that can be used to fine-tu

Django rest framework only creates serializer fields

Neils I have a Django model that acts as a request description. It was created to be requested by a REST client, to record the current state of the task, and to record historical requests received by the client. The model has fields that can be used to fine-tu

How to customize fields in Django Rest Framework serializer

Sun Yun I want to create a new record in the database, I set up the model like this: class FooModel(models.Model) subject_key= models.CharField(max_length=2) subject= modeels.CharField(max_length=100) What I do is: client should provide field "subject

How to customize fields in Django Rest Framework serializer

Sun Yun I want to create a new record in the database, I set up the model like this: class FooModel(models.Model) subject_key= models.CharField(max_length=2) subject= modeels.CharField(max_length=100) What I do is: client should provide field "subject

Django Rest Framework - only deserialized serializer fields

b_pcakes I'm writing a simple API to encrypt/decrypt ciphertext using Django Rest Framework, and I'm wondering if it's possible to define a demon in the serializer that is only used for deserialization (i.e. only for validation). For example, I have a model cl

Exclude fields from Django Rest Framework serializer

Minuddin Ahmed Rana In the serializer below, I have a nested serializer[ ContainerSerializer] field, and I want to exclude a field from (container), ContainerSerializerbut I don't want to change anything ContainerSerializer. How can I do this? class BLcontaine

Exclude fields from Django Rest Framework serializer

Minuddin Ahmed Rana In the serializer below, I have a nested serializer[ ContainerSerializer] field, and I want to exclude a field from (container), ContainerSerializerbut I don't want to change anything ContainerSerializer. How can I do this? class BLcontaine

Exclude declared serializer fields in Django Rest Framework

Tim Evan In my project, I just upgraded Django Rest Framework to version 3.6. Previously, I could exclude fields declared on the serializer, now it prevents me from doing so. class MyModelSerializer(Serializer): expensive_field = SerializerMethodField()

Django Rest Framework serializer - return related fields

Nick Dart I have a model that has a one-to-one relationship with the main model: class User(models.Model): id = models.BigIntegerField(primary_key=True) username = models.CharField(max_length=100, blank=True) class AggregatedStats(models.Model): u

Additional Serializer Fields in Django REST Framework 3

Enmagko condition I am creating a simple endpoint that allows to create users. I need a field (ie confirm_password) that is not in my User model. I would run a validation that compares this field with another field in the model, and then never use the other fi

Exclude fields from Django Rest Framework serializer

Minuddin Ahmed Rana: In the serializer below, I have a nested serializer[ ContainerSerializer] field, and I want to exclude a field from (container), ContainerSerializerbut I don't want to change anything ContainerSerializer. How can I do this? class BLcontain

Additional Serializer Fields in Django REST Framework 3

Enmagko Condition I am creating a simple endpoint that allows to create users. I need a field (ie confirm_password) that is not in my User model. I would run a validation that compares this field to another field in the model, and then never use the other fiel