ManyToMany上的Django .count()变得非常慢

时间:2018-02-02 21:33:25

标签: django django-admin django-queryset

我有一个Django项目,包括我们库存的刮刀,每隔几个小时在服务器上作为cronjob运行,以及Django Admin页面 - 我们用它来查看/访问所有项目。

我们有大约30个被编入索引的项目。 因此,每个“刮擦操作”由大约30个单独的“搜索操作”组成,每个“搜索操作”每次运行可获得大约500个结果。

现在,这个描述有点混乱,所以我已经包含了下面的模型。

class ScrapingOperation(models.Model):
    date_started = models.DateTimeField(default=timezone.now, editable=True)
    date_completed = models.DateTimeField(blank=True, null=True)
    completed = models.BooleanField(default=False)
    round = models.IntegerField(default=-1)
    trusted = models.BooleanField(default=True)


class Search(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    date_started = models.DateTimeField(default=timezone.now, editable=True)
    date_completed = models.DateTimeField(blank=True, null=True)
    completed = models.BooleanField(default=False)
    round = models.IntegerField(default=1)
    scraping_operation = models.ForeignKey(ScrapingOperation, on_delete=models.CASCADE, related_name='searches')
    trusted = models.BooleanField(default=True)

    def total_ads(self):
        return self.ads.count()


class Ad(models.Model): 

  item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='ads')    
  title = models.CharField(max_length=500)
  price = models.DecimalField(max_digits=8, decimal_places=2, null=True)         
  first_seen = models.DateTimeField(default=timezone.now, editable=True)
  last_seen = models.DateTimeField(default=timezone.now, editable=True)

  def __str__(self):
      return self.title

现在我们遇到了这个问题。

在搜索模型和SeachOperation模型的管理页面上,我们希望看到为该特定对象抓取的广告数量(表示为数字)这对我们的四个传感器工作正常,但我们对SearchOperation的实施有遇到问题

这是我们使用的代码:

class ScrapingOperationAdmin(admin.ModelAdmin):
    list_display = ['id', 'completed', 'trusted', 'date_started', 'date_completed', 'number_of_ads']
    list_filter = ('completed', 'trusted')
    view_on_site = False

    inlines = [
        SearchInlineAdmin,
    ]

    def number_of_ads(self, instance):
        total_ads = 0
        for search in instance.searches.all():
            total_ads += search.ads.count()
        return total_ads

我们遇到的问题是:代码工作并提供正确的数字,但是,在+/- 10 ScrapingOperation之后,我们注意到网站在加载页面时开始变慢。我们现在最多有60个ScrapingOperations,当我们点击Django管理员中的ScrapingOperations页面时,加载几乎需要一分钟。

有更有效的方法吗?我们考虑过将广告总数保存到模型本身,但将字段专用于可通过简单的.count()调用访问的信息似乎很浪费。然而,我们的查询显然是如此低效,以至于整个站点在执行时锁定了将近一分钟。有没有人知道我们做错了什么?

根据以下评论,我目前正致力于以下解决方案:

def number_of_ads(self, instance):
    total_ads = 0
    searches = Search.objects.filter(scraping_operation=instance).annotate(Count('ads'))
    for search in searches:
        total_ads += search.ads__count
    return total_ads

1 个答案:

答案 0 :(得分:1)

获取查询集时使用注释

from django.db.models import Count
class ScrapingOperationAdmin(admin.ModelAdmin):
     ...
     def get_queryset(self, request):
           qs = super().get_queryset(request)
           qs.annotate(number_of_ads=Count('searches__ads')
           return qs