How to change a queryset in Django?

64 views Asked by At

I try to change output of Django serializer.

Class-based view has function get_projects that returns queryset of Contract instances.

projects = Project.objects.all()
contracts = Contract.objects.filter(project__in=projects)

Relationship of Contract and Project is one-to-many:

class Contract(models.Model):
    project = models.ForeignKey(Project)

Now I have such MySerializer:

class MySerializer(serializers.Serializer):
    project_guid = serializers.UUIDField(source='guid')
    project_name = serializers.CharField(source='name')
    contract_guid = serializers.UUIDField(source='guid')
    contract_number = serializers.CharField(source='number')

MySerializer's response is:

[
  {
    "projectGuid": "project_guid_1",
    "projectName": "project_name_1",
    "contractGuid": "contract_guid_1",
    "contractNumber": "contract_number_1"
  },
  {
    "projectGuid": "project_guid_1",
    "projectName": "project_name_1",
    "contractGuid": "contract_guid_2",
    "contractNumber": "contract_number_2"
  },
  {
    "projectGuid": "project_guid_2",
    "projectName": "project_name_2",
    "contractGuid": "contract_guid_4",
    "contractNumber": "contract_number_4"
  },
  {
    "projectGuid": "project_guid_2",
    "projectName": "project_name_2",
    "contractGuid": "contract_guid_5",
    "contractNumber": "contract_number_5"
  },
]

I would like to change serializer output - to group contracts for exact projects like this:

[
 {
  "project_guid": "project_guid_1",
  "project_name": "project_name_1",
  "contracts": [
    {
      "contract_guid": "contract_guid_1",
      "contract_number": "contract_number_1",
    },
    {
      "contract_guid": "contract_guid_2",
      "contract_number": "contract_number_2",
    },
    ....
  ]
 },
 {
  "project_guid": "project_guid_2",
  "project_name": "project_name_2",
  "contracts": [
    {
      "contract_guid": "contract_guid_4",
      "contract_number": "contract_number_4",
    },
    {
      "contract_guid": "contract_guid_5",
      "contract_number": "contract_number_5",
    },
    ....
  ]
 },
]

How can I do this?

2

There are 2 answers

0
S.Shevchenko On BEST ANSWER

The problem was with the queryset. I refactored qs in get_projects:

projects = Project.objects.filter(active=True).prefetch_related(
        Prefetch('contract_set', queryset=Contract.objects.filter(**filters), to_attr='contracts')
    )

And refactored MySerializer like this:

class ContractSerializer(serializers.Serializer):
    contract_guid = serializers.UUIDField(source='guid')
    contract_number = serializers.CharField(source='number')

class MySerializer(serializers.Serializer):
    project_guid = serializers.UUIDField(source='guid')
    project_name = serializers.CharField(source='name')
    contracts = ContractSerializer(many=True)

Thanks to SerhiiL for hint with prefetch_related.

0
SerhiiL On

For this you can try this

class ContactSerializer(serializers.ModelSerializer):
    class Meta:
       model = Contract
       fields = ["contract_guid", "contract_number"]

class ProjectSerializer(serializers.ModelSerializer):
    contracts = ContactSerializer(many=True)
    class Meta:
        model = Project 
        fields = ["project_guid", "project_name", "contracts"]

And don't forget use prefetch related for optimization your query.