Money fields for Django forms and models.

Overview

django-money

Build Status Coverage Status Documentation Status PyPI

A little Django app that uses py-moneyed to add support for Money fields in your models and forms.

  • Django versions supported: 1.11, 2.1, 2.2, 3.0, 3.1
  • Python versions supported: 3.5, 3.6, 3.7, 3.8, 3.9
  • PyPy versions supported: PyPy3

If you need support for older versions of Django and Python, please refer to older releases mentioned in the release notes.

Through the dependency py-moneyed, django-money gets:

  • Support for proper Money value handling (using the standard Money design pattern)
  • A currency class and definitions for all currencies in circulation
  • Formatting of most currencies with correct currency sign

Installation

Using pip:

$ pip install django-money

This automatically installs py-moneyed v0.8 (or later).

Add djmoney to your INSTALLED_APPS. This is required so that money field are displayed correctly in the admin.

INSTALLED_APPS = [
   ...,
   'djmoney',
   ...
]

Model usage

Use as normal model fields:

from djmoney.models.fields import MoneyField
from django.db import models


class BankAccount(models.Model):
    balance = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')

To comply with certain strict accounting or financial regulations, you may consider using max_digits=19 and decimal_places=4, see more in this StackOverflow answer

It is also possible to have a nullable MoneyField:

class BankAccount(models.Model):
    money = MoneyField(max_digits=10, decimal_places=2, null=True, default_currency=None)

account = BankAccount.objects.create()
assert account.money is None
assert account.money_currency is None

Searching for models with money fields:

from djmoney.money import Money


account = BankAccount.objects.create(balance=Money(10, 'USD'))
swissAccount = BankAccount.objects.create(balance=Money(10, 'CHF'))

BankAccount.objects.filter(balance__gt=Money(1, 'USD'))
# Returns the "account" object

Field validation

There are 3 different possibilities for field validation:

  • by numeric part of money despite on currency;
  • by single money amount;
  • by multiple money amounts.

All of them could be used in a combination as is shown below:

from django.db import models
from djmoney.models.fields import MoneyField
from djmoney.money import Money
from djmoney.models.validators import MaxMoneyValidator, MinMoneyValidator


class BankAccount(models.Model):
    balance = MoneyField(
        max_digits=10,
        decimal_places=2,
        validators=[
            MinMoneyValidator(10),
            MaxMoneyValidator(1500),
            MinMoneyValidator(Money(500, 'NOK')),
            MaxMoneyValidator(Money(900, 'NOK')),
            MinMoneyValidator({'EUR': 100, 'USD': 50}),
            MaxMoneyValidator({'EUR': 1000, 'USD': 500}),
        ]
    )

The balance field from the model above has the following validation:

  • All input values should be between 10 and 1500 despite on currency;
  • Norwegian Crowns amount (NOK) should be between 500 and 900;
  • Euros should be between 100 and 1000;
  • US Dollars should be between 50 and 500;

Adding a new Currency

Currencies are listed on moneyed, and this modules use this to provide a choice list on the admin, also for validation.

To add a new currency available on all the project, you can simple add this two lines on your settings.py file

import moneyed
from moneyed.localization import _FORMATTER
from decimal import ROUND_HALF_EVEN


BOB = moneyed.add_currency(
    code='BOB',
    numeric='068',
    name='Peso boliviano',
    countries=('BOLIVIA', )
)

# Currency Formatter will output 2.000,00 Bs.
_FORMATTER.add_sign_definition(
    'default',
    BOB,
    prefix=u'Bs. '
)

_FORMATTER.add_formatting_definition(
    'es_BO',
    group_size=3, group_separator=".", decimal_point=",",
    positive_sign="",  trailing_positive_sign="",
    negative_sign="-", trailing_negative_sign="",
    rounding_method=ROUND_HALF_EVEN
)

To restrict the currencies listed on the project set a CURRENCIES variable with a list of Currency codes on settings.py

CURRENCIES = ('USD', 'BOB')

The list has to contain valid Currency codes

Additionally there is an ability to specify currency choices directly:

CURRENCIES = ('USD', 'EUR')
CURRENCY_CHOICES = [('USD', 'USD $'), ('EUR', 'EUR €')]

Important note on model managers

Django-money leaves you to use any custom model managers you like for your models, but it needs to wrap some of the methods to allow searching for models with money values.

This is done automatically for the "objects" attribute in any model that uses MoneyField. However, if you assign managers to some other attribute, you have to wrap your manager manually, like so:

from djmoney.models.managers import money_manager


class BankAccount(models.Model):
    balance = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
    accounts = money_manager(MyCustomManager())

Also, the money_manager wrapper only wraps the standard QuerySet methods. If you define custom QuerySet methods, that do not end up using any of the standard ones (like "get", "filter" and so on), then you also need to manually decorate those custom methods, like so:

from djmoney.models.managers import understands_money


class MyCustomQuerySet(QuerySet):

   @understands_money
   def my_custom_method(*args, **kwargs):
       # Awesome stuff

Format localization

The formatting is turned on if you have set USE_L10N = True in the your settings file.

If formatting is disabled in the configuration, then in the templates will be used default formatting.

In the templates you can use a special tag to format the money.

In the file settings.py add to INSTALLED_APPS entry from the library djmoney:

INSTALLED_APPS += ('djmoney', )

In the template, add:

{% load djmoney %}
...
{% money_localize money %}

and that is all.

Instructions to the tag money_localize:

{% money_localize <money_object> [ on(default) | off ] [as var_name] %}
{% money_localize <amount> <currency> [ on(default) | off ] [as var_name] %}

Examples:

The same effect:

{% money_localize money_object %}
{% money_localize money_object on %}

Assignment to a variable:

{% money_localize money_object on as NEW_MONEY_OBJECT %}

Formatting the number with currency:

{% money_localize '4.5' 'USD' %}
Return::

    Money object

Testing

Install the required packages:

git clone https://github.com/django-money/django-money

cd ./django-money/

pip install -e ".[test]" # installation with required packages for testing

Recommended way to run the tests:

tox

Testing the application in the current environment python:

make test

Working with Exchange Rates

To work with exchange rates, add the following to your INSTALLED_APPS.

INSTALLED_APPS = [
    ...,
    'djmoney.contrib.exchange',
]

Also, it is required to have certifi installed. It could be done via installing djmoney with exchange extra:

pip install "django-money[exchange]"

To create required relations run python manage.py migrate. To fill these relations with data you need to choose a data source. Currently, 2 data sources are supported - https://openexchangerates.org/ (default) and https://fixer.io/. To choose another data source set EXCHANGE_BACKEND settings with importable string to the backend you need:

EXCHANGE_BACKEND = 'djmoney.contrib.exchange.backends.FixerBackend'

If you want to implement your own backend, you need to extend djmoney.contrib.exchange.backends.base.BaseExchangeBackend. Two data sources mentioned above are not open, so you have to specify access keys in order to use them:

OPEN_EXCHANGE_RATES_APP_ID - '<your actual key from openexchangerates.org>'

FIXER_ACCESS_KEY - '<your actual key from fixer.io>'

Backends return rates for a base currency, by default it is USD, but could be changed via BASE_CURRENCY setting. Open Exchanger Rates & Fixer supports some extra stuff, like historical data or restricting currencies in responses to the certain list. In order to use these features you could change default URLs for these backends:

OPEN_EXCHANGE_RATES_URL = 'https://openexchangerates.org/api/historical/2017-01-01.json?symbols=EUR,NOK,SEK,CZK'
FIXER_URL = 'http://data.fixer.io/api/2013-12-24?symbols=EUR,NOK,SEK,CZK'

Or, you could pass it directly to update_rates method:

>>> from djmoney.contrib.exchange.backends import OpenExchangeRatesBackend
>>> backend = OpenExchangeRatesBackend(url='https://openexchangerates.org/api/historical/2017-01-01.json')
>>> backend.update_rates(symbols='EUR,NOK,SEK,CZK')

There is a possibility to use multiple backends in the same time:

>>> from djmoney.contrib.exchange.backends import FixerBackend, OpenExchangeRatesBackend
>>> from djmoney.contrib.exchange.models import get_rate
>>> OpenExchangeRatesBackend().update_rates()
>>> FixerBackend().update_rates()
>>> get_rate('USD', 'EUR', backend=OpenExchangeRatesBackend.name)
>>> get_rate('USD', 'EUR', backend=FixerBackend.name)

Regular operations with Money will use EXCHANGE_BACKEND backend to get the rates. Also, there are two management commands for updating rates and removing them:

$ python manage.py update_rates
Successfully updated rates from openexchangerates.org
$ python manage.py clear_rates
Successfully cleared rates for openexchangerates.org

Both of them accept -b/--backend option, that will update/clear data only for this backend. And clear_rates accepts -a/--all option, that will clear data for all backends.

To set up a periodic rates update you could use Celery task:

CELERYBEAT_SCHEDULE = {
    'update_rates': {
        'task': 'path.to.your.task',
        'schedule': crontab(minute=0, hour=0),
        'kwargs': {}  # For custom arguments
    }
}

Example task implementation:

from django.utils.module_loading import import_string

from celery import Celery
from djmoney import settings


app = Celery('tasks', broker='pyamqp://[email protected]//')


@app.task
def update_rates(backend=settings.EXCHANGE_BACKEND, **kwargs):
    backend = import_string(backend)()
    backend.update_rates(**kwargs)

To convert one currency to another:

>>> from djmoney.money import Money
>>> from djmoney.contrib.exchange.models import convert_money
>>> convert_money(Money(100, 'EUR'), 'USD')
<Money: 122.8184375038380800 USD>

Exchange rates are integrated with Django Admin.

django-money can be configured to automatically use this app for currency conversions by settings AUTO_CONVERT_MONEY = True in your Django settings. Note that currency conversion is a lossy process, so automatic conversion is usually a good strategy only for very simple use cases. For most use cases you will need to be clear about exactly when currency conversion occurs, and automatic conversion can hide bugs. Also, with automatic conversion you lose some properties like commutativity (A + B == B + A) due to conversions happening in different directions.

Usage with Django REST Framework

Make sure that djmoney and is in the INSTALLED_APPS of your settings.py and that rest_framework has been installed. MoneyField will automatically register a serializer for Django REST Framework through djmoney.apps.MoneyConfig.ready().

You can add a serializable field the following way:

from djmoney.contrib.django_rest_framework import MoneyField

class Serializers(serializers.Serializer):
    my_computed_prop = MoneyField(max_digits=10, decimal_places=2)

Built-in serializer works in the following way:

class Expenses(models.Model):
    amount = MoneyField(max_digits=10, decimal_places=2)


class Serializer(serializers.ModelSerializer):
    class Meta:
        model = Expenses
        fields = '__all__'

>>> instance = Expenses.objects.create(amount=Money(10, 'EUR'))
>>> serializer = Serializer(instance=instance)
>>> serializer.data
ReturnDict([
    ('id', 1),
    ('amount_currency', 'EUR'),
    ('amount', '10.000'),
])

Note that when specifying individual fields on your serializer, the amount and currency fields are treated separately. To achieve the same behaviour as above you would include both field names:

class Serializer(serializers.ModelSerializer):
    class Meta:
        model = Expenses
        fields = ('id', 'amount', 'amount_currency')

Customization

If there is a need to customize the process deconstructing Money instances onto Django Fields and the other way around, then it is possible to use a custom descriptor like this one:

class MyMoneyDescriptor:

    def __get__(self, obj, type=None):
        amount = obj.__dict__[self.field.name]
        return Money(amount, "EUR")

It will always use EUR for all Money instances when obj.money is called. Then it should be passed to MoneyField:

class Expenses(models.Model):
    amount = MoneyField(max_digits=10, decimal_places=2, money_descriptor_class=MyMoneyDescriptor)

Background

This project is a fork of the Django support that was in http://code.google.com/p/python-money/

This version adds tests, and comes with several critical bugfixes.

Comments
  • Upgrade to py-moneyed 2.0

    Upgrade to py-moneyed 2.0

    • Update py-moneyed to 2.0.
    • Remove the deprecated Money.decimal_places_display property and argument.
    • Remove the deprecated CURRENCY_DECIMAL_PLACES_DISPLAY setting.

    WIP, there are some things to figure out in terms of behavior due py-moneyed no longer having a default dummy currency. I believe this should be pretty straightforward though.

    opened by antonagestam 24
  • Currency choices

    Currency choices

    I would like to have an option to specify the currencies selectable, ie I can now do this:

    price = MoneyField(
        verbose_name=_(u'price'),
        help_text=_(u'Price of the prototype, changing it does not affect any already sold tickets.'),
        default_currency=DKK,
        default=Money('0.00', DKK),
        currency_choices=[(DKK.code, _(DKK.name))],
        decimal_places=2,
        max_digits=12,
    )
    
    opened by benjaoming 20
  • Currency exchange

    Currency exchange

    TODO:

    • [x] Docs
    • [x] Better interface for exchange (move convert to another module)
    • [x] More tests
    • [x] Another exchange backend
    • [x] Better error handling
    • [x] Replace django-money-rates integration with this one
    • [x] Test with PostgreSQL & MySQL
    • [x] Output for management commands

    Since django-money-rates seems to be abandoned I decided to implement similar features directly in djmoney.

    opened by Stranger6667 17
  • Migration fails after field has been added

    Migration fails after field has been added

    I've added the value field to my existing model:

    class Cost(PolymorphicModel):
        ...
        value = fields.MoneyField(max_digits = 5, decimal_places = 2, default_currency = 'GBP')
    

    Migrations have been then prepared successfully, but migration process failed. This is the stack trace:

    Operations to perform:
      Synchronize unmigrated apps: frontend, staticfiles, messages, polymorphic, material, material_admin
      Apply all migrations: admin, auth, contenttypes, <anon>_fuel, sessions
    Synchronizing apps without migrations:
      Creating tables...
        Running deferred SQL...
      Installing custom SQL...
    Running migrations:
      Rendering model states... DONE
      Applying <anon>_fuel.0007_auto_20151006_1506...Traceback (most recent call last):
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/sqlite3/base.py", line 318, in execute
        return Database.Cursor.execute(self, query, params)
    sqlite3.IntegrityError: NOT NULL constraint failed: <anon>_fuel_cost__new.value_currency
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "./manage.py", line 10, in <module>
        execute_from_command_line(sys.argv)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
        utility.execute()
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
        self.fetch_command(subcommand).run_from_argv(self.argv)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/core/management/base.py", line 393, in run_from_argv
        self.execute(*args, **cmd_options)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/core/management/base.py", line 444, in execute
        output = self.handle(*args, **options)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 222, in handle
        executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/migrations/executor.py", line 110, in migrate
        self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/migrations/executor.py", line 148, in apply_migration
        state = migration.apply(state, schema_editor)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/migrations/migration.py", line 115, in apply
        operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/migrations/operations/fields.py", line 62, in database_forwards
        field,
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/sqlite3/schema.py", line 179, in add_field
        self._remake_table(model, create_fields=[field])
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/sqlite3/schema.py", line 147, in _remake_table
        self.quote_name(model._meta.db_table),
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 111, in execute
        cursor.execute(sql, params)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute
        return super(CursorDebugWrapper, self).execute(sql, params)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/utils.py", line 97, in __exit__
        six.reraise(dj_exc_type, dj_exc_value, traceback)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/utils/six.py", line 658, in reraise
        raise value.with_traceback(tb)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
        return self.cursor.execute(sql, params)
      File "/home/mfita/.virtualenvs/<anon>-django-py3.4/lib/python3.4/site-packages/django/db/backends/sqlite3/base.py", line 318, in execute
        return Database.Cursor.execute(self, query, params)
    django.db.utils.IntegrityError: NOT NULL constraint failed: <anon>_fuel_cost__new.value_currency
    
    bug 
    opened by michalfita 17
  • Set value check

    Set value check

    suggested better fix for #427

    TODO: is the currency field guaranteed to appear before the amount (and main) field? yes because of the creation_counters?

    opened by davidszotten 16
  • Changed managers patching

    Changed managers patching

    I propose a solution to the problem, which I mentioned here https://github.com/jakewins/django-money/pull/28. It's based on class_prepared signal usage.

    opened by Stranger6667 16
  • Comparison with different currencies

    Comparison with different currencies

    Can you explain why MoneyPatched class doesn't allow equality comparison of two values if they are of different currency? It seems pretty ok to know if Money(100, 'USD') == Money(100, 'EUR'). When you try to do this with MoneyPatched class, you get an error "Cannot add or subtract two Money instances with different currencies". Seems like the message was copypasted from other method, and the error is non relevant here.

    opened by ivirabyan 15
  • Appends _currency to non-money ExpressionFields

    Appends _currency to non-money ExpressionFields

    It seems like _expand_money_params processes all ExpressionNodes (e.g. Q and F) as if they are MoneyFields which leads to querying for $field_currency. I had some trouble getting all of the tests to pass (and your travis-ci widget points to the wrong project), but the below should evidence the error.

    diff --git a/djmoney/tests/model_tests.py b/djmoney/tests/model_tests.py
    index 974be95..d062853 100644
    --- a/djmoney/tests/model_tests.py
    +++ b/djmoney/tests/model_tests.py
    @@ -10,7 +10,7 @@ from .testapp.models import (ModelWithVanillaMoneyField,
         ModelRelatedToModelWithMoney, ModelWithChoicesMoneyField, BaseModel, InheritedModel, InheritorModel,
         SimpleModel, NullMoneyFieldModel, ModelWithDefaultAsDecimal, ModelWithDefaultAsFloat, ModelWithDefaultAsInt,
         ModelWithDefaultAsString, ModelWithDefaultAsStringWithCurrency, ModelWithDefaultAsMoney, ModelWithTwoMoneyFields,
    -    ProxyModel)
    +    ProxyModel, ModelWithNonMoneyField)
     import moneyed
    
    
    @@ -171,6 +171,15 @@ class RelatedModelsTestCase(TestCase):
             ModelRelatedToModelWithMoney.objects.get(moneyModel__money__lt=Money("1000.0", moneyed.ZWN))
    
    
    +class NonMoneyTestCase(TestCase):
    +
    +    def testAllowExpressionNodesWithoutMoney(self):
    +        """ allow querying on expression nodes that are not Money """
    +        ModelWithNonMoneyField(money=Money(100.0), desc="hundred").save()
    +        instance = ModelWithNonMoneyField.objects.filter(desc=F("desc")).get()
    +        self.assertEqual(instance.desc, "hundred")
    +
    +
     class InheritedModelTestCase(TestCase):
         """Test inheritence from a concrete model"""
    
    diff --git a/djmoney/tests/testapp/models.py b/djmoney/tests/testapp/models.py
    index 8a91882..aeb64c6 100644
    --- a/djmoney/tests/testapp/models.py
    +++ b/djmoney/tests/testapp/models.py
    @@ -51,6 +51,11 @@ class ModelWithChoicesMoneyField(models.Model):
         )
    
    
    +class ModelWithNonMoneyField(models.Model):
    +    money = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
    +    desc = models.CharField(max_length=10)
    +
    +
     class AbstractModel(models.Model):
         price1 = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
    
    FieldError: Cannot resolve keyword 'desc_currency' into field. Choices are: desc, id, money, money_currency
    
    opened by AlexRiina 15
  • Tests refactoring

    Tests refactoring

    Hello! I'm doing some refactorings & bugfixes to the current test suite. It is not completed yet, but I'll try to do my best here :) These are more like proposed changes, because the current test suite is made with different testing styles, if something is not working, etc. So, I want to discuss the changes and improve django-money testing suite :)

    Testing framework

    Current master uses Django runner, which doesn't run any tests at all and py.test. So, I decided to completely switch to py.test. Not so many features are used at this point, but it seems useful to me. Tox is still used for multi env testing, but with some simplifications and compatibility fixes.

    Compatibility and test builds

    • Django master is supported
    • Python 3.2 is supported completely
    • PyPy3 builds were added. It works for Django 1.5/1.6/1.7/1.8
    • Added test with django-money-rates and it works
    • Django 1.5.x on Python 3.2 is fixed with proper django-reversion version (new 1.7.2 version is available on PyPI now).

    Also coverage was added to test builds and I'll add codecov.io integration later. Some builds were excluded from build matrix, based on Python versions support in Django. E.g. Django 1.9 doesn't support Python 3.2/3.3.

    Found bugs and issues

    I've found some bugs during refactoring.

    1. Q objects generates different queries then regular kwargs passed to manager.filter() on Django 1.8+ It doesn't add currency part to WHERE clause. I've skipped equal parts (Python 2.7, Django 1.9)
    >>> print str(ModelWithTwoMoneyFields.objects.filter(Q(amount1__gt=F('amount2'))).query)
    ... WHERE "testapp_modelwithtwomoneyfields"."amount1" > ("testapp_modelwithtwomoneyfields"."amount2")
    >>> print str(ModelWithTwoMoneyFields.objects.filter(amount1__gt=F('amount2')).query)
    ... WHERE ("testapp_modelwithtwomoneyfields"."amount1" > ("testapp_modelwithtwomoneyfields"."amount2") AND "testapp_modelwithtwomoneyfields"."amount1_currency" = ("testapp_modelwithtwomoneyfields"."amount2_currency"))
    

    On Django versions prior to 1.8 queries are logically equal. 2. Multiplication & division on F objects. Addition and subtraction are implemented, but these operations are not. Later I'll create separate issues for these things.

    Cleanups

    Shell scripts for configs generation were removed because they was very out of date and current Tox & Travis CI configs are simple enough to be handled manually. All non-relevant extensions were removed from MANIFEST.in, also MANIFEST was removed as obsolete. .gitignore was also updated to cover all required stuff (coverage reports, uncovered eggs). six is not required as a dependency. The only usage of six is django.utils.six. Code was slightly pep8ified.

    New features

    Universal wheel config were added, now PyPI package holder can do python setup.py bdist_wheel and upload the universal wheel. Makefile. I've added that to simplify development. Now it is possible to clean up working dir, to see the coverage, to run the test suite with make. Isort was applied. Just isort -rc . and imports will be organized in the same way in every file.

    Questions

    1. Why there are test builds against py-moneyed==0.4? Latest version as requirement for django-money is not enough?
    2. About rounding. Consider this code:
    money = Money('100.0623456781123219')
    instance = ModelWithVanillaMoneyField.objects.create(money=money)
    # Now instance.money is not rounded
    retrieved = ModelWithVanillaMoneyField.objects.get(pk=instance.pk)
    # retrieved.money is rounded to 100.06
    

    Should instance.money be rounded?

    So, hope you'll find this PR interesting :) Discussion is welcome

    opened by Stranger6667 14
  • MinValueValidator for more than one currency

    MinValueValidator for more than one currency

    In the example it shows how to use MinValueValidator with one currency what if I have two currencies USD and GBP and I want to set min value for both of the currency for example min value USD 10 and GBP 100 how can I do that?

    from django.core.validators import MinValueValidator
    from django.db import models
    from djmoney.models.fields import MoneyField, MoneyPatched
    
    
    class BankAccount(models.Model):
        balance = MoneyField(max_digits=10, decimal_places=2, validators=[MinValueValidator(MoneyPatched(100, 'GBP'))])
    
    feature 
    opened by thormengkheang 13
  • Updating the currency of an existing object fails

    Updating the currency of an existing object fails

    We are trying to update the currency of an existing object (django-money 0.7.6), this fails because of a specific check in the set method, what is the idea of this check?

    def __set__(self, obj, value):
        if isinstance(value, tuple):
            value = Money(amount=value[0], currency=value[1])
        if isinstance(value, Money):
            obj.__dict__[self.field.name] = value.amount
            # we have to determine whether to replace the currency.
            # i.e. if we do the following:
            # .objects.get_or_create(money_currency='EUR')
            # then the currency is already set up, before this code hits
            # __set__ of MoneyField. This is because the currency field
            # has less creation counter than money field.
            obj_curr = obj.__dict__[self.currency_field_name]
            val_curr = str(value.currency)
            def_curr = str(self.field.default_currency)
            if obj_curr != val_curr:
                # in other words, update the currency only if it wasn't
                # changed before.
                if obj_curr == def_curr:   # <=== why ????
                setattr(
                    obj, self.currency_field_name,
                    smart_unicode(value.currency))
    
    opened by Hedde 13
  • Document all MoneyField options

    Document all MoneyField options

    #693 shows that it's a bit hard to grasp how to use defaults without reading the code. This could be fixed in the "Model usage" section of the docs by adding a reference to all MoneyField options.

    documentation good first issue 
    opened by benjaoming 0
  • add field generator for model bakery

    add field generator for model bakery

    Add field generator which ensures seamless model creation with model bakery.

    This doesn't create any additional dependency (except testing). It is used only if model_bakery is installed.

    opened by PetrDlouhy 0
  • Migration created after makemigrations with DEFAULT_AUTO_FIELD modified settings

    Migration created after makemigrations with DEFAULT_AUTO_FIELD modified settings

    An migration called "0002_alter_rate_id.py" created after running "makemigrations" with DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

    opened by ventur40 6
  • Currencies not restricted when `CURRENCIES` is set in `settings.py`

    Currencies not restricted when `CURRENCIES` is set in `settings.py`

    As the documentation states, one can restrict the currencies listed on the project by setting the CURRENCIES variable with a list of currency codes on settings.py. I have added the following line to settings.py:

    CURRENCIES = ["NOK"]
    

    However, when saving an object with another currency than NOK (e.g., USD), no ValidationError or other exception is raised. Thus, I have added a custom check in my model's clean_fields() method:

    class Claim(models.Model):
        amount = MoneyField(
            max_digits=12,
            decimal_places=4,
            default_currency="NOK",
            validators=[
                MinMoneyValidator(0),
                MaxMoneyValidator(99999999),
            ],
        )
    
        objects: ClaimManager = money_manager(ClaimManager())
    
        def clean_fields(self, *args: Any, **kwargs: Any):
            errors = {}
    
            # Amount currency
            if not self.amount_currency in settings.CURRENCIES:
                currency_string = ", ".join(settings.CURRENCIES)
    
                errors["amount_currency"] = [
                    _(f"Currency has to be one of {currency_string}")
                ]
    
            if len(errors) > 0:
                raise ValidationError(errors)
    
            # Check model validators:
            super().clean_fields(*args, **kwargs)
    

    Is this expected behavior? If so, what then does the documentation mean by one can restrict the currencies listed on the project?

    opened by simensol 0
  • no changes detected when field previously had CurrencyField without null constraint.

    no changes detected when field previously had CurrencyField without null constraint.

    Hello, I've found a version difference between 2.x and 3.0 that has lead to an error for me. I have a field on a model which is nullable. When it was created, the migration was this:

    migrations.AddField(
                model_name='checkoutline',
                name='price_per_unit_at_submit',
                field=djmoney.models.fields.MoneyField(decimal_places=2, max_digits=19, null=True),
            ),
            migrations.AddField(
                model_name='checkoutline',
                name='price_per_unit_at_submit_currency',
                field=djmoney.models.fields.CurrencyField(choices=[('USD', 'US Dollar')], default='XYZ', editable=False, max_length=3),
            ),
    

    But upon upgrading to 3.0, I'm now getting errors where this field has a non-null constraint. This was not an issue before. I can understand the change but I would expect it to be fixed by running ./manage.py makemigrations (as a few other fields were). I can't seem to see why this currency field isn't being migrated, and instead I only get "no changes detected".

    Potentially related but I don't think quite the same: https://github.com/django-money/django-money/issues/530 I believe it's coming from or related to https://github.com/django-money/django-money/issues/672

    Likely originating from https://github.com/django-money/django-money/pull/638

    opened by nstephenh 7
Releases(3.0)
  • 3.0(Jun 20, 2022)

    Changed

    • Update py-moneyed to 2.0. #638
    • Remove the deprecated Money.decimal_places_display property and argument. #638
    • Remove the deprecated CURRENCY_DECIMAL_PLACES_DISPLAY setting. #638
    • Null constraint on an implicit CurrencyField is now declared from null=... argument to MoneyField. #638

    Fixed

    • Improve the internal check for whether a currency is provided #657
    • Fix test suite for django main branch #657
    • MoneyField raises TypeError when default contains a valid amount but no currence, i.e. Money(123, None). #661
    • MoneyField supports default of type bytes #661

    Added

    • Add support for Django 4.0 and 4.1.
    • Add support for Python 3.10.

    Removed

    • Drop support for Django 3.1.
    • Drop support for Python 3.6.
    Source code(tar.gz)
    Source code(zip)
  • 2.1.1(Jan 2, 2022)

    Changed

    • Renamed master branch to main (@benjaoming)

    Fixed

    • Make Django REST Framework integration always raise lower-level errors as ValidationError. #601, #637 (@flaeppe)
    • False positives in Migration changes, improvements to MoneyField.deconstruct. #646, #648 (@flaeppe)
    Source code(tar.gz)
    Source code(zip)
  • 2.1(Sep 17, 2021)

  • 2.0.3(Sep 4, 2021)

  • 2.0.2(Sep 4, 2021)

  • 2.0.1(Jul 9, 2021)

  • 2.0(May 23, 2021)

    Added

    • New setting CURRENCY_CODE_MAX_LENGTH configures default max_length for MoneyField and exchange app models.

    Changed

    • BREAKING: Update py-moneyed to >=1.2,<2. It uses babel to format Money, which formats it differently than py-moneyed<1. #567 (@antonagestam)

    Deprecated

    • Money.decimal_places_display will be removed in django-money 3.0.
    • CURRENCY_DECIMAL_PLACES_DISPLAY will be removed in django-money 3.0.
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Feb 4, 2021)

  • 1.3(Jan 10, 2021)

    Added

    • Improved localization: New setting CURRENCY_DECIMAL_PLACES_DISPLAY configures decimal places to display for each configured currency. #521 (@wearebasti)

    Changed

    • Set the default value for models.fields.MoneyField to NOT_PROVIDED. (@tned73)

    Fixed

    • Pin pymoneyed<1.0 as it changed the repr output of the Money class. (@Stranger6667)
    • Subtracting Money from moneyed.Money. Regression, introduced in 1.2. #593 (@Stranger6667)
    • Missing the right Money.decimal_places and Money.decimal_places_display values after some arithmetic operations. #595 (@Stranger6667)
    Source code(tar.gz)
    Source code(zip)
  • 1.2.2(Dec 29, 2020)

    Fixed

    • Confusing "number-over-money" division behavior by backporting changes from py-moneyed. #586 @wearebasti
    • AttributeError when a Money instance is divided by Money. #585 @niklasb
    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Nov 29, 2020)

  • 1.2(Nov 26, 2020)

    Added

    • Django 3.1 support.

    Fixed

    • Resulting Money object from arithmetics (add / sub / ...) inherits maximum decimal_places from arguments. #522 (@wearebasti)
    • DeprecationWarning related to the usage of cafile in urlopen. #553 @Stranger6667
    Source code(tar.gz)
    Source code(zip)
  • 1.1(Jun 16, 2020)

    Fixed

    • Optimize money operations on MoneyField instances with the same currencies. #541 @horpto

    Added

    • Support for Money type in QuerySet.bulk_update() #534 @satels
    Source code(tar.gz)
    Source code(zip)
  • 1.0(Nov 8, 2019)

    Added

    • Support for money descriptor customization. (@Stranger6667)
    • Fix order_by() not returning money-compatible queryset #519 (@lieryan)
    • Django 3.0 support

    Removed

    • Support for Django 1.8 & 2.0. (@Stranger6667)
    • Support for Python 2.7. #515 (@benjaoming)
    • Support for Python 3.4. (@Stranger6667)
    • MoneyPatched, use djmoney.money.Money instead. (@Stranger6667)

    Fixed

    • Support instances with decimal_places=0 #509 (@fara)
    Source code(tar.gz)
    Source code(zip)
  • 0.15(Jun 22, 2019)

  • 0.14.4(Jan 10, 2019)

  • 0.14.3(Aug 14, 2018)

  • 0.14.2(Jul 23, 2018)

  • 0.14.1(Jul 23, 2018)

    Added

    • Support for indirect rates conversion through maximum 1 extra step (when there is no direct conversion rate: converting by means of a third currency for which both source and target currency have conversion rates). #425 (@Stranger6667, @77cc33)

    Fixed

    • Error was raised when trying to do a query with a ModelWithNullableCurrency. #427 (@Woile)
    Source code(tar.gz)
    Source code(zip)
  • 0.14(Jun 9, 2018)

    Added

    • Caching of exchange rates. #398 (@Stranger6667)
    • Added support for nullable CurrencyField. #260 (@Stranger6667)

    Fixed

    • Same currency conversion getting MissingRate exception #418 (@humrochagf)
    • TypeError during templatetag usage inside a for loop on Django 2.0. #402 (@f213)

    Removed

    • Support for Python 3.3 #410 (benjaoming)
    • Deprecated choices argument from djmoney.forms.fields.MoneyField. Use currency_choices instead. (@Stranger6667)
    Source code(tar.gz)
    Source code(zip)
  • 0.13.5(May 19, 2018)

  • 0.13.4(May 19, 2018)

  • 0.13.3(May 12, 2018)

  • 0.13.2(Apr 16, 2018)

    Added

    • Django Admin integration for exchange rates. #392 (Stranger6667)

    Fixed

    • Exchange rates. TypeError when decoding JSON on Python 3.3-3.5. #399 (kcyeu)
    • Managers patching for models with custom Meta.default_manager_name. #400 (Stranger6667)
    Source code(tar.gz)
    Source code(zip)
  • 0.13.1(Apr 7, 2018)

  • 0.13(Apr 7, 2018)

    Added

    • Currency exchange. #385 (@Stranger6667)

    Removed

    • Support for django-money-rates. #385 (@Stranger6667)
    • Deprecated Money.__float__ which is implicitly called on some sum() operations #347. (@jonashaag)

    See release notes about replacing django-money-rates here:

    http://django-money.readthedocs.io/en/stable/changes.html

    Source code(tar.gz)
    Source code(zip)
  • 0.12.3(Dec 13, 2017)

  • 0.12.2(Dec 13, 2017)

    Fixed

    • Django master branch compatibility. #361 (@Stranger6667)
    • Fixed get_or_create for models with shared currency. #364 (@Stranger6667)

    Changed

    • Removed confusing rounding to integral value in Money.repr. #366 (@Stranger6667, @evenicoulddoit)
    Source code(tar.gz)
    Source code(zip)
  • 0.12.1(Nov 20, 2017)

    See also: http://django-money.readthedocs.io/en/stable/changes.html

    Fixed

    • Fixed migrations on SQLite. #139, #338 (@Stranger6667)
    • Fixed Field.rel.to usage for Django 2.0. #349 (@richardowen)
    • Fixed Django REST Framework behaviour for serializers without *_currency field in serializer’s Meta.fields. #351 (@elcolie, @Stranger6667)
    Source code(tar.gz)
    Source code(zip)
  • 0.12(Oct 22, 2017)

    See also: http://django-money.readthedocs.io/en/stable/changes.html

    Added

    • Ability to specify name for currency field. #195 (@Stranger6667)
    • Validators for MoneyField. #308 (@Stranger6667)

    Changed

    • Improved Money support. Now django-money fully relies on pymoneyed localization everywhere, including Django admin. #276 (@Stranger6667)
    • Implement __html__ method. If used in Django templates, an Money object's amount and currency are now separated with non-breaking space (&nbsp;). #337 (@jonashaag)

    Deprecated

    • djmoney.models.fields.MoneyPatched and moneyed.Money are deprecated. Use djmoney.money.Money instead.

    Fixed

    • Fixed model field validation. #308 (@Stranger6667).
    • Fixed managers caching for Django >= 1.10. #318 (@Stranger6667).
    • Fixed F expressions support for in lookups. #321 (@Stranger6667).
    • Fixed money comprehension on querysets. #331 (@Stranger6667, @jaavii1988).
    • Fixed errors in Django Admin integration. #334 (@Stranger6667, @adi-).

    Removed

    • Dropped support for Python 2.6 and 3.2. (@Stranger6667)
    • Dropped support for Django 1.4, 1.5, 1.6, 1.7 and 1.9. (@Stranger6667)
    Source code(tar.gz)
    Source code(zip)
Simply integrate Summernote editor with Django project.

django-summernote Summernote is a simple WYSIWYG editor. django-summernote allows you to embed Summernote into Django very handy. Support admin mixins

Summernote 936 Jan 02, 2023
Official Python agent for the Elastic APM

elastic-apm -- Elastic APM agent for Python This is the official Python module for Elastic APM. It provides full out-of-the-box support for many of th

elastic 369 Jan 05, 2023
Source files for a free pyRevit toolbar.

pyRoovit (WIP) What is this? PyRoovit is/will be a toolbar for the use with pyRevit built by Gavin Crump (aka Aussie BIM Guru). Having used and taught

Gavin Crump 11 Nov 10, 2022
Django + NextJS + Tailwind Boilerplate

django + NextJS + Tailwind Boilerplate About A Django project boilerplate/templa

Shayan Debroy 3 Mar 11, 2022
A test microblog project created using Django 4.0

django-microblog This is a test microblog project created using Django 4.0. But don't worry this is a fully working project. There is no super-amazing

Ali Kasimoglu 8 Jan 14, 2022
This is a personal django website for forum posts

Django Web Forum This is a personal django website for forum posts It includes login, registration and forum posts with date time. Tech / Framework us

5 May 12, 2022
Django channels basic chat

Django channels basic chat

Dennis Ivy 41 Dec 24, 2022
A slick ORM cache with automatic granular event-driven invalidation.

Cacheops A slick app that supports automatic or manual queryset caching and automatic granular event-driven invalidation. It uses redis as backend for

Alexander Schepanovski 1.7k Jan 03, 2023
Application made in Django to generate random passwords as based on certain criteria .

PASSWORD GENERATOR Welcome to Password Generator About The App Password Generator is an Open Source project brought to you by Iot Lab,KIIT and it brin

IoT Lab KIIT 3 Oct 21, 2021
Developer-friendly asynchrony for Django

Django Channels Channels augments Django to bring WebSocket, long-poll HTTP, task offloading and other async support to your code, using familiar Djan

Django 5.5k Dec 29, 2022
Django friendly finite state machine support

Django friendly finite state machine support django-fsm adds simple declarative state management for django models. If you need parallel task executio

Viewflow 2.1k Dec 31, 2022
Exemplo de biblioteca com Django

Bookstore Exemplo de biblioteca feito com Django. Este projeto foi feito com: Python 3.9.7 Django 3.2.8 Django Rest Framework 3.12.4 Bootstrap 4.0 Vue

Regis Santos 1 Oct 28, 2021
This is raw connection between redis server and django python app

Django_Redis This repository contains the code for this blogpost. Running the Application Clone the repository git clone https://github.com/xxl4tomxu9

Tom Xu 1 Sep 15, 2022
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Rishav Sinha 4 Nov 18, 2021
Full featured redis cache backend for Django.

Redis cache backend for Django This is a Jazzband project. By contributing you agree to abide by the Contributor Code of Conduct and follow the guidel

Jazzband 2.5k Jan 03, 2023
Keep track of failed login attempts in Django-powered sites.

django-axes Axes is a Django plugin for keeping track of suspicious login attempts for your Django based website and implementing simple brute-force a

Jazzband 1.1k Dec 30, 2022
Loguru is an exceeding easy way to do logging in Python

Django Easy Logging Easy Django logging with Loguru Loguru is an exceeding easy way to do logging in Python. django-easy-logging makes it exceedingly

Neutron Sync 8 Oct 17, 2022
Django app for handling the server headers required for Cross-Origin Resource Sharing (CORS)

django-cors-headers A Django App that adds Cross-Origin Resource Sharing (CORS) headers to responses. This allows in-browser requests to your Django a

Adam Johnson 4.8k Jan 03, 2023
Django API that scrapes and provides the last news of the city of Carlos Casares by semantic way (RDF format).

"Casares News" API Api that scrapes and provides the last news of the city of Carlos Casares by semantic way (RDF format). Usage Consume the articles

Andrés Milla 6 May 12, 2022
Simple Login Logout System using Django, JavaScript and ajax.

Djanog-UserAuthenticationSystem Technology Use #version Python 3.9.5 Django 3.2.7 JavaScript --- Ajax Validation --- Login and Logout Functionality, A

Bhaskar Mahor 3 Mar 26, 2022