当数据在其他地方发生变化时迭代大型 Django 查询集

Sam*_*lay 5 python django django-models

迭代查询集,如下所示:

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()
Run Code Online (Sandbox Code Playgroud)

...工作得很好。然而,这是一个 cron 任务。book.views发生这种情况时正在增加。如果book.views在此 cronjob 运行时进行修改,则会将其恢复。

现在,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()
Run Code Online (Sandbox Code Playgroud)

Wil*_*rdy 4

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

from django.db.models import F
Book.objects.all().update(activity=F('views')*4)
Run Code Online (Sandbox Code Playgroud)

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

for book in Book.objects.all().iterator():
    Book.objects.filter(pk=book.pk).update(activity=book.calculate_activity())
Run Code Online (Sandbox Code Playgroud)

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