2017-09-15 75 views
2

我在Airbnb上工作就像app一樣。我有一個Flat模型和Price模型,價格有不同類型的優先級,它允許客戶創建一個靈活的價格範圍。如何在基於外部查詢的子查詢中創建條件?

我堅持一個複雜的查詢。此查詢應按日期範圍返回Flats並計算此範圍的價格。


這裏我的模型的一部分:

class Flat(models.Model): 
    prices = models.ManyToManyField(
     'Price' 
     related_name='flats' 
    ) 
    price = models.PositiveIntegerField() 
    ... 

class Price(models.Model): 
    PRIORITY_CHOICES = ((i, i) for i in range(1, 6)) 

    priority = PositiveIntegerField(choices=PRIORITY_CHOICES) 
    start_date = models.DateField() 
    end_date = models.DateField() 
    price = models.PositiveIntegerField() 

到目前爲止,我弄清楚瞭如何通過每一天,更高的優先級標註價格。我寫了平板定製經理:

class FlatManager(models.Manager): 

    def with_prices(self, start, end): 
     days = get_days(start, end) # utils function return list of days 
     prices = {} 
     for day in days: 
      prices[str(day)] = models.Subquery(
      Price.objects.filter(
       flats=models.OuterRef('pk')). 
       filter(start_date__lte=day, end_date__gte=day). 
       order_by('-priority'). 
       values('price')[:1] 
     ) 
     return self.annotate(**price_dict) 

這裏是我的問題

Flat一些日期可以沒有價格數據塊,因此平有自己的price領域客戶不會不會使用靈活的價格的情況。我不知道我需要在查詢中添加條件運算符。如果我將其添加到Price子查詢中,那麼由於嵌套,我無法使用Outref('price')。 當我解決它時,我認爲計算總計值的總和對我來說不會那麼複雜。

請給出一些提示,至少我真的堅持下去。

回答

0

我將條件移至主查詢,檢查子查詢是否返回None,然後使用Flat模型字段。
對於一個date值,它會是這樣的:

day = # some date 
price = Price.objects.filter(
      flats=models.OuterRef('pk')). 
      filter(start_date__lte=day, end_date__gte=day). 
      order_by('-priority'). 
      values('price')[:1]) 
Flat.objects.annotate(price_block=Subquery(price).annotate(
    derived_price = Case(
         When(price_block__isnull=True, then='price'), 
         default=F('price_block') 
    )) 

所以derived_price值將包含來自Price模型或Flat模型priceprice值,如果子查詢返回None
但在我來說,我有一個日期範圍,所以我需要每個日期的子查詢和條件。另外我需要所有註釋價格的總和。
下面是我做的:

class FlatManager(models.Manager): 

    def _construct_conditions(self, keys): 
     # Function construct conditions 
     # like in previous example for each date in range 
     annotations = {} 
     for key in keys: 
      condition = {'{}__isnull'.format(key): True, 'then': 'price'} 
      annotations['derived_{}'.format(key)] = Case(When(**condition), default=F(key)) 
     return annotations 

    def _add_prices(self, keys): 
     values = [F('derived_{}'.format(key)) for key in keys] 
     return sum(values) 

    def with_prices(self, start, end): 
     days = get_days(start, end) # utils function return list of days 
     prices = {} 
     for num, day in enumerate(days): 
      prices['price_{}'.format(num)] = Subquery(
       Price.objects.filter(
       flats=OuterRef('pk')). 
       filter(start_date__lte=day, end_date__gte=day). 
       order_by('-priority'). 
       values('weekend_price' if day.weekday() in [4, 5] else 'price')[:1] 
       # also add a condition for weekend price 
     ) 
     return (self.annotate(**prices). 
       annotate(**self._construct_conditions(prices.keys())). 
       annotate(sum=self._add_prices(prices.keys())) 
       )