Skip to Content

QuerySet Helpers

Helper functions for filtering and sorting Django QuerySets by converted currency values.

Why QuerySet Helpers?

MoneyField.price_target is a Python property calculated at runtime. It cannot be used in database queries:

# This will NOT work - price_target is not a database field Property.objects.filter(price_target__gte=10000) # FieldError!

QuerySet Helpers solve this by annotating the queryset with converted prices using SQL CASE/WHEN:

from django_cfg.apps.tools.currency.services import annotate_converted_price # Annotate with converted price qs = annotate_converted_price( Property.objects.all(), price_field="price", currency_field="price_currency", target_currency="USD", annotation_name="price_usd", ) # Now you can filter/sort by price_usd qs.filter(price_usd__gte=10000) # Works!

How It Works

Generated SQL

SELECT *, CASE WHEN price_currency = 'IDR' THEN price * 0.0000625 WHEN price_currency = 'THB' THEN price * 0.028 WHEN price_currency = 'USD' THEN price * 1.0 ELSE price END AS price_usd FROM properties WHERE price_usd >= 10000 AND price_usd <= 50000

Functions

annotate_converted_price

Annotates a QuerySet with a price field converted to the target currency.

from django_cfg.apps.tools.currency.services import annotate_converted_price qs = annotate_converted_price( queryset=Property.objects.all(), price_field="price", currency_field="price_currency", target_currency="USD", annotation_name="price_usd", ) # Filter by converted price expensive = qs.filter(price_usd__gte=100000) # Order by converted price sorted_qs = qs.order_by("price_usd") # Access on instances for prop in qs[:5]: print(f"{prop.price} {prop.price_currency} = {prop.price_usd:.2f} USD")

Parameters:

ParameterTypeDescription
querysetQuerySetThe QuerySet to annotate
price_fieldstrName of the price field (e.g., "price")
currency_fieldstrName of the currency field (e.g., "price_currency")
target_currencystrCurrency to convert to (e.g., "USD", "EUR")
annotation_namestrName for the annotated field (default: "price_converted")

filter_by_converted_price

Convenience function that combines annotation and filtering:

from django_cfg.apps.tools.currency.services import filter_by_converted_price # Filter properties between $100k and $500k USD qs = filter_by_converted_price( queryset=Property.objects.all(), price_field="price", currency_field="price_currency", target_currency="USD", min_price=100_000, max_price=500_000, )

Parameters:

ParameterTypeDescription
querysetQuerySetThe QuerySet to filter
price_fieldstrName of the price field
currency_fieldstrName of the currency field
target_currencystrCurrency to convert to
min_pricenumber/NoneMinimum price in target currency
max_pricenumber/NoneMaximum price in target currency
annotation_namestrName for the annotated field

get_conversion_rates

Get conversion rates for multiple source currencies:

from django_cfg.apps.tools.currency.services import get_conversion_rates rates = get_conversion_rates(["IDR", "THB", "KRW"], "USD") # {'IDR': Decimal('0.0000625'), 'THB': Decimal('0.028'), 'KRW': Decimal('0.00071')}

Django Filter Integration

Use with django-filter FilterSet:

import django_filters from django_cfg.apps.tools.currency.services import annotate_converted_price PRICE_TARGET_CURRENCY = "USD" class PropertyFilter(django_filters.FilterSet): min_price = django_filters.NumberFilter(method='filter_min_price') max_price = django_filters.NumberFilter(method='filter_max_price') class Meta: model = Property fields = [] def filter_min_price(self, queryset, name, value): """Filter by min price in USD.""" queryset = annotate_converted_price( queryset, price_field="price", currency_field="price_currency", target_currency=PRICE_TARGET_CURRENCY, annotation_name="price_usd", ) return queryset.filter(price_usd__gte=value) def filter_max_price(self, queryset, name, value): """Filter by max price in USD.""" queryset = annotate_converted_price( queryset, price_field="price", currency_field="price_currency", target_currency=PRICE_TARGET_CURRENCY, annotation_name="price_usd", ) return queryset.filter(price_usd__lte=value)

Converting to Different Currencies

The helpers support any target currency, not just USD:

# Convert to EUR qs_eur = annotate_converted_price( Property.objects.all(), price_field="price", currency_field="price_currency", target_currency="EUR", annotation_name="price_eur", ) # Convert to THB qs_thb = annotate_converted_price( Property.objects.all(), price_field="price", currency_field="price_currency", target_currency="THB", annotation_name="price_thb", )

Performance Notes

  • Rate caching: Rates are fetched once from CurrencyRate table
  • Single query: Conversion happens in database, not Python
  • Efficient: Uses SQL CASE/WHEN, no N+1 queries
  • Currency discovery: Fetches distinct currencies from model

See Also

Last updated on