2

I'm using Django 2.2

I have three models

class LeadList(SafeDeleteModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=128, verbose_name='Lead List Name')

    def entries_count(self):
        return LeadListEntry.objects.filter(
            dynamic_url_object__lead_list=self
        ).count()
class DynamicUrlObject(SafeDeleteModel):
    lead_list = models.ForeignKey(
        LeadList,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        default=None,
        related_name='dynamic_url_object'
    )
class LeadListEntry(models.Model):
    dynamic_url_object = models.ForeignKey(
        'otherapp.DynamicUrlObject',
        on_delete=models.CASCADE,
        related_name='lead_list_entry'
    )
    data = models.TextField(help_text='Lead List entry data. (JSON data)', blank=False)

LeadList has reference in the DynamicUrlObject. LeadList can be linked to multiple DynamicUrlObject instances.

The LeadListEntry is linked to DynamicUrlObject to record data for each DynamicUrlObject instance.

I want to get the count of LeadListEntry for particular LeadList.

For that I have a model method in the LeadList model to return the count of the LeadListEntry entries.

But on getting the list of LeadList, it is producing N+1 problem where entries_count is called for each lead_list object which in turn runs a query on LeadListEntry to get the count.

I tried to use prefetch_related in the view like

def get_queryset(self):
    return self.serializer_class.Meta.model.objects.filter(
        user=self.request.user
    ).prefetch_related(
        'dynamic_url_object__lead_list_entry'
    )

But there is no benefit of it.

How can I get the count for each lead_list object without running into N+1 problem?

Anuj TBE
  • 9,198
  • 27
  • 136
  • 285

1 Answers1

1

You can do

ll = LeadList.objects.first()  # some lead list object

le_count = LeadListEntry.objects.filter(dynamic_url_object__lead_list=ll).count()

EDIT

You can use annotation like this.

from django.db.models import Count


ll = LeadList.objects.annotate(le_count=Count('dynamicurlobject__leadlistentry'))

Every object in ll queryset will have an attribute le_count containing total number of leadlistentries for that particular object.

Nafees Anwar
  • 6,324
  • 2
  • 23
  • 42
  • I'm doing the same thing in the model method (mentioned in the question). This works fine for single object but with increase in number of `LeadList` records, the model method will be called for each object producing `n+1` problem. – Anuj TBE Feb 25 '20 at 11:58