在Django中排除一行重复的行

时间:2013-10-21 10:35:08

标签: django

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

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()一起使用以获取重复的行开头,但这只给了med一个重复的行集,而不是我想要的两个。

例如:

╔════════════════════════════════════════╗
║ 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的训练时刻以及链接到特定组的多对多字段)。如果运动员在他也有团体时刻的那个时刻计划一个团体时刻,那么他自己的时刻应该超越这个时刻。

一个实用的,非常简化的例子

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

以下小组时刻: 一天,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)

这很接近。有了这个,我每时每刻都会得到一个时刻" (组时刻和个别时刻的时刻和日期相同)。问题是我需要同时解决"碰撞"时刻。然后,我可以从生成的查询集中排除群组时刻,最后在我的应用中总结训练时刻时,有时间排除。

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

1 个答案:

答案 0 :(得分:2)

喜欢这个

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

我建议您在django文档中阅读一些关于模型API的内容,然后使用manage.py shell

修改

所以这可能不是完美的解决方案,但它肯定是你想要的方向。你需要的是把它分为两类:时刻和实习生。实习生将暂时拥有一个布尔字段和一个外键。

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))