How do I collect all my Django data from the database before invoking my serializer?

I'm using Python 3.9 and Django 3.2. I have a Django model with a couple of many-to-many relationsips

class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType, blank=False)
    addresses = models.ManyToManyField(Address, through='CoopAddressTags')
    enabled = models.BooleanField(default=True, null=False)
    phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
    email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
    web_site = models.TextField()
    description = models.TextField(null=True)
    approved = models.BooleanField(default=False, null=True)
    proposed_changes = models.JSONField("Proposed Changes", null=True)
    reject_reason = models.TextField(null=True)

I can search for my model using a manager class that builds a query like so ...

    def find(
        self,
        partial_name,
        types_arr=None,
        enabled=None,
        city=None,
        zip=None,
        street=None,
        state_abbrev=None
    ):
        """
        Lookup coops by varying criteria.
        """
        q = Q()
        if partial_name:
            q &= Q(name__icontains=partial_name)
        if enabled != None:
            q &= Q(enabled=enabled)
        if types_arr != None:
            filter = Q(
                *[('types__name', type) for type in types_arr],
                _connector=Q.OR
            )
            q &= filter
        if street != None:
            q &= Q(addresses__raw__icontains=street)
        if city != None:
            q &= Q(addresses__locality__name__iexact=city)
        if zip != None:
            q &= Q(addresses__locality__postal_code=zip)
        if state_abbrev != None:
            q &= Q(addresses__locality__state__code=state_abbrev)
            q &= Q(addresses__locality__state__country__code="US")

        queryset = Coop.objects.filter(q)
        print(queryset.query)
        return queryset

In my view, I invoke and return the data using

coops = Coop.objects.find(...)
serializer = CoopSearchSerializer(coops, many=True)

in which the serializer looks like

class CoopSearchSerializer(serializers.ModelSerializer):
    ...
    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['coopaddresstags_set'] = CoopAddressTagsSerializer(instance.coopaddresstags_set.all(), many=True).data
        return rep

What I'm noticing is when I search for a result set that contains 6 results, and each one is serialized using the logic above, I get a proportional number of queries run for each result ...

 SELECT "directory_coopaddresstags"."id", "directory_coopaddresstags"."coop_id", "directory_coopaddresstags"."address_id", "directory_coopaddresstags"."is_public" FROM "directory_coopaddresstags" WHERE "directory_coopaddresstags"."coop_id" = 271; args=(271,)
(0.000) SELECT "directory_coopaddresstags"."id", "directory_coopaddresstags"."coop_id", "directory_coopaddresstags"."address_id", "directory_coopaddresstags"."is_public" FROM "directory_coopaddresstags" WHERE "directory_coopaddresstags"."coop_id" = 271; args=(271,)
type of instance: <class 'directory.models.CoopAddressTags'>
(0.000) SELECT "address_address"."id", "address_address"."street_number", "address_address"."route", "address_address"."locality_id", "address_address"."raw", "address_address"."formatted", "address_address"."latitude", "address_address"."longitude" FROM "address_address" WHERE "address_address"."id" = 263 LIMIT 21; args=(263,)
(0.000) SELECT "address_locality"."id", "address_locality"."name", "address_locality"."postal_code", "address_locality"."state_id" FROM "address_locality" WHERE "address_locality"."id" = 16 LIMIT 21; args=(16,)
(0.000) SELECT "address_state"."id", "address_state"."name", "address_state"."code", "address_state"."country_id" FROM "address_state" WHERE "address_state"."id" = 19313 LIMIT 21; args=(19313,)
(0.000) SELECT "address_country"."id", "address_country"."name", "address_country"."code" FROM "address_country" WHERE "address_country"."id" = 484 LIMIT 21; args=(484,)

So if there are 6 results, the above gets run 6 times with different IDs. Is there a way to only have a single set of queries run so taht regardless of whether there are 1, 10, or 100 results, the same number of queries gets run to return the data?



Comments

Popular posts from this blog

Spring Elasticsearch Operations

Network Error and Timeout on Authorize.net JS

Object oriented programming concepts (OOPs)