3

我有一个这样的模型(简化):

class TrainingMoment(models.Model):
    date = models.DateField()
    # Moment of the day, 2 is afternoon for example
    moment_nr = models.IntegerField()
    is_group_moment = models.BooleanField()

在给定的日期和 moment_nr 上,可以存在 2 行。一个与is_group_moment=False一个与is_group_moment=True。对于TrainingMoment.objects.filter(date__range=(start_date,end_date))我想要排除的行中的每一个重复项is_group_moment = True

请注意,我只想排除is_group_moment=True具有相同日期和 moment_nr 的行的行is_group_moment=False

我曾尝试将 annotate() 与 group_by() 一起使用来获取重复的行,但这只给了每个重复行集中的一个,而不是我想要的两个。

例如:

╔════════════════════════════════════════╗
║ date       moment_nr is_group_moment   ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True              ║
║ 2013-10-02 1         False             ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True              ║
║ 2013-10-04 2         False             ║
║ 2013-10-01 1         True              ║
╚════════════════════════════════════════╝

应该:

╔═════════════════════════════════════════╗
║ date       moment_nr is_group_moment    ║
╠═════════════════════════════════════════╣
║ 2013-10-01 1          True              ║
║ 2013-10-02 1          False             ║
║ 2013-10-03 1          False             ║
║ 2013-10-03 2          False             ║
║ 2013-10-04 2          False             ║
║ 2013-10-01 1          True              ║
╚═════════════════════════════════════════╝

我有另一个模型 Activity,它存储训练时间,我想使用上面的查询集来获得正确的一周总和:

class Activity(models.Model):
    activitytype = models.ForeignKey(ActivityType)
    trainingmoment = models.ForeignKey(TrainingMoment)
    # Time in minutes
    time = models.IntegerField()

我想要完成的总结:

# 1. Get training moments for a given period, for example a week (no problem here)
tms_for_summing = TrainingMoment.objects.filter(date__range=(start_date,end_date))

# 2. Filter out duplicates in the way described above

# 3. Use the resulting queryset (tms_for_summing) to sum activity
summed_activity = Activity.objects.filter(trainingmoment__in = tms_for_summing)    

编辑 这里为那些想知道我的数据库设计的人提供了一些额外的解释:

正如您可能已经想到的那样,这是一个培训记录应用程序。在我的问题中,我想要实现的页面是一个计划页面。所有的训练时刻都是计划好的训练时刻。单个运动员可以计划自己的训练。此外,教练可以同时为一组运动员计划训练。这成为一个组时刻(is_groupmoment = True 的训练时刻以及链接到特定组的多对多字段)。如果运动员在他也有一个集体时刻的时刻和日期计划一个集体时刻,他自己的时刻应该覆盖这个时刻。

一个实用的、非常简单的例子

我有以下个人时刻: Day, Momentnr 星期一 1 星期二 1

以及以下集体时刻:Day, Momentnr 周二 1 周三 1

在我显示与这些时刻相关的时刻和总活动的表格中,我想显示周一和周二的个人时刻,以及周三的集体时刻,因为那里没有个人时刻超越集体时刻。我可以使用纯python代码在视图中求和,方法是对时刻进行逐个求和,同时检查同一日期是否同时存在组时刻和个人时刻,但这将是一种非常丑陋且缓慢的方法它,特别是在总结一整年时。

我试过的

我试过这个:

training_moments = TrainingMoment.objects.filter(date__range=('2013-08-19','2013-10-28'))
moments_to_exclude = training_moments.annotate(num_dates=Count('date'), num_momentnrs=Count('momentnr')).filter(num_dates__gt= 1, num_momentnrs__gt=1)

这很接近。有了这个,我为每个“时间段”得到一个时刻(其中时刻 nr 和日期对于团体时刻和个人时刻是相同的)。问题是我需要同时获得“碰撞”时刻。然后我可以从结果查询集中排除组时刻,最后在我的应用程序中总结训练时刻时排除这些时刻。

moments_to_exclude = moments_to_exclude.exclude(is_group_moment=True)
desired_result = training_moments.exclude(pk__in=moments_to_exclude)
4

1 回答 1

2

像这样

TrainingMoment.objects.filter(date=… , moment_nr=…).exclude(is_group_moment=True)

我建议您在 django 文档中阅读一些有关模型 API 的信息,并使用manage.py shell

编辑

所以这可能不是完美的解决方案,但它肯定是您想要的方向。您需要将其分解为两个类别:Moment 和 Trainee。受训者将暂时拥有一个布尔字段和一个外键。

class Moment(models.Model):
    date = …
    number = …

class Trainee(models.Model):
    is_group = models.BooleanField()
    moment = models.ForeignKey(Moment)

您在这里获得的是没有具有相同日期 + 数字字段的重复 Moment 对象,因此更容易过滤范围而不会太头疼。同时,过滤 True 值更容易(使用方法进行),您可以通过客户端大小进行过滤(甚至只需检查相关学员的数量)。

您必须添加自定义约束以防止添加具有相同信息的对象,但这并不是很复杂

编辑 2

为了回答您的意见,让我们以您的示例表为例。假设您当前的布局如下所示:

╔════════════════════════════════════════╗
║ date       moment_nr is_group_moment   ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True              ║
║ 2013-10-02 1         False             ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True              ║
║ 2013-10-04 2         False             ║
╚════════════════════════════════════════╝

然后根据我的设计建议,它看起来像这样:

╔════════════════════════════════════════╗
║ date       moment_nr related trainees  ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True, False       ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True, False       ║
╚════════════════════════════════════════╝

因为现在您没有重复行,即它实际上会将早期设计中的两个 Moment 对象合并为一个具有两个相关 Trainee 对象的对象。

换句话说,问题末尾的最终代码将如下所示:

# 1. Get training moments for a given period, for example a week (no problem here)
tms_for_summing = TrainingMoment.objects.filter(date__range=(start_date,end_date))    

# 3. Use the resulting queryset (tms_for_summing) to sum activity
summed_activity = Activity.objects.filter(trainingmoment__in = tms_for_summing)

坦率地说,这也可以缩减为一行:

summed = Activity.objects.filter(trainingmoment__range=(start_date, end_date))
于 2013-10-21T10:50:41.087 回答