2013-10-07 2 views
0

У меня следующие три таблицы определены.Избегайте декартова продукта с Django

class Operator(models.Model): 
    DisplayName = models.CharField(max_length=64) 

    class Meta: 
     app_label = "Experiment" 
     db_table = "EXPERIMENT_OPERATOR" 

class OperatorSummary(models.Model): 
    Operator = models.ForeignKey(Operator, related_name="TransactionSummary") 
    TransactionCount = models.IntegerField() 
    TransactionValue = models.DecimalField(max_digits=18, decimal_places=2) 
    StartTime = models.DateTimeField(default=timezone.now()) 

    class Meta: 
     app_label = "Experiment" 
     db_table = "EXPERIMENT_OPERATORSUMMARY" 

class OperatorAlerts(models.Model): 
    Operator = models.ForeignKey(Operator, related_name="AlertSummary") 
    AlertScore = models.IntegerField() 
    AlertCount = models.IntegerField() 
    StartTime = models.DateTimeField(default=timezone.now()) 

    class Meta: 
     app_label = "Experiment" 
     db_table = "EXPERIMENT_OPERATORALERTS" 

для оператора, я хотел бы, чтобы получить AlertScore и TransactionCount для заданного диапазона дат. Запрос я использую выглядит следующим образом:

tz = timezone.get_default_timezone()  
vs = Operator.objects.filter(DisplayName="Jimmy", 
          TransactionSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)), 
          AlertSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)))\ 
    .annotate(TotalTransactions=Sum("TransactionSummary__TransactionCount"), 
       TotalAlerts=Sum("AlertSummary__AlertScore"))\ 
    .values("DisplayName", "TransactionSummary__TransactionCount", "AlertSummary__AlertScore") 

Этот запрос выполняет декартово произведение и возвращает все строки в OperatorAlerts и OperatorSummary таблицы, которые соответствуют запросу. Это то, что она возвращает:

{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10} 
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10} 
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 10} 
{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22} 
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22} 
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 22} 
{'AlertSummary__AlertScore': 20, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234} 
{'AlertSummary__AlertScore': 44, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234} 
{'AlertSummary__AlertScore': 543, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 234} 

Я хотел бы, чтобы это исправить, так что я получаю следующий результат:

{'AlertSummary__AlertScore': 607, 'DisplayName': u'Jimmy', 'TransactionSummary__TransactionCount': 266} 

Все результаты рухнуло в одну строку, где AlertScore и TransactionCount суммируются ,

Возможно ли это? Я всегда могу вернуться к созданию отдельного запроса для OperatorAlerts и OperatorSummary, а затем итерации по набору результатов в Python для получения результатов, которые я хочу, или для вызова .aggregate, но я уверен, что должен быть лучший способ?

ответ

1

Попробуйте изменить порядок, в котором применяются методы values() и annotate(). values() должен прийти первый:

vs = Operator.objects.filter(DisplayName="Jimmy", 
          TransactionSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)), 
          AlertSummary__StartTime__gte=tz.localize(datetime(year=2013, month=10, day=1)))\ 
    .values("DisplayName")\ 
    .annotate(TotalTransactions=Sum("TransactionSummary__TransactionCount"), 
       TotalAlerts=Sum("AlertSummary__AlertScore")) 

Это будет группа результатов полей, указанной в values(), а затем генерировать аннотацию для каждой группы. Порядок очень важен - as documented.

Применение values() и annotate() в том, что вы делаете (т.е. annotate() до values()) будет генерировать аннотации для каждого элемента в отдельности.

Обратите внимание, что код выше сгруппирует результаты по DisplayName. Вы можете захотеть сгруппировать по другому полю, например pk.

Кроме того, я предполагаю, что в вашем реальном коде вам нужно будет сразу получить значения для нескольких операторов. Если вы всегда запрашивали для одного оператора за раз (например, в вашем примере), вам лучше использовать aggregate() вместо annotate().

+0

Спасибо за очень полезный ответ. Вы правы, что я хотел бы запросить сразу несколько операторов. Тем не менее, я 'annotate' все еще то, что мне нужно, поскольку я бы хотел, чтобы« TotalTransactions »и« TotalAlerts »были для каждого пользователя, а не для всех пользователей. – CadentOrange

+0

Я согласен, это именно то, что я имел в виду. Я думаю, что предложение было немного запутанным, потому что я использовал неправильную грамматическую форму. Я исправил это сейчас. –

 Смежные вопросы

  • Нет связанных вопросов^_^