我有一个这样的模型(简化):
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)
答案 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))