【问题标题】:cross instance calculations in django querysetDjango 查询集中的跨实例计算
【发布时间】:2019-01-11 00:37:21
【问题描述】:

不确定(a)是否可行,(b)我是否正确地制定了任务。也许正确的方法是重构数据库设计,但我希望对此有任何意见。

我在 django 应用程序中有一个模型,我在其中跟踪用户进入和退出某个页面的时间(通过表单提交或只是关闭浏览器窗口)。我使用 django 频道进行跟踪,但在这种情况下并不重要。

模型如下:

class TimeStamp(models.Model):
    class Meta:
        get_latest_by = 'enter_time'

    page_name = models.CharField(max_length=1000)
    participant = models.ForeignKey(to=Participant, related_name='timestamps')
    timestamp = models.DateTimeField()
    enter_exit_type = models.CharField(max_length=1000, choices=ENTEREXITTYPES,)

我需要做的是计算用户在此页面上花费的时间。所以我需要循环遍历特定用户的Timestamp的所有记录,并计算“进入”和“退出”类型记录之间的时间差。

所以 db 数据可能如下所示:

id  timestamp  enter_exit_type
1  20:12:12  enter
2  20:12:13  exit
3  20:18:12  enter
4  20:21:12  exit
5  20:41:12  enter

那么生成如下查询集的正确方法是:

id  time_spent_sec  
1  0:01  
2  3:00 

最后一个“进入”记录被忽略,因为没有相应的“退出”记录。 结果查询集中的记录 1 是 ids 2 和 1 中的时间戳之间的差异。结果查询集中的记录 2 是 ids 4 和 3 中的时间戳之间的差异。

我可以遍历记录,寻找最近的“退出”记录并计算它,但我在想是否有更简单的解决方案?

【问题讨论】:

  • type 是什么?
  • 对不起,它实际上叫做 event_type。要么进入,要么退出(取决于人此时进入页面还是退出)
  • enter_exit_type 的模型吗?
  • 是的,完全正确。你说的对。抱歉不清楚
  • 可以使用子查询,但它通常会在 (n log n) 或二次时间中运行,并且需要在 timestamp 上设置索引(以便处理它而不是快)。

标签: django django-models django-queryset


【解决方案1】:

有可能:

1) 如果您想在一个查询中获得所有用户的答案,请使用here 方法按用户分组。

2) 用enter_exit_type == 'enter' 过滤掉最后一个未关闭 条目。

3) .annotate(timestamp_with_sign=Case(When(enter_exit_type='exit', then=F('timestamp') * -1), default=F('timestamp' ),)

4) 通过 timestamp_with_sign 字段的 Sum()。

我不确定 F('timestamp') 是否有效,您可能需要寻找将其转换为 unix 时间的方法。

【讨论】:

    【解决方案2】:

    此模型结构可能不足以满足您的要求。所以我建议将您的模型更改为,

    class TimeStamp(models.Model):
        class Meta:
            get_latest_by = 'enter_time'
    
        page_name = models.CharField(max_length=1000)
        participant = models.ForeignKey(Musician, related_name='timestamps')
        enter = models.DateTimeField()
        exit = models.DateTimeField(null=True, blank=True)
    

    然后你会得到数据,

    from django.db.models import F, Sum, ExpressionWrapper, DurationField
    
    TimeStamp.objects.values(
        'participant').annotate(
        sum_time_diff=Sum(
            ExpressionWrapper(F('exit') - F('enter'), output_field=DurationField())
        )
    )

    反应是这样的,

    <QuerySet [{'participant': 1, 'sum_time_diff': datetime.timedelta(0, 7)}, {'participant': 2, 'sum_time_diff': datetime.timedelta(0, 2)}]>
    

    【讨论】:

    • 你需要排除那些exit is None
    猜你喜欢
    • 1970-01-01
    • 2012-01-26
    • 2016-11-14
    • 1970-01-01
    • 2021-07-09
    • 2018-09-10
    • 2010-09-26
    • 2019-07-06
    • 2010-12-11
    相关资源
    最近更新 更多