【问题标题】:How to make a condition in subquery based on outer query?如何根据外部查询在子查询中创建条件?
【发布时间】:2018-02-24 15:53:42
【问题描述】:

我在 Airbnb 之类的应用程序上工作。我有一个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()

到目前为止,我已经弄清楚了如何每天以更高的优先级注释价格。我为 Flat 编写了一个自定义管理器:

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 中的某些日期可能没有价格限制,因此 Flat 有自己的 price 字段,用于客户不使用灵活价格的情况。我不知道我需要在查询中在哪里添加条件运算符。如果我在Price 子查询中添加它,那么由于嵌套,我无法使用Outref('price')。 当我解决它时,我认为计算聚合值的总和对我来说不会那么复杂。

请至少给一些提示,我真的坚持下去。

【问题讨论】:

    标签: python django django-models django-queryset


    【解决方案1】:

    我将条件移至主查询,检查子查询是否返回 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 模型的price 值或来自Flat 模型的price 值,如果子查询返回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()))
                    )
    

    【讨论】:

      猜你喜欢
      • 2019-10-12
      • 2012-02-11
      • 1970-01-01
      • 1970-01-01
      • 2012-11-09
      • 1970-01-01
      • 1970-01-01
      • 2016-08-29
      • 2021-07-29
      相关资源
      最近更新 更多