获取切片后过滤 Django 查询集
Filtering a Django queryset once a slice has been taken
在我维护的一个 django 论坛中,我将禁止辱骂的用户 4 天。在论坛的"Home page"里,我展示的是大家的评论,但是不包括被封禁的人的评论。它是这样的:
def get_queryset(self):
if self.request.user_banned: #if user is hell-banned
return Link.objects.order_by('-id')[:120]
else: #if user is not hell-banned
global condemned
queryset = Link.objects.order_by('-id').exclude(submitter_id__in=condemned)[:120]
return queryset
以上是ListView
的一个get_queryset
方法。请注意被禁止访问的用户如何无法判断他们的评论已被网站排除(禁止访问点)。 condemned
是一个包含地狱封禁用户主键的列表。
现在我想通过先切片然后排除被禁止的人来优化上面的内容。我正在尝试通过以下方式做到这一点:
def get_queryset(self):
if self.request.user_banned: #if user is hell-banned
return Link.objects.order_by('-id')[:120]
else: #if user is not hell-banned
global condemned
queryset = Link.objects.order_by('-id')[:120]
queryset = queryset.exclude(submitter_id__in=condemned)
return queryset
不幸的是,这给了我错误:
Cannot filter a query once a slice has been taken.
我有什么选择?需要我能找到的最有效的解决方案,因为性能是关键。我在 Django < 1.8。提前致谢。
首先,Django 不允许您在切片之后进行过滤,因为在底层 SQL 中,您无法轻易地 limit
结果然后使用 where
进行过滤。
进行过滤然后切片可能无论如何都不是问题。请注意 querysets are lazy,因此 Django 只会从数据库中获取 120 个对象。
您需要进行一些基准测试以确定排除是否真的让您变慢了。您可以测试使用 exclude
和切片的查询是否明显比使用切片的查询慢。
如果发现排除速度慢,可以在Python
中过滤
comments = [c for c in comments if c.submitter_id not in condemned].
请注意,您最终得到的评论可能少于 120 条。
另一种选择是向 Submitter
模型添加 condemned
标志,然后将查询更改为 .exclude(submitter__condemned=True)
。这可能比当前的 .exclude(submitter_id__in=condemned)
.
更快
您还应该检查您的数据库是否有 submitter_id
字段的索引。因为它是外键,所以它可能是。
在我维护的一个 django 论坛中,我将禁止辱骂的用户 4 天。在论坛的"Home page"里,我展示的是大家的评论,但是不包括被封禁的人的评论。它是这样的:
def get_queryset(self):
if self.request.user_banned: #if user is hell-banned
return Link.objects.order_by('-id')[:120]
else: #if user is not hell-banned
global condemned
queryset = Link.objects.order_by('-id').exclude(submitter_id__in=condemned)[:120]
return queryset
以上是ListView
的一个get_queryset
方法。请注意被禁止访问的用户如何无法判断他们的评论已被网站排除(禁止访问点)。 condemned
是一个包含地狱封禁用户主键的列表。
现在我想通过先切片然后排除被禁止的人来优化上面的内容。我正在尝试通过以下方式做到这一点:
def get_queryset(self):
if self.request.user_banned: #if user is hell-banned
return Link.objects.order_by('-id')[:120]
else: #if user is not hell-banned
global condemned
queryset = Link.objects.order_by('-id')[:120]
queryset = queryset.exclude(submitter_id__in=condemned)
return queryset
不幸的是,这给了我错误:
Cannot filter a query once a slice has been taken.
我有什么选择?需要我能找到的最有效的解决方案,因为性能是关键。我在 Django < 1.8。提前致谢。
首先,Django 不允许您在切片之后进行过滤,因为在底层 SQL 中,您无法轻易地 limit
结果然后使用 where
进行过滤。
进行过滤然后切片可能无论如何都不是问题。请注意 querysets are lazy,因此 Django 只会从数据库中获取 120 个对象。
您需要进行一些基准测试以确定排除是否真的让您变慢了。您可以测试使用 exclude
和切片的查询是否明显比使用切片的查询慢。
如果发现排除速度慢,可以在Python
中过滤comments = [c for c in comments if c.submitter_id not in condemned].
请注意,您最终得到的评论可能少于 120 条。
另一种选择是向 Submitter
模型添加 condemned
标志,然后将查询更改为 .exclude(submitter__condemned=True)
。这可能比当前的 .exclude(submitter_id__in=condemned)
.
您还应该检查您的数据库是否有 submitter_id
字段的索引。因为它是外键,所以它可能是。