【问题标题】:Iterating over a large Django queryset while the data is changing elsewhere当数据在其他地方发生变化时迭代大型 Django 查询集
【发布时间】:2010-01-20 19:48:35
【问题描述】:

遍历查询集,如下所示:

class Book(models.Model):
    # <snip some other stuff>
    activity = models.PositiveIntegerField(default=0)
    views = models.PositiveIntegerField(default=0)

    def calculate_statistics():
        self.activity = book.views * 4
        book.save()

def cron_job_calculate_all_book_statistics():
    for book in Book.objects.all():
        book.calculate_statistics()

...工作得很好。但是,这是一项 cron 任务。发生这种情况时,book.views 正在递增。如果在此 cronjob 运行时修改了 book.views,它会被还原。

现在,book.views 没有被 cronjob 修改,但它在 .all() 查询集调用期间被缓存。当book.save() 时,我有一种感觉是在使用旧的book.views 值。

有没有办法确保只更新activity 字段?或者,假设有 100,000 本书。这将需要相当长的时间才能运行。但是book.views 将来自查询集最初开始运行的时间。是否只使用.iterator() 的解决方案?

更新:这就是我正在做的事情。如果您对如何使这项工作很好地内联有想法,那么我完全赞成。

def calculate_statistics(self):
    self.activity = self.views + self.hearts.count() * 2
    # Can't do self.comments.count with a comments GenericRelation, because Comment uses
    # a TextField for object_pk, and that breaks the whole system. Lame.
    self.activity += Comment.objects.for_model(self).count() * 4
    self.save()

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    以下内容将在 Django 1.1 中为您完成这项工作,无需循环:

    from django.db.models import F
    Book.objects.all().update(activity=F('views')*4)
    

    您也可以进行更复杂的计算:

    for book in Book.objects.all().iterator():
        Book.objects.filter(pk=book.pk).update(activity=book.calculate_activity())
    

    这两个选项都有可能使活动字段与其他选项不同步,但我认为您可以接受,因为您是在 cron 作业中计算它。

    【讨论】:

    • 实际上有很多处理正在进行,包括其他字段。为了这个问题,我只是简化了它。那将是一个很好的解决方案,但我有很多事情要做,我认为我不能直接内联。
    • 我认为您可能不得不接受“活动”可能与其他字段不同步的事实,我认为您已经很好了,因为您使用的是 cron更新“活动”。我更新了答案以处理更复杂的处理,同时避免覆盖对象。
    • 添加了 .iterator(),正如 Alex 所说。
    【解决方案2】:

    除了其他人所说的,如果您正在迭代一个大型查询集,您应该使用 iterator():

    Book.objects.filter(stuff).order_by(stuff).iterator()
    

    这将导致 Django 在迭代时不缓存项目(这可能会为大型结果集使用大量内存)。

    【讨论】:

    • .itertor() 效果很好,但是有什么方法可以“锁定”数据库,以便我可以执行读取,然后以原子方式执行后续写入?这是否需要下降到原始 SQL,这当然是可能的,或者 Django 是否为此提供了一个钩子?
    【解决方案3】:

    无论您如何解决此问题,请注意与交易相关的问题。例如。默认事务隔离级别设置为 REPEATABLE READ,至少对于 MySQL 后端。这一点,再加上 Django 和 db 后端都在特定的自动提交模式下工作且正在进行事务的事实意味着,即使您使用(非常好的)whrde 建议,“views”的值也可能是 no有效期更长。我在这里可能是错的,但请注意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-26
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 2011-09-28
      • 2010-09-09
      相关资源
      最近更新 更多