Skip to content
This repository was archived by the owner on Feb 28, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion djmoney_rates/models.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,29 +1,100 @@
from __future__ import unicode_literals

from django.core.cache import cache
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver


class CacheRateSourceManager(models.Manager):

def _get_cache_key(self, source_name):
return "dj_money_rate__ratesource__{}".format(source_name)

def get_source_base_currency(self, source_name):
cache_key = self._get_cache_key(source_name)
base_currency = cache.get(cache_key)
if base_currency is None:
base_currency = RateSource.objects.get(
name=source_name).base_currency
cache.set(cache_key, base_currency) # cache for 'ever'
return base_currency

def set_base_currency(self, source_rate):
cache_key = self._get_cache_key(source_rate.name)
cache.set(cache_key, source_rate.base_currency) # cache for 'ever'

def clear_base_currency(self, source_rate):
cache_key = self._get_cache_key(source_rate.name)
cache.delete(cache_key)


@python_2_unicode_compatible
class RateSource(models.Model):
name = models.CharField(max_length=100, unique=True)
last_update = models.DateTimeField(auto_now=True)
base_currency = models.CharField(max_length=3)
objects = CacheRateSourceManager()

def __str__(self):
return _("%s rates in %s update %s") % (
self.name, self.base_currency, self.last_update)


@receiver(post_save, sender=RateSource)
def update_rate_source_cache(sender, instance, created, **kwargs):
RateSource.objects.set_base_currency(instance)


@receiver(post_delete, sender=RateSource)
def delete_rate_cache(sender, instance, created, **kwargs):
RateSource.objects.clear_base_currency(instance)


class CacheRateManager(models.Manager):

def _get_cache_key(self, source_name, currency):
return "dj_money_rate__rate__{}__{}".format(source_name, currency)

def get_rate_value(self, source_name, currency):
cache_key = self._get_cache_key(source_name, currency)
rate_value = cache.get(cache_key)
if rate_value is None:
rate_value = Rate.objects.get(
source__name=source_name,
currency=currency).value
cache.set(cache_key, rate_value) # cache for 'ever'
return rate_value

def set_rate_value(self, rate):
cache_key = self._get_cache_key(rate.source.name, rate.currency)
cache.set(cache_key, rate.value) # cache for 'ever'

def clear_rate_value(self, rate):
cache_key = self._get_cache_key(rate.source.name, rate.currency)
cache.delete(cache_key)


@python_2_unicode_compatible
class Rate(models.Model):
source = models.ForeignKey(RateSource)
currency = models.CharField(max_length=3)
value = models.DecimalField(max_digits=20, decimal_places=6)
objects = CacheRateManager()

class Meta:
unique_together = ('source', 'currency')

def __str__(self):
return _("%s at %.6f") % (self.currency, self.value)


@receiver(post_save, sender=Rate)
def update_rate_cache(sender, instance, created, **kwargs):
Rate.objects.set_rate_value(instance)


@receiver(post_delete, sender=Rate)
def delete_rate_cache(sender, instance, created, **kwargs):
Rate.objects.clear_rate_value(instance)
Empty file.
55 changes: 55 additions & 0 deletions djmoney_rates/templatetags/money_rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
from decimal import Decimal
from django import template
from djmoney_rates.utils import convert_money
from utils.money import MoneyPatched

register = template.Library()


class ChangeCurrencyNode(template.Node):

def __init__(self, price, price_currency, dest_currency, decimal=None):
self.price = template.Variable(price)
self.price_currency = template.Variable(price_currency)
self.currency = template.Variable(dest_currency)
self.decimal = None
if decimal:
self.decimal = template.Variable(decimal)

def render(self, context):
price = self.price.resolve(context)
if not isinstance(price, Decimal):
price = Decimal(price)
try:
money = convert_money(
price,
self.price_currency.resolve(context),
self.currency.resolve(context)
)
patched_money = MoneyPatched._patch_to_current_class(money)
if self.decimal:
patched_money.decimal_places = self.decimal.resolve(context)
return patched_money
except template.VariableDoesNotExist:
return ''


@register.tag(name='change_currency')
def change_currency(parser, token):
try:
params = token.split_contents()
tag_name = params[0]
current_price = params[1]
current_currency = params[2]
new_currency = params[3]
decimal = None
if len(params) > 4:
decimal = params[4]

except ValueError:
tag_name = token.contents.split()[0]
raise template.TemplateSyntaxError(
'%r tag requires exactly two arguments' % (tag_name))
return ChangeCurrencyNode(current_price, current_currency, new_currency,
decimal)
34 changes: 26 additions & 8 deletions djmoney_rates/utils.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import unicode_literals

from decimal import Decimal

from .exceptions import CurrencyConversionException
Expand All @@ -11,14 +10,17 @@

def get_rate(currency):
"""Returns the rate from the default currency to `currency`."""
source = get_rate_source()
backend = money_rates_settings.DEFAULT_BACKEND()
source_name = backend.get_source_name()
try:
return Rate.objects.get(source=source, currency=currency).value
return Rate.objects.get_rate_value(
source_name=source_name,
currency=currency)
except Rate.DoesNotExist:
raise CurrencyConversionException(
"Rate for %s in %s do not exists. "
"Please run python manage.py update_rates" % (
currency, source.name))
currency, source_name))


def get_rate_source():
Expand All @@ -29,17 +31,29 @@ def get_rate_source():
except RateSource.DoesNotExist:
raise CurrencyConversionException(
"Rate for %s source do not exists. "
"Please run python manage.py update_rates" % backend.get_source_name())
"Please run python manage.py update_rates" % backend.get_source_name()) # NOQA


def get_rate_source_base_currency():
"""Get the default Rate Source and return it."""
backend = money_rates_settings.DEFAULT_BACKEND()
try:
return RateSource.objects.get_source_base_currency(
source_name=backend.get_source_name())
except RateSource.DoesNotExist:
raise CurrencyConversionException(
"Rate for %s source do not exists. "
"Please run python manage.py update_rates" % backend.get_source_name()) # NOQA


def base_convert_money(amount, currency_from, currency_to):
"""
Convert 'amount' from 'currency_from' to 'currency_to'
"""
source = get_rate_source()
source_base_currency = get_rate_source_base_currency()

# Get rate for currency_from.
if source.base_currency != currency_from:
if source_base_currency != currency_from:
rate_from = get_rate(currency_from)
else:
# If currency from is the same as base currency its rate is 1.
Expand All @@ -60,5 +74,9 @@ def convert_money(amount, currency_from, currency_to):
Convert 'amount' from 'currency_from' to 'currency_to' and return a Money
instance of the converted amount.
"""
new_amount = base_convert_money(amount, currency_from, currency_to)
# assign new_amount to amount in case of no convertion, avoid useless else
new_amount = amount
# if the currency is the same, avoid convert nothing
if str(currency_from) != str(currency_to):
new_amount = base_convert_money(amount, currency_from, currency_to)
return moneyed.Money(new_amount, currency_to)