查询过滤器基于GenericForeignKey的字段

时间:2014-12-29 20:33:50

标签: python django generic-foreign-key

这是简化的问题,我有一个Book模型:

class Book(models.Model):
     language = models.CharField(max_length=2, choices=LANGUAGE_CHOICES)
     ...

我使用django-hitcount来计算我的图书的视图(可能没有人知道它,因为它是一个旧项目),无论如何让我粗略总结一下:它创建一个HitCount对象,其中包含一个命中计数器和一个GenericForeignKey到各自的对象。

我想获得一本特定语言的15本书,点击率更高,显然是按点击顺序排列。

我已经查看了this question,它帮助我找出了我的(部分)解决方案,分为3个查询:

  1. 获取某种语言的所有图书ID;
  2. 获取前15个HitCounts related_object的ID,这些ID也在第一个列表中;
  3. 获取包含在步骤2中获取的ID的图书;
  4. 翻译成代码:

    content_type = ContentType.objects.get_for_model(Book)
    books = tuple(Books.objects.filter(
            language=language).values_list('id', flat=True))
    
    all_time = list(HitCount.objects.filter(
            content_type=content_type,
            object_pk__in=books).values_list(
            'object_pk', 'hits').order_by('-hits')[:15])
    
    all_time_ids = [i[0] for i in all_time]
    
    best_of_all_time = Books.objects.select_related(
            'manga').filter(pk__in=all_time_ids)
    

    这种方法存在两个问题:

    1. 使用大型数据库(如我的),第二个查询变得非常昂贵;
    2. 使用上一个查询,我会通过第二个查询的点击次数丢失订单;
    3. 有人有建议吗?

1 个答案:

答案 0 :(得分:1)

  1. 而不是id列表将查询集传递给object_pk__in条件。 Django非常聪明,可以将其转换为SQL子查询,因此所有费用都将由SQL服务器处理,这也很聪明: - )

  2. 使用queryset的in_bulk()方法获取一个易于访问的图书词典。

  3. 所以代码看起来像这样:

    # just queryset instead of tuple of ids
    books = Books.objects.filter(language=language).values_list('id', flat=True)
    rating = list(HitCount.objects.filter(content_type=content_type,
                                          object_pk__in=books)
                                  .values_list('object_pk', 'hits')
                                  .order_by('-hits')[:15])
    
    book_ids = [r[0] for r in rating]
    
    # dict of Books with book.pk as a key
    books_d = Books.objects.select_related('manga').in_bulk(book_ids)
    
    # list of tuples (book, hits) ordered by -hits
    best_of_all_time = [books_d[pk], hits for pk, hits in rating]