并使用反向关系进行搜索

时间:2016-06-14 17:22:02

标签: django django-models django-admin

我正在使用以下模型开展django项目。

class User(models.Model):
    pass

class Item(models.Model):
    user = models.ForeignKey(User)
    item_id = models.IntegerField()

大约有1000万件物品和10万名用户。

我的目标是覆盖永久性的默认管理搜索 返回拥有" all"的所有匹配用户在合理的时间范围内指定的项目ID。

这些是我用来更好地说明我的标准的几项测试。

class TestSearch(TestCase):
    def search(self, searchterm):
        """A tuple is returned with the first element as the queryset"""
        return do_admin_search(User.objects.all())

    def test_return_matching_users(self):
        user = User.objects.create()
        Item.objects.create(item_id=12345, user=user)
        Item.objects.create(item_id=67890, user=user)

        result = self.search('12345 67890')
        assert_equal(1, result[0].count())
        assert_equal(user, result[0][0])

    def test_exclude_users_that_do_not_match_1(self):
        user = User.objects.create()
        Item.objects.create(item_id=12345, user=user)

        result = self.search('12345 67890')
        assert_false(result[0].exists())

    def test_exclude_users_that_do_not_match_2(self):
        user = User.objects.create()

        result = self.search('12345 67890')
        assert_false(result[0].exists())

以下代码段是我使用annotate尝试超过50秒的最佳尝试。

def search_by_item_ids(queryset, item_ids):
    params = {}
    for i in item_ids:
        cond = Case(When(item__item_id=i, then=True), output_field=BooleanField())
        params['has_' + str(i)] = cond

    queryset = queryset.annotate(**params)

    params = {}
    for i in item_ids:
        params['has_' + str(i)] = True
    queryset = queryset.filter(**params)
    return queryset

我有什么办法可以加快速度吗?

1 个答案:

答案 0 :(得分:0)

以下是一些快速建议,可以大幅改善效果。

在初始查询集上使用prefetch_related`获取相关项

queryset = User.objects.filter(...).prefetch_related('user_set')

使用__in运算符进行过滤,而不是循环使用ID列表

def search_by_item_ids(queryset, item_ids):
    return queryset.filter(item__item_id__in=item_ids)

请勿注释它是否已成为查询的条件

由于您知道此查询集仅包含item_ids列表中包含ID的记录,因此无需为每个对象编写该记录。

全部放在一起

只需致电 -

,您就可以大大加快您的工作速度
queryset = User.objects.filter(
    item__item_id__in=item_ids
).prefetch_related('user_set')

完整查询只有2 db命中。