2015-06-06 6 views
0

Я поставил предыдущий пост, связанный с этой проблемой here, но потому что это связанная проблема . Я думал, что было бы лучше сделать еще одно сообщение для этого.Django ORM - LEFT JOIN with WHERE clause

Я использую Django 1.8

У меня есть модель пользователя и модель UserAction. У пользователя есть тип. UserAction имеет время, которое указывает, сколько времени прошло действие, а также start_time, который указывает, когда действие началось. Они выглядят так:

class User(models.Model): 
    user_type = models.IntegerField() 

class UserAction: 
    user = models.ForeignKey(User) 
    time = models.IntegerField() 
    start_time = models.DateTimeField() 

Теперь то, что я хочу сделать, это получить все пользователи данного типа и суммой времени их действия, возможно, отфильтрованное start_time.

То, что я делаю что-то вроде этого:

# stubbing in a start time to filter by 
start_time = datetime.now() - datetime.timedelta(days=2) 
# stubbing in a type 
type = 2 
# this gives me the users and the sum of the time of their actions, or 0 if no 
# actions exist 
q = User.objects.filter(user_type=type).values('id').annotate(total_time=Coalesce(Sum(useraction__time), 0) 
# now I try to add the filter for start_time of the actions to be greater than or # equal to start_time 
q = q.filter(useraction__start_time__gte=start_time) 

Теперь то, что это делает, конечно, это внутреннее соединение на UserAction, тем самым удаляя все пользователи без действий. То, что я действительно хочу сделать, это эквивалент моей LEFT JOIN с предложением WHERE, но для жизни меня я не могу найти, как это сделать. Я просмотрел документы, посмотрел на источник, но не нашел ответа. Я (довольно) уверен, что это то, что можно сделать, я просто не вижу, как это сделать. Может ли кто-нибудь указать мне в правильном направлении? Любая помощь будет очень высоко ценится. Большое спасибо!

+0

Не включаются ли пользователи без пользовательского воздействия? – f43d65

+0

@ f43d65 - да, они должны – TheMethod

ответ

0

У меня такая же проблема, как и у вас. Я еще не нашел подходящего способа решения проблемы, но нашел несколько исправлений.

  • Один из способов будет пробегаем по всем пользователям:

    q = User.objects.filter(user_type=type) 
    for (u in q): 
        u.time_sum = UserAction.filter(user=u, start_time__gte=start_time).aggregate(time_sum=Sum('time'))['time_sum'] 
    

    Этот метод делает, однако запрос в базу данных для каждого пользователя. Это может сделать трюк, если у вас мало пользователей, но может потребовать много времени, если у вас большая база данных.

  • Другим способом решения проблемы будет использование метода QuerySet API extra. Это метод, который подробно описан в this blog post Тимми О'Махони.

    valid_actions = UserAction.objects.filter(start_time__gte=start_time) 
    q = User.objects.filter(user_type=type).extra(select={ 
        "time_sum": """ 
        SELECT SUM(time) 
        FROM userAction 
        WHERE userAction.user_id = user.id 
        AND userAction.id IN %s 
        """ % (%s) % ",".join([str(uAction.id) for uAction in valid_actions.all()]) 
    }) 
    

    Этот метод, однако опирается на вызов базы данных с именами SQL таблицы, которая очень не-Django - если изменить db_table одной из баз данных или db_column одной из своих колонок, этот код будет больше не работают. Он требует только 2 запроса, первый - для получения списка допустимых userAction, а другой - для их сопоставления с соответствующим пользователем.