【问题标题】:Query filter based on GenericForeignKey's fields基于 GenericForeignKey 的字段的查询过滤器
【发布时间】:2014-12-29 20:33:49
【问题描述】:

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

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

我正在使用django-hitcount 来计算我的 Books 的浏览量(可能没有人知道它,因为它是一个旧项目),无论如何让我粗略地总结一下:它创建了一个带有命中计数器和 GenericForeignKey 的 HitCount 对象相应的对象。

我想得到 15 本书的点击次数更多的某种语言的书籍,显然是按点击次数排序的。

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

  1. 获取某种语言的所有图书 ID;
  2. 获取同样在第一个列表中的前 15 个 HitCounts related_object 的 ID;
  3. 获取具有在步骤 2 中获取的 ID 的图书;

翻译成代码:

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. 在最后一个查询中,我失去了第二个查询命中的订单;

有人有什么建议吗?

【问题讨论】:

    标签: python django generic-foreign-key


    【解决方案1】:
    1. 将查询集传递给 object_pk__in 条件,而不是 id 列表。 Django 足够聪明,可以将其转换为 SQL 子查询,因此所有费用都将由同样智能的 SQL 服务器处理 :-)

    2. 使用 queryset 的 in_bulk() 方法获取易于访问的 Books 字典。

    所以代码看起来像这样:

    # 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]
    

    【讨论】:

    • 你能解释一下为什么使用 in_bulk 比使用 filter 更好吗?
    • in_bulk() 并不比filter() 好 :-) 它只允许您在一行中构造 ordered 书籍列表(在我的示例中为最后一行)。这与性能无关 - in_bulkfilter 版本的 SQL 查询将是相同的。
    • 真正的性能提升是示例中的前两行代码。使用 SQL 子查询而不是将 id 列表加载到内存中在速度方面是一个巨大的胜利。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-25
    相关资源
    最近更新 更多