Django: Как добавить агрегированное поле в набор запросов на основе данных из строки и данных из другой модели? - PullRequest
1 голос
/ 08 апреля 2020

У меня есть приложение Django со следующими моделями:

CURRENCY_CHOICES = (('USD', 'US Dollars'), ('EUR', 'Euro'))

class ExchangeRate(models.Model):
    currency = models.CharField(max_length=3, default='USD', choices=CURRENCY_CHOICES)
    rate = models.FloatField()
    exchange_date = models.DateField()


class Donation(models.Model):
    donation_date = models.DateField()
    donor = models.CharField(max_length=250)
    amount = models.FloatField()
    currency = models.CharField(max_length=3, default='USD', choices=CURRENCY_CHOICES)

У меня также есть форма, которую я использую для фильтрации пожертвований по некоторым критериям:

class DonationFilterForm(forms.Form)
    min_amount = models.FloatField(required=False)
    max_amount = models.FloatField(required=False)

The * Поля 1007 * и max_amount всегда будут представлять значения в долларах США.

Мне нужно иметь возможность фильтровать набор запросов на основе min_amount и max_amount, но для этого все суммы должны быть в долларах США. , Чтобы преобразовать сумму пожертвования в доллары США, мне нужно умножить ее на ExchangeRate валюты и даты пожертвования.

Единственный способ, который я до сих пор делал, это итерация dict (queryset) и добавление нового значения называется usd_amount, но это может привести к очень низкой производительности в будущем.

Чтение документации Django, похоже, то же самое можно сделать с помощью агрегации, но до сих пор я не смог создать правильный лог c, который дал бы мне тот же результат.

1 Ответ

1 голос
/ 08 апреля 2020

Я знал, что мне нужно было использовать аннотирование, чтобы решить эту проблему, но я не знал точно, как, потому что это включало получение данных из несвязанной модели.

При дальнейшем исследовании я нашел то, что мне нужно, в Django Документация. Мне нужно было использовать выражения Подзапрос и OuterRef , чтобы получить значения из внешнего набора запросов, чтобы я мог отфильтровать внутренний набор запросов.

Окончательное решение выглядит следующим образом :

# Prepare the filter with dynamic fields using OuterRef
rates = ExchangeRate.objects.filter(exchange_date=OuterRef('date'), currency='EUR')

# Get the exchange rate for every donation made in Euros
qs = Donation.objects.filter(currency='EUR').annotate(exchange_rate=Subquery(rates.values('rate')[:1]))
# Get the equivalent amount in USD
qs = qs.annotate(usd_amount=F('amount') * F('exchange_rate'))

Итак, наконец, я могу отфильтровать результирующий набор запросов следующим образом:

final_qs = qs.filter(usd_amount__gte=min_amount, usd_amount__lte=max_amount)
...