2015-05-24 2 views
0

У меня есть модель «Питание» с иностранным ключом «Еда». У каждого блюда есть рейтинг: хороший, плохой или равнодушный. Я хочу запросить список всех продуктов и аннотировать количество каждого типа оценки питания, но некоторые продукты еще не имеют еды, поэтому я хочу, чтобы запрос использовал LEFT OUTER JOIN, и в этом случае счетчики должны быть равны нулю.В запросе Django с условным условным выражением используется INNER JOIN. Как я могу использовать OUTER JOIN?

Я использую условные выражения в Django 1.8, и он всегда переключает связь с INNER JOIN между «Food» и «Meal». Например:

модель Питание:

class Meal(models.Model): 
    GOOD = 1 
    BAD = 2 
    INDIFFERENT = 3 
    RATING_CHOICES = (
     (GOOD, 'Good'), 
     (BAD, 'Bad'), 
     (INDIFFERENT, 'Indifferent') 
    ) 
    meal_time = models.DateTimeField() 
    food = models.ForeignKey("Food") 
    rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES) 

Когда я запрос Food.objects.annotate(total_meals=Count('meal')), Джанго генерирует запрос типа

SELECT ... FROM "Food" 
LEFT OUTER JOIN "Meal" ON ... 
GROUP BY "Food" 

Однако, когда я добавить эти условные аннотации:

class FoodQuerySet(models.QuerySet): 
    def with_meal_rating_frequency(self): 
     return self.annotate(
      total_meals=Count('meal'), 
      good_meals=Sum(
       Case(When(meal__rating=Meal.GOOD, then=1), 
        output_field=models.IntegerField(), default=0) 
      ), 
      bad_meals=Sum(
       Case(When(meal__rating=Meal.BAD, then=1), 
        output_field=models.IntegerField(), default=0) 
      ), 
      indifferent_meals=Sum(
       Case(When(meal__rating=Meal.INDIFFERENT, then=1), 
        output_field=models.IntegerField(), default=0) 
      ) 
     ) 

Вместо этого Django использует и INNER JOIN.

SELECT ... FROM "Food" 
INNER JOIN "Meal" ON ... 
GROUP BY "Food" 

Я знаю, что этот вопрос очень похож на this one, но его мне не ясно, как применить принятое решение моего дела. Как я могу заставить Django использовать LEFT OUTER JOIN? Ваша помощь приветствуется, спасибо!

ответ

1

Я нашел решение, которое, кажется, работает до сих пор, используя Count() вместо Sum() и иметь условия для проверки NULL блюд, которые не будут включены в счет:

class FoodQuerySet(models.QuerySet): 
    def with_meal_rating_frequency(self): 
     return self.annotate(
      total_meals=Count('meal'), 
      good_meals=Count(
       Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.GOOD), then='meal__rating'), 
        output_field=models.IntegerField(), default=None) 
      ), 
      bad_meals=Count(
       Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.BAD), then='meal__rating'), 
        output_field=models.IntegerField(), default=None) 
      ), 
      indifferent_meals=Count(
       Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.INDIFFERENT), then='meal__rating'), 
        output_field=models.IntegerField(), default=None) 
      ) 
     ) 
Смежные вопросы