我有一个具有以下模型的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)
min_amount
和max_amount
字段将始终以美元表示值。
我需要能够基于min_amount
和max_amount
过滤查询集,但为此,所有金额必须以美元为单位。要将捐赠金额转换为美元,我需要乘以捐赠货币和日期的汇率。
到目前为止,我发现这样做的唯一方法是迭代dict(queryset)并添加一个名为usd_amount
的新值,但是将来可能会提供非常差的性能。
阅读Django文档,似乎可以使用聚合来完成相同的事情,但是到目前为止,我还无法创建正确的逻辑以产生相同的结果。
我知道我必须使用注释来解决此问题,但我不知道具体如何做,因为它涉及从不相关的模型中获取数据。
经进一步调查,我在Django文档中找到了所需的内容。我需要使用Subquery和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)