Django使用分页删除查询集,而不是捕获集合的所有部分

Posted

技术标签:

【中文标题】Django使用分页删除查询集,而不是捕获集合的所有部分【英文标题】:Django deleting querysets with paging, not catching all parts of the set 【发布时间】:2018-01-23 01:15:07 【问题描述】:

我有一个奇怪的问题,我不太能解释。

我有一个 django 项目,周围有一些陈旧的物品。例如,假设我的对象看起来像这样:

class blog_post(models.Model):
    user_account = models.ForeignKey('accounts.Account')
    text = models.CharField(max_length=255)
    authors = models.ManyToManyField(author)
    created =  models.DateTimeField(blank=True, null=True)

这不是我模型的精确副本,但足够接近。

我创建了一个管理命令来构建这些对象的有序查询集,然后使用分页器删除

我的命令看起来像这样:

all_accounts = Account.objects.all()
for act in all_accounts.iterator():
    stale_objects = blog_post.objects.filter(user_account=act, 
    created=django.utils.timezone.now() - datetime.timedelta(days=7))

    paginator = Paginator(stale_objects.order_by('id'), 100)
    for page in range(1, paginator.num_pages + 1):
        page_stale_objects = blog_post.objects.filter(id__in=paginator.page(page).object_list.values_list('id'))
        page_stale_objects.delete()

我遇到的问题是,在我用我的命令删除这些对象后,仍然有符合查询集参数但没有被删除的对象。因此,我必须运行该命令 3 次以上才能正确找到并删除所有对象。

我首先发现我的日期范围奇怪地位于 DateTime 的边缘,因此在我的命令时间过后 1 周后不久就没有捕捉到对象。情况并非如此,我已经从查询集中删除了 created=... 过滤器,并且得到了相同的结果。

为什么我的查询集在该命令第一次运行时没有捕获所有对象?没有过多的对象,最多约 30,000 行。

【问题讨论】:

Paginator 分页您的数据。我假设如果您删除 page_stale_objects,则只会删除 1 页(这意味着您找到的对象数量超过了分页器的 per_page 值) 【参考方案1】:

查询集的分页被转换为连续的 LIMIT/OFFSET 调用。所以,考虑一下顺序:

获取偏移量为 0 且限制为 20 的项目 删除那些项目 获取下一页,即偏移 21 处的 20 个项目

但是等等!一旦我们删除了第一组,查询集现在再次从 0 开始。跳过现在从 0 到 20 的项目。

解决方案是,不要这样做。分页用于显示对象,而不是删除它们。

【讨论】:

啊,是的,这是我通过更多测试开始得出的结论。谢谢。【参考方案2】:

如果您只想删除查询集,我不明白您为什么要使用分页器。如果我错了,请纠正我,但看起来您正在执行以下操作:

获取查询集 按 id 对该查询集进行排序 分页 从每个列表中获取对象 删除它们

什么时候可以这样做:

获取查询集 删除查询集

如果您有很多对象,这将极大地提升性能。

所以,我建议你这样做:

stale_objects = blog_post.objects.filter(...)
stale_objects.delete()

希望对你有帮助!

【讨论】:

这并没有真正尝试回答核心问题,而且我已经展示了对查询集删除的理解。但是,为了回答你的问题,我这样做有几个原因,1)在此过程中还有其他相关对象正在更新或删除,2)在大型查询集上使用 delete() 需要更长的时间与一次分页和删除一个子集相比,删除。我已经对此进行了多次测试,发现它适用于我的所有案例。

以上是关于Django使用分页删除查询集,而不是捕获集合的所有部分的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 中使用两个以不同方式格式化的查询集进行分页

Laravel 分页不适用于数组而不是集合

Django查询集QuerySet及两大特性

查询集 QuerySet

有啥方法可以将过滤后的查询集传递给 Django 分页?

即使数据库将 0 存储为字段值,Django 查询集对象也返回 None 而不是 0