0

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

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

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

我想获得 15 种具有更多点击量的某种语言的书籍,显然是按点击量排序的。

我已经看过这个问题,它帮助我找出了我的(部分)解决方案,分为 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. 在最后一个查询中,我通过第二个查询的命中而丢失了订单;

有人有建议吗?

4

1 回答 1

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

  2. 使用查询集的in_bulk()方法来获取易于访问的书籍字典。

所以代码看起来像这样:

# 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]
于 2014-12-30T08:37:00.283 回答