Как объединить два запроса Django, но сохранить результат? - PullRequest
0 голосов
/ 29 апреля 2019

У меня есть два запроса, и оба дают мне правильный результат.

Я попытался объединить их, но затем результаты получаются неправильными (числа умножаются).

    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 и объединяю их - чтоработает, но не хорошо, так как мне не нужно, чтобы это был фрейм данных.

Соответствующие модели выглядят так:

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__day и opportunity__turnover__date

Ожидаемый результат - список всех клиентов с соответствующей суммой рабочих журналов в месяц иОборот за месяц.

Идеально заполняет «пропущенный» месяц, так что есть для каждого клиента, например, январь, февраль, мар, апрель, хотя у клиента нет ни оборота, ни рабочих журналов в феврале и марте - однакомои текущие запросы также пока не делают этого.

Спасибо!

1 Ответ

0 голосов
/ 29 апреля 2019

Вы можете использовать |Оператор, подобный следующему:

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