我有在有限时间内分配给团队的成员。成员可以分配给多个团队。成员每天创建工作日志。工作日志分配给成员,该成员分配给团队。中间表“成员资格”存储团队成员的分配日期。
例如,我需要查询与特定团队相关的所有工作日志。在2019年第一季度。团队成员可以在该季度加入和离开,只应考虑团队分配期间的工作日志。
class Teamuser(models.Model):
key = models.CharField(max_length=200, primary_key=True)
fullname = models.CharField(max_length=200)
class Team(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=200)
members = models.ManyToManyField(Teamuser, through='Membership')
class Membership(models.Model):
id = models.IntegerField(primary_key=True)
memberid = models.IntegerField(null=False, blank=False)
teamuser = models.ForeignKey(Teamuser, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
dateFrom = models.DateField(null=True, blank=True, default=None)
dateTo = models.DateField(null=True, blank=True, default=None)
class Worklog(models.Model):
worker = models.ForeignKey(Teamuser, on_delete=models.PROTECT)
day = models.DateField(null=True, blank=True, default=None)
effort = models.IntegerField()
comment = models.TextField(null=True)
我尝试首先获取在此时间段内分配的所有用户的列表。
start = date(2019,1,10)
end = date(2019,1,20)
Teamuser.objects.filter(Q(membership__team_id=305), Q(membership__dateFrom__range=[start, end]) | Q(membership__dateTo__range=[start, end]) | Q(membership__dateFrom=None) | Q(membership__dateTo=None))
但是,这不包括在查询日期之前加入团队并且之后离开的人员。没有dateFrom和dateTo的人始终在团队中。
目前我最大的挑战是弄清楚两个日期范围(查询第一季度,团队用户的成员资格)在哪个日期重叠
我希望团队用户在指定时间(2019年1月)分配给查询的团队期间(2019年第一季度)创建的所有工作日志的输出。
答案 0 :(得分:1)
我发现了一种可行的方法:
start=date(2019, 1, 1)
end=date(2019, 1, 31)
Worklog.objects.filter(
Q(worker__membership__team_id=305),
Q(day__range=[start, end]),
Q(day__range=[F('worker__membership__dateFrom'), F('worker__membership__dateTo')]) |
Q(worker__membership__dateFrom=None) |
Q(worker__membership__dateTo=None)
)
基本上限制成员资格中的日期和查询期间。
我以前不知道F()
对象,这是关键。
仍要弄清楚是否能涵盖所有情况。
更新:
这有效并显示了正确的结果,我根据下面的Endre Both的输入包括了正确的过滤器。该解决方案也可以使用,但是在我较小的数据集上感觉有点慢。我没有测量时间。
Worklog.objects.filter(
Q(worker__membership__team_id=305),
Q(day__range=[start, end]),
Q(worker__membership__dateFrom=None) | Q(worker__membership__dateFrom__lte=end),
Q(worker__membership__dateTo=None) | Q(worker__membership__dateTo__gte=start),
)
答案 1 :(得分:1)
这可以使您在所考虑的时间段内成为团队成员的用户:
teamusers = (Teamuser.objects
.filter(
Q(membership__dateTo__gte=start) | Q(membership__dateTo=None),
Q(membership__dateFrom__lte=end) | Q(membership__dateFrom=None),
membership__team_id=305,
)
.distinct()
)
此过滤器将筛选出结束日期为null(大于或等于期间开始)的开始日期,以及开始日期为null(小于或等于结束日期)。
但这对于预过滤工作日志最有用,因为您不能只考虑并在一定时间内完成列表中所有成员的日志。您可能有用户在其成员资格日期之外发布的工作日志。
因此,您必须从日志表开始,对有问题的时间段进行预过滤,如果确定可以提高性能,则可能对上面计算出的团队成员列表进行预过滤。
worklogs = Worklog.objects.filter(day__range=[start, end], worker__in=teamusers)
然后,您使用子查询来检查每条记录,在该特定日期发布该记录的用户是否是该团队的成员:
from django.db.models import Q, OuterRef, Exists
membership_subquery = Teamuser.objects.filter(
Q(membership__dateTo__gte=OuterRef('day')) | Q(membership__dateTo=None),
Q(membership__dateFrom__lte=OuterRef('day')) | Q(membership__dateFrom=None),
membership__team_id=305,
teamuser=OuterRef('worker'),
)
worklogs = (worklogs
.annotate(in_team=Exists(membership_subquery))
.filter(in_team=True)
)
现在您有了所有有效工作日志的列表,并且可以附加进一步的处理,例如注释聚合等。