Search code examples
djangoormdjango-queryset

Move django model field from nested models to the 'main' model


I have 2 django models linked like this:

class ModelA(models.Model):
    id = models.UUIDField(primary_key=True)
    # some other fields here


class ModelB(models.Model):
    modela_id = models.UUIDField(primary_key=True)
    unique_field = models.FloatField()
    order = models.IntegerField(primary_key=True)
    random_field = models.FloatField()

and serializers for these 2 models:

class ModelASerializer(serializers.Serializer):
    id = serializers.CharField(help_text="Measurement ID")
    # some other fields here
    b_objects = ModelBSerializer(many=True)

classModelBSerializer(serializers.Serializer):
    id = serializers.CharField(source="order")
    random_field = serializers.FloatField()
    unique_field = serializers.FloatField()

I want to write an endpoint that will return the list of ModelA entities and all the related ModelB objects to the particular ModelA object.

I have this currently, it works just fine: ModelA.objects.prefetch_related("b_objects").all()

which returns a list of objects like this one:

    {
            "id": "92eb8314-3f26-4bf6-8cc0-83d2935434d9",
            ### modelA fields here
            "b_objects": [
                {
                    "id": "1",
                    "random_field": 1.0,
                    "unique_field": 0.1
                },
                {
                    "id": "2",
                    "random_field": 5.0,
                    "unique_field": 0.1
                }
            ]
     }

What I want here is to move "unique_field" from inner level to the level of modelA so that it would return:


    {
            "id": "92eb8314-3f26-4bf6-8cc0-83d2935434d9",
            "unique_field": 0.1
            ### modelA fields here
            "b_objects": [
                {
                    "id": "1",
                    "random_field": 1.0
                },
                {
                    "id": "2",
                    "random_field": 5.0
                }
            ]
     }

It's 100% that all values of unique_field will be equal in all the b_objects within particular modelA objects.

I tried to add .annotate(unique_value="b_objects") and other inside of annotate, but it raises an error:

QuerySet.annotate() received non-expression(s): b_objects.

Is there any way of achieving this using queryset API?


Solution

  • Try adding unique_field = serializers.FloatField() to ModelASerializer. You have to add

        def to_representation(self, instance):
            data = super().to_representation(instance)
            unique_field_values = [b_obj['unique_field'] for b_obj in data['b_objects']]
            if unique_field_values:
                data['unique_field'] = unique_field_values[0]
                for b_obj in data['b_objects']:
                    b_obj.pop('unique_field', None)
            return data
    

    and that will take the unique_field from ModelB and add it into outer returning object.