2015-05-24 20 views
0

我有一个带有“食物”外键的“膳食”模型。每餐有一个评价:好,坏,或无动于衷。我想查询所有食物的列表并注释每种膳食评级的计数,但有些食物还没有进餐,所以我希望查询使用左外部连接,在这种情况下计数应该为零。带注释条件表达式的Django查询使用INNER JOIN。我如何获得它使用OUTER JOIN?

我在Django 1.8中使用条件表达式,它总是将关系切换为“食物”和“膳食”之间的INNER JOIN。例如:

膳食模型:

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')),Django的生成等

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