如何合并两个Django查询但保持结果?

时间:2019-04-29 15:06:27

标签: django django-orm

我有两个查询,两个查询都给我自己一个正确的结果。

我尝试合并它们,但是结果错误(数字相乘)。

    qs2 =  Customer.objects.filter(
                opportunity__turnover__date__range=[start, end]
            ).annotate(
                month=TruncMonth('opportunity__turnover__date')
            ).annotate(
                turnover=Sum(
                    "opportunity__turnover__value", 
                    filter=(
                        Q(opportunity__turnover__type='Project')
                    )
                ),

            ).order_by(
                'name','month'
            ).values('name','month','turnover')     

    qs = Customer.objects.filter(
                project__worklog__day__range=[start, end]
            ).annotate(
                month=TruncMonth('project__worklog__day')
            ).annotate(
                hours=Sum("project__worklog__effort")/60/60,
                hoursBilled=Sum(
                    "project__worklog__effort", 
                    filter=(
                        Q(project__worklog__account__category='Abrechenbar')
                        | Q(project__worklog__account__category='Billable')
                    )
                )/60/60,
            ).order_by(
                'name','month'
            ).values('name','hours','month','hoursBilled')  

    df = pd.DataFrame(list(qs2)).merge(pd.DataFrame(list(qs)))

目前,我正在将两个结果都转换为Pandas Dataframes并合并它们-可以,但是效果不好,因为我不需要将其作为Dataframe。

相关模型如下:

class Opportunity(models.Model):
    id = models.IntegerField(primary_key=True) #CRMID
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    probability = models.FloatField(null=True, blank=True, default=None)
    closeDate = models.DateField(null=True, blank=True, default=None)
    duration = models.IntegerField()
    value = models.FloatField(null=True, blank=True, default=None)
    name = models.CharField(max_length=200, null=False, blank=False)
    type = models.CharField(max_length=100, null=True, blank=True)
class Turnover(models.Model):
    opportunity = models.ForeignKey(Opportunity, on_delete=models.CASCADE)
    type = models.CharField(max_length=100)
    date = models.DateField(null=True, blank=True, default=None)
    value = models.FloatField(null=True, blank=True, default=None)
class Worklog(models.Model):
    worker = models.ForeignKey(Person, on_delete=models.CASCADE)
    day = models.DateField(null=True, blank=True, default=None)
    effort = models.FloatField() #timeSpentSeconds
    account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True, blank=True)
    comment = models.TextField(null=True)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    issue = models.CharField(max_length=200)
class Project(models.Model):
    key = models.CharField(max_length=200, primary_key=True)
    name = models.CharField(max_length=200, null=False, blank=False)
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True) #this has to come via project category description field 
class Customer(models.Model):
    id = models.IntegerField(primary_key=True) #CRMID
    name = models.CharField(max_length=200, null=True, blank=True, default=None)

这是我目前在一个查询集中拥有所有内容的状态:

    qs3 = Customer.objects.filter(
                Q(project__worklog__day__range=[start, end])
                | Q(opportunity__turnover__date__range=[start, end])
            ).annotate(
                month=TruncMonth('project__worklog__day'),
                monthOpp=TruncMonth('opportunity__turnover__date')
            ).filter(
                month=F('monthOpp')
            ).annotate(
                turnover=Sum(
                    "opportunity__turnover__value", 
                    filter=(
                        Q(opportunity__turnover__type='Project')
                    )
                ),
                hours=Sum("project__worklog__effort")/60/60,
                hoursBilled=Sum(
                    "project__worklog__effort", 
                    filter=(
                        Q(project__worklog__account__category='Abrechenbar')
                        | Q(project__worklog__account__category='Billable')
                    )
                )/60/60,
            ).order_by(
                'name','month'
            ).values('name','hours','month','hoursBilled','turnover')  

我得到以下结果:

       hours  hoursBilled       month                                      name     turnover
0    1640.00      1415.00  2019-01-01                       Customer Number ONE     5094288.00
1    1581.67      1482.92  2019-02-01                       Customer Number ONE     5801496.00
2    1117.50      1042.50  2019-03-01                       Customer Number ONE     2226822.72
3     911.67       557.92  2019-04-01                       Customer Number ONE     1395000.00
4   20749.22     14487.72  2019-01-01                       Customer The Second   128129544.48
5   15762.83     14163.33  2019-02-01                       Customer The Second   194805059.94
6   15550.50     12145.00  2019-03-01                       Customer The Second    79077900.00
7     525.00       525.00  2019-04-01                       Customer The Second      190400.00

但是会期望得到这样的结果:

      hours  hoursBilled       month                                      name     turnover
0    328.00       283.00  2019-01-01                       Customer Number ONE       33961.92
1    316.33       296.58  2019-02-01                       Customer Number ONE       35592.00
2    223.50       208.50  2019-03-01                       Customer Number ONE       25020.48
3    182.33       111.58  2019-04-01                       Customer Number ONE       15500.00
4   1482.09      1034.84  2019-01-01                       Customer The Second       99479.46
5   1125.92      1011.67  2019-02-01                       Customer The Second      248793.18
6   1110.75       867.50  2019-03-01                       Customer The Second      102300.00
7     37.50        37.50  2019-04-01                       Customer The Second        5440.00

面临的挑战是要考虑两个日期:

project__worklog__dayopportunity__turnover__date

预期结果是所有客户的列表,以及每月相关的工作日志总数和该月的营业额。

理想情况下填写“缺失”月份,因此每个客户都有一月,二月,三月,四月,即使客户在二月和三月都没有营业额或工作日志,但是我目前的查询还没有这样做。

谢谢!

1 个答案:

答案 0 :(得分:0)

您可以使用|像这样的运算符:

merged = qs | qs2

如果这不起作用,您可以手动构造它:

merged = []

for obj in qs:
    merged.append(obj)

for element in qs2:
    merged.append(qs2)

现在您可以遍历

for item in merged:
    print(item)

这是一个可以完成所有工作的功能

def merge(list_of_querysets)
    return [element for queryset in list_of_querysets for element in queryset]

merged = merge([qs, qs2])