Как фильтровать объекты Django на основе значения, возвращаемого методом?

#python #django

#python #django

Вопрос:

У меня есть объект Django с методом get_volume_sum(self) , который возвращает значение с плавающей запятой, и я хотел бы запросить верхние n объектов с наибольшим значением, как я могу это сделать?

Например, я мог бы выполнить подобный цикл, но мне хотелось бы более элегантного решения.

 vol = []
obj = []
for m in Market.object.filter(**args): #  3000 objects
    sum = m.get_volume_sum()
    vol.append(sum)
    obj.append(m.id)
top = search_top_n(obj, vol, n)
  

И вот как выглядит метод:

 # return sum of volume over last hours
def get_volume_sum(self, hours):
    return Candle.objects.filter(market=self,
                                 dt__gte=timezone.now()-timedelta(hours=hours)
                                 ).aggregate(models.Sum('vo'))
  

Из того, что я вижу здесь, даже с Python нет единого линейного решения.

Комментарии:

1. Как это get_volume_sum выглядит. Вы не можете фильтровать a QuerySet на основе метода, поскольку база данных не знает о методах. Обычно лучше выполнять логику на стороне базы данных, поскольку это будет быстрее.

2. привет @WillemVanOnsem спасибо за ответ, я добавил, как выглядит метод. Спасибо!

3. где это hours происходит в m.get_volume_sum() ?

4. hours происходит из другого метода для другого объекта

Ответ №1:

Вы не должны фильтровать с помощью метода, это приведет к проблеме N 1: для 3’000 Market объектов он сгенерирует дополнительные 3’0000 запросов для получения объемов.

Вы можете сделать это массово с помощью .annotate(…) [Django-doc]:

 from django.db.models import Sum

hours = 12  # some value for hours

Market.objects.filter(
    **args,
    candle__dt__gte=timezone.now()-timedelta(hours=hours),
).annotate(
    candle_vol=Sum('candle__vo')
).order_by('-candle_vol')  

Здесь, однако, есть небольшое предостережение: если нет связанных Candle , то они Market будут отфильтрованы. Мы можем предотвратить это, разрешив также Market s без Candle s с:

 from django.db.models import Q, Sum

hours = 12  # some value for hours

Market.objects.filter(
    Q(candle__dt__gte=timezone.now()-timedelta(hours=hours)) |
    Q(candle=None),
    **args
).annotate(
    candle_vol=Sum('candle__vo')
).order_by('-candle_vol')