如何在Django中计算当前连续记录?

1 投票
1 回答
1261 浏览
提问于 2025-04-18 04:07

我有一个简单的日记应用,里面有以下这些模型:

class Journal(models.Model):    
    title = models.CharField(max_length=50)
    created_by = models.ForeignKey(User)

class Entry(models.Model):
    journal = models.ForeignKey(Journal)
    note = models.TextField()
    date = models.DateField()    

我想计算当前的连续提交天数:从今天开始,用户连续提交了多少天的日记。

我有一个函数(效率很低)来计算这个连续天数:

def current_streak(journal):
    count = 0
    today = datetime.date.today()
    date = today-datetime.timedelta(days=count) 
    while Entry.objects.filter(journal=journal, date=date).exists():
        count += 1
        date = today-datetime.timedelta(days=count) 
    return count

有没有更好的方法来计算这个,能减少数据库查询的次数?

1 个回答

4

做这种事情最好的方法是一次性通过一个SQL查询获取所有的日期(并确保Django的ORM确实用一个SQL查询把数据拉取过来),然后在服务器端处理这些信息:

(注意,这个例子假设今天有一条日记的日期是今天,那么这就是一次连续记录的开始,算作1。)

def current_streak(journal):
    total_streak = 0
    current_streak = 0
    today = datetime.date.today()
    compareDate = today + datetime.timedelta(1) # Tomorrow

    # Using list() here pulls all the entries from the DB at once
    # Gets all entry dates for this journal and whose dates are <= today
    entry_dates = list(Entry.objects.values("date").filter(journal=journal, date__lte = today).order_by("-date"))

    for date in entry_dates:
        # Get the difference btw the dates
        delta = compareDate - date

        if delta.days == 1: # Keep the streak going!
            current_streak += 1
        elsif delta.days == 0: # Don't bother increasing the day if there's multiple ones on the same day
            pass
        else: # Awwww...
            break # The current streak is done, exit the loop

        compareDate = date

    if current_streak > total_streak:
        total_streak = current_streak

    return total_streak

这里有一些关于Django ORM的信息,以及它什么时候会从数据库中拉取数据: https://docs.djangoproject.com/en/dev/topics/db/queries/#caching-and-querysets

撰写回答