Giter Site home page Giter Site logo

django-money's Introduction

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: 2.2, 3.2, 4.0, 4.1, 4.2
  • Python versions supported: 3.7, 3.8, 3.9, 3.10, 3.11
  • PyPy versions supported: PyPy3 (for Django <= 4.0)

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 v1.2 (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

The default currency code length is 3 but you can change it with the CURRENCY_CODE_MAX_LENGTH setting.

Caution: this setting also affects the initial migration of the exchange plugin, so changing it after running the initial migration has no effect. (You'd need to manage migrate exchange zero and migrate again if you want to change it).

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;

Constructing form data

The default ModelForm class will use a form field (djmoney.forms.fields.MoneyField) that is constructed of two separate fields for amount and currency.

If you need to feed data directly to such a form (for instance if you are writing a test case), then you need to pass amount and currency like this:

# models.py
class Product(models.Model):
    price = MoneyField(
        max_digits=14,
        decimal_places=2,
        default_currency='EUR'
    )

# forms.py
class ProductForm(ModelForm):
    class Meta:
        model = Product
        fields = ["price"]

# tests.py

# construct the form in your test case
form = ProductForm({'price_0': 10, 'price_1': 'EUR'})

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 simply add these few lines to your settings.py file:

import moneyed

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

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

Note on serialization

Django-money provides a custom deserializer, it is not registered by default so you will have to actively register it in your settings.py.

SERIALIZATION_MODULES = {"json": "djmoney.serializers"}

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:

CELERY_BEAT_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://guest@localhost//')


@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.

django-money's People

Contributors

akumria avatar antonagestam avatar ashleyh avatar ayushin avatar benjaoming avatar browniebroke avatar davidszotten avatar edwinlunando avatar fara avatar felixxm avatar fizista avatar flaeppe avatar glarrain avatar humrochagf avatar jakewins avatar kjagiello avatar mariocesar avatar melvyn-apryl avatar plumdog avatar reinbach avatar richardowen avatar satels avatar sdarmofal avatar spookylukey avatar stranger6667 avatar tned73 avatar toudi avatar washeck avatar wearebasti avatar willhcr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-money's Issues

py-moneyed version

The readme states:

3.2.x_, 3.3.x_ (* These versions of Python work only for the moment when you install the following fork "py-moneyed" https://github.com/fizista/py-moneyed/tree/python3 )

Is this still the case? The upstream version of py-moneyed states in its readme:

The py-moneyed package has been tested with Python 2.6, 2.7, 3.2, 3.3 and PyPy 2.1.

And, according to the commit history, that comment about the upstream (limist's) supporting Python 3.x comes from a commit authored by the author of the version you recommend in your readme, so I'm wondering if the applicable fixes from fizista's version were merged into limist's version.

__get_current_locale does not return the locale but the language code

Hi,

I had a problem to use the formatting made for the locale "en_US". Note the "_" instead of a "-"
The locale is defined on py-moneyed:
https://github.com/limist/py-moneyed/blob/9ee56146ce67872f12509c2eb62da59701c51f0a/src/moneyed/localization.py#L326

However, in the __get_current_locale method, you use

locale = translation.get_language()

which actually returns the language code (with the "-").
Then py-moneyed cannot find the given locale "en-us" in its configuration and uses the default one for USD.

There is a method in django translations utils to return a locale code based on the language code:
https://github.com/django/django/blob/master/django/utils/translation/trans_real.py#L71

Maybe you could use it to really get a locale to pass to the formatter ?

Thanks

Magic money querying ability does not chain

Take, for example, VanillaMoneyFieldTestCase.testCurrencySearch. In the line

shouldBeEmpty = ModelWithVanillaMoneyField.objects.filter(money__lt=otherMoney)

if you replace objects with objects.all().all(), then the test fails.

Basically, you end up with a vanilla QuerySet that does not 'understand money'. However, there is no warning - the query appears to succeed, but returns the wrong answer. This hugely violates expectations - people normally chain ORM methods a lot, and don't expect them to suddenly behave differently because of an earlier filter or order_by etc.

This is a serious flaw in this feature, IMO. If it can't be fixed, I'd recommend pulling it completely. The alternative method (explicit filtering against the _currency field) will always work.

To solve it, I think you need a custom QuerySet subclass. It is this that should have understands_money applied. And you then need to do patching that will return this QuerySet rather than anything else. I may be able to work something up for this.

CURRENCY_CHOICES includes a testing item.

In the currency drop down menu, I see an item entitled ('XTS', 'Codes specifically reserved for testing purposes'). This should not be available for my users to see or select as a valid currency.

Deserialization Error

I get an error when using ./manage.py dumpdata then loaddata.

DeserializationError: [u"'US$1,021.90' value must be a decimal number."]

Templatetags missing

I just installed django-money using pip install django-money and I am getting the following error:

'djmoney' is not a valid tag library: Template library djmoney not found, tried django.templatetags.djmoney,django.contrib.admin.templatetags.djmoney,django.contrib.staticfiles.templatetags.djmoney,compressor.templatetags.djmoney,debug_toolbar.templatetags.djmoney

I've added djmoney into INSTALLED_APPS and restarted django server.

When I check site-packages/djmoney I can see only forms, models and tests folders - no templatetags folder.

I've also tried to reinstall using github repo like this without luck:

pip install git+https://github.com/jakewins/django-money.git --upgrade

MoneyField currency_choices confusion

Current MoneyField __init__ accepts both choices and currency_choices keyword args.

choices, however is completely ignored (not withstanding a line that assigns to it - the value is never used).

On the other hand currency_choices is used (it is passed to the ChoiceField, but if you use it, you get a warning saying that it is deprecated!

It's not obvious to me what the intended behaviour should be. Either it should issue the warning for choices not currency_choices, or it should actually use choices.

Aggregate

Is it possible to sum Money fields even if values have set different currencies?

account = BankAccount(balance=Money(10, USD))
swissAccount = BankAccount(balance=Money(10, CHF))

BankAccount.objects.aggregate(total=Sum('balance'))

Ignoring default currency, creating new records with XYZ, latest package 0.3.4 broken

Hello.

Have multiple issues. Firstly upgraded to 0.3.3.2 package. I have defined default currency:

price_list = MoneyField(max_digits=10, decimal_places=2, default_currency='EUR')

And also in settings.py:

CURRENCIES = ('EUR', )

Despite of this, all new records in db are created with currency XYZ.

Then I found out that new 0.3.4 package is online, I hoped that will fix these issues and package tarball is empty, no setup.py and sources:

screenshot from 2013-11-26 18 16 59

Exception after upgrading to Django 1.6: Invalid money input, expected amount and currency, got: .

After recently upgrading to django 1.6, the admin form field for money has changed from a text box for the value and a dropdown for the currency to just a single textbox with a number spinner:

borked-django-money

When I try to save the form that has a money field, I get the following exception:

Exception at (url redacted)
Invalid money input, expected amount and currency, got: .
Request Method: POST
Request URL:    http://localhost:8000/admin/(url redacted)/
Django Version: 1.6
Exception Type: Exception
Exception Value:    
Invalid money input, expected amount and currency, got: .
Exception Location: /home/(username)/.virtualenvs/(project)/lib/python2.7/site-packages/djmoney/forms/fields.py in to_python, line 26
Python Executable:  /home/(username)/.virtualenvs/(project)/bin/python
Python Version: 2.7.3
Python Path:    
['/(projectpath)/',
 '/home/(username)/.virtualenvs/(projectname)/lib/python27.zip',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/plat-cygwin',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/lib-tk',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/lib-old',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/lib-dynload',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-cygwin',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages',
 '/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/PIL']

Here's the full stack trace:

Environment:


Request Method: POST
Request URL: http://localhost:8000/admin/(objects)/5/

Django Version: 1.6
Python Version: 2.7.3


Traceback:
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  430.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  52.         response = view_func(request, *args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  198.             return view(request, *args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  29.             return bound_func(*args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  99.                     response = view_func(request, *args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  25.                 return func(self, *args2, **kwargs2)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/db/transaction.py" in inner
  339.                 return func(*args, **kwargs)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/contrib/admin/options.py" in change_view
  1209.             if form.is_valid():
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/forms/forms.py" in is_valid
  129.         return self.is_bound and not bool(self.errors)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/forms/forms.py" in errors
  121.             self.full_clean()
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/forms/forms.py" in full_clean
  273.         self._clean_fields()
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/forms/forms.py" in _clean_fields
  288.                     value = field.clean(value)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/django/forms/fields.py" in clean
  148.         value = self.to_python(value)
File "/home/(username)/.virtualenvs/(projectname)/lib/python2.7/site-packages/djmoney/forms/fields.py" in to_python
  26.                 "Invalid money input, expected amount and currency, got: %s." % value)

Exception Type: Exception at /admin/(object)/5/
Exception Value: Invalid money input, expected amount and currency, got: .

Here's the line where I define the money field:

msrp = MoneyField('MSRP',max_digits=6,decimal_places=2,default_currency='USD',blank=True,null=True)

Any ideas what's causing this and how I can fix it besides reverting back to Django 1.5?

Abstract classes and MoneyField

because of this commit: 954c895

money fields defined in abstract classes aren't populated in the inherited classes.

i commented this part of code out, and it works without any problems. any particular reason why the code is commented? or just historical one?

regards,
toudi

Turkish Money Symbol

Turkish currency symbol is ₺ in last 2 years (HTML: ₺) but it is still TL in that probject. This also should be updated.

Error in South Data Migration with Money field

I'm writing a south datamigration to move a Money field (called rate) on one model to another. When I run the migration, I get the below error:

DatabaseError: multiple assignments to same column "rate_currency"

I spoke with the south guys on irc and they said that the Money field is injecting a second copy of the field (rate_currency) and that is what's causing the issue.

Has anyone had a similar issue?

installation with django 1.7

I am starting a new django project with django 1.7. When I tried to install django-money it uninstalled django 1.7 and installed the latest 1.6.

Taking a look at the setup.py here, it says "django > 1.4, django < 1.7". Is django-money really incompatible with django 1.7, or the requirements stanza on setup.py is just outdated?

MoneyField and Django 1.7 migrations

I am trying to upgrade a Django 1.6 application to 1.7pre, and ran into the following while trying to makemigrations for a model with a MoneyField. The helpful URL at the end of the error leads me to believe that I should be helping Django serialize MoneyField's default value with deconstruct(). But does anyone else know of how I should proceed with this?

Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "lib/python2.7/site-packages/django/core/management/base.py", line 337, in execute
    output = self.handle(*args, **options)
  File "lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 115, in handle
    self.write_migration_files(changes)
  File "lib/python2.7/site-packages/django/core/management/commands/makemigrations.py", line 143, in write_migration_files
    migration_string = writer.as_string()
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 124, in as_string
    operation_string, operation_imports = OperationWriter(operation).serialize()
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 76, in serialize
    arg_string, arg_imports = MigrationWriter.serialize(item)
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 229, in serialize
    item_string, item_imports = cls.serialize(item)
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 290, in serialize
    return cls.serialize_deconstructed(path, args, kwargs)
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 205, in serialize_deconstructed
    arg_string, arg_imports = cls.serialize(arg)
  File "lib/python2.7/site-packages/django/db/migrations/writer.py", line 349, in serialize
    raise ValueError("Cannot serialize: %r\nThere are some values Django cannot serialize into migration files.\nFor more, see https://docs.djangoproject.com/en/dev/topics/migrations/#migration-serializing" % value)
ValueError: Cannot serialize: 0 XYZ
There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/dev/topics/migrations/#migration-serializing

Duplicate column name

Environment:
django 1.4.3
pip install django-money

Description:

Application with two models

class ModelA(models.Model):
    price = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
    class Meta:
        abstract = True

class ModelB(ModelA):
    name = models.CharField(max_length=50)

produces exception on syncdb command: django.db.utils.DatabaseError: duplicate column name: price_currency

The reason must be abstract base class. If ModelA is not abstract, everything works.

Similar happens at runtime if database is updated with south (which works with abstract base class).

Type error is thrown if query has a SQLEvaluator instance in the query tree

Base class Field handles SQLEvaluator, while the derived MoneyField does not.

This is the patch that was applied to MoneyField in fields.py:

def get_db_prep_lookup(self, lookup_type, value, connection,
prepared=False):
if not lookup_type in SUPPORTED_LOOKUPS:
raise NotSupportedLookup(lookup_type)
if isinstance(value, Money):
value = value.amount
#value = self.get_db_prep_save(value, connection)
return super(MoneyField, self).get_db_prep_lookup(lookup_type, value,
connection, prepared)

On syncdb I get ValueError: need more than 1 value to unpack

I'm getting the following when I run python manage.py syncdb:

File "/home/ubuntu/globalspiritualevents/event/models.py", line 206, in Event
cost = MoneyField(max_digits=10, decimal_places=2, default_currency='USD', default='0.00', blank=True, null=True)

File "/home/ubuntu/globalspiritualevents/local/lib/python2.7/site-packages/djmoney/models/fields.py"
amount, currency = default.split(" ")

ValueError: need more than 1 value to unpack

Important: I have not changed this line in over a year. The only difference is that I ported the code from Windows to Ubuntu.

I'm using
Amazon Ubuntu 13.10
python 2.7.5+
django==1.4.3
django-money==0.4.1
I've tried using py-moneyed==0.4.1, as well as, 0.5.0, but the error persists.

Any advice or solutions are welcome.

Formatting not working

The price formatting is not working for all currencies for me.

See admin screenshot attached:
money_formatter

What am I doing wrong? Thank you.

Lacking clarity in docs about USE_L10N and formatting

1.- The readme says:

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.

this means that if USE_L10N=True I do not need {% money_localize money_object %} and i can just use {{ money_object }} or this means that {% money_localize money_object %} works only if USE_L10N=True??

2.- Whats the correct way of handling empty values in forms?. Originally I wanted to default to None but aparently this is not possible so my default value es 0. If a user sends and empty amount field should I change it to 0 in the view?

3.- My money field defines decimal_places=0 and I want to print the money field in the templates without the trailing .00 (ej: CLP$100,002.00 -> CLP$100,002), do you know a way for doing this?

4.- I'm not sure if the localization is working for me: {% money_localize money_object %} prints CLP$100,002.00 but in my country (Chile) the thousands separator is '.' and the decimal separator is ',' (so it should print CLP$100.002,00)

That's is, how you can see I'm having some noob problems. Thanks for your time in advance.

Update Readme related to pl-moneyed branch

The Readme reads:

Django-money currently needs a special version of py-moneyed to work (2011-05-15).
This will be resolved as soon as my fork of it is approved and merged into py-moneyed main branch.

Looking over limist/py-moneyed closed issues, this seems to already have been merged. Could you clarify if there are still any issues with using the official repo?

Error using at leas two MoneyField in a model class

When I use in django 1.3 two MoneyField in one model class, it appear the next error when make manage sql myclass:

Traceback (most recent call last):
File "./manage.py", line 14, in
execute_manager(settings)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/init.py", line 438, in execute_manager
utility.execute()
File "/usr/local/lib/python2.7/dist-packages/django/core/management/init.py", line 379, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 191, in run_from_argv
self.execute(_args, *_options.dict)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 219, in execute
self.validate()
File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 249, in validate
num_errors = get_validation_errors(s, app)
File "/usr/local/lib/python2.7/dist-packages/django/core/management/validation.py", line 36, in get_validation_errors
for (app_name, error) in get_app_errors().items():
File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 146, in get_app_errors
self._populate()
File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 61, in _populate
self.load_app(app_name, True)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 78, in load_app
models = import_module('.models', app_name)
File "/usr/local/lib/python2.7/dist-packages/django/utils/importlib.py", line 35, in import_module
import(name)
File "/home/juanfe/Vichara/liquidity/Web/bids/models.py", line 5, in
class Bids(models.Model):
File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 96, in new
new_class.add_to_class(obj_name, obj)
File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 216, in add_to_class
value.contribute_to_class(cls, name)
File "/usr/local/lib/python2.7/dist-packages/djmoney/models/fields.py", line 96, in contribute_to_class
cls.objects = money_manager(cls._default_manager)
AttributeError: type object 'Bids' has no attribute '_default_manager'

The error is solved if I change the line 96 in fields.py, where say _default_manager by objects:

96c96

< cls.objects = money_manager(cls._default_manager)

        cls.objects = money_manager(cls.objects)

Regards

South support

When I created the migration with south, the migration file reads :

    db.add_column('coop_local_paymentmodality', 'price', self.gf('djmoney.models.fields.MoneyField')(default='0.0', max_digits=10, decimal_places=2, default_currency=EUR), keep_default=False)

which triggers this error

ValueError: Cannot successfully create field 'price' for model 'paymentmodality': name 'EUR' is not defined.

ImportError: No module named serializers

I get this when I add a MoneyField to a model and then do a south migration or even simply call manage.py validate.

This is the field I'm adding.

cost = MoneyField(max_digits=10, decimal_places=2, default_currency='ZAR')

MoneyField does not handle localization

A comma within the string causes an exception to be thrown when passing the string as is to Money, which tries to create a Decimal from it.
Decimal cannot handle a string formatted as follows: 1,000
Following is the patch that was applied to fix the problem in widgets.py

def value_from_datadict(self, data, files, name):
if name not in data:
return None
amount, currency = data.get(name), data.get(get_currency_field_name(name))
if isinstance(amount, Money):
return amount
if isinstance(amount, str):
amount = amount.replace(',','')
return Money(amount=amount, currency=currency)

Currency symbol not visible for some currencies in Django Admin

Currency symbol is not visible for some currencies (for example PLN or EUR).

See django admin screenshot attached:
money_formatter

Expected behaviour:
image

I think, it is a unicode bug, because this workaround works (but it disables ordering):

def price_unicode(self, obj):
    return u'%s' % obj.price

Tested with both USE_L10N = True and False as well.

django 1.7 support?

Hi,

Does this app fully support Django 1.7?
I am trying django-money==0.4.2 and there is a Deprecated warning:

/venv/local/lib/python2.7/site-packages/djmoney/models/managers.py:87: RemovedInDjango18Warning: `BaseManager.get_query_set` is deprecated, use `get_queryset` instead.
  return add_money_comprehension_to_queryset(old_get_query_set(*args, **kwargs))

It's not a big issue since it's just a warning but I am wondering whether there is something else I should be aware of when using it with django 1.7? I see there is another open issue #91 related to the installation with django 1.7 but is there anything else?

Thanks

get_db_prep_save() got multiple values for keyword argument 'connection'

Environment:

Request Method: GET
Request URL: http://127.0.0.1:8000/new/100/

Django Version: 1.3.1
Python Version: 2.6.6
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'money_test']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware')

Traceback:
File "C:\Python26\lib\site-packages\django\core\handlers\base.py" in get_response

  1.                     response = callback(request, _callback_args, *_callback_kwargs)
    
    File "C:\Joinerysoft\Development\money_test..\money_test\moneh\views.py" in new
  2. new_account.save()
    
    File "C:\Python26\lib\site-packages\django\db\models\base.py" in save
  3.     self.save_base(using=using, force_insert=force_insert, force_update=force_update)
    
    File "C:\Python26\lib\site-packages\django\db\models\base.py" in save_base
  4.                     for f in meta.local_fields if not isinstance(f, AutoField)]
    
    File "C:\Python26\lib\site-packages\django\db\models\fields\subclassing.py" in inner
  5.         return func(_args, *_kwargs)
    
    File "C:\Python26\lib\site-packages\djmoney\models\fields.py" in get_db_prep_save
  6.     return super(MoneyField, self).get_db_prep_save(value, connection)
    
    File "C:\Python26\lib\site-packages\django\db\models\fields\subclassing.py" in inner
  7.         return func(_args, *_kwargs)
    

Exception Type: TypeError at /new/100/
Exception Value: get_db_prep_save() got multiple values for keyword argument 'connection'

Error creating object with blank MoneyField

I have a MoneyField on a model and I want to make the population of that field optional; i.e., allow for null/blank values. When I create an instance of the model, I get the following error:

Traceback (most recent call last):
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/contrib/admin/options.py", line 366, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/views/decorators/cache.py", line 89, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/contrib/admin/sites.py", line 196, in inner
    return view(request, *args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/utils/decorators.py", line 25, in _wrapper
    return bound_func(*args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/utils/decorators.py", line 91, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/utils/decorators.py", line 21, in bound_func
    return func(self, *args2, **kwargs2)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/db/transaction.py", line 209, in inner
    return func(*args, **kwargs)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1053, in change_view
    if all_valid(formsets) and form_validated:
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/formsets.py", line 372, in all_valid
    if not formset.is_valid():
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/formsets.py", line 269, in is_valid
    err = self.errors
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/formsets.py", line 247, in _get_errors
    self.full_clean()
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/formsets.py", line 290, in full_clean
    self._errors.append(form.errors)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/forms.py", line 115, in _get_errors
    self.full_clean()
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/forms.py", line 270, in full_clean
    self._clean_fields()
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/forms.py", line 287, in _clean_fields
    value = field.clean(value)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/django/forms/fields.py", line 153, in clean
    value = self.to_python(value)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/djmoney/forms/fields.py", line 26, in to_python
    return Money(amount=amount, currency=currency)
  File "/Volumes/stout/dev/spothero.com/lib/python2.7/site-packages/moneyed/classes.py", line 59, in __init__
    amount = Decimal(str(amount))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.py", line 548, in __new__
    "Invalid literal for Decimal: %r" % value)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.py", line 3844, in _raise_error
    raise error(explanation)
decimal.InvalidOperation: Invalid literal for Decimal: 'None'
'''

Can't create MoneyField with null/None as default value

There are two issues here:

  • MoneyField provides a default value of 0.0 (float), unlike DecimalField which doesn't. This means that a nullable MoneyField doesn't get the default value of None, unlike DecimalField
  • MoneyField actively rejects it if you pass default=None - it requires a Money instance.

I'm happy to put together a patch for this, if it would be accepted, with the following behaviour:

  • the restriction that stops default from being None is lifted
  • if the field is nullable (null=True passed), then the default should be None, not zero. Otherwise, the current default of zero will be used.

This is slightly backwards incompatible, but only for people using nullable MoneyFields.

PIP / PyPi install issue

Hey there,

When installing via pip, it does not install all the correct files. It doesn't include the sireralizers or tests. It includes the following:

init.py
forms/
init.py
fields.py
widgets.py
models/
init.py
fields.py
managers.py

I'm installing it by doing: pip install django-money

Am I doing it wrong or is this a bug?

Thanks.

has_changed not working

@spookylukey would you be able to have a look at the test failure that I'm getting from 1e09181 ?

-> % ./runtests.py 
Creating test database for alias 'default'...
........F
======================================================================
FAIL: testChangedData (djmoney.tests.form_tests.MoneyFormTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/media/archive/documents/code/django-money/djmoney/tests/form_tests.py", line 56, in testChangedData
    self.assertEquals(form.changed_data, [])
AssertionError: Lists differ: ['money'] != []

First list contains 1 additional elements.
First extra element 0:
money

- ['money']
+ []

----------------------------------------------------------------------
Ran 9 tests in 0.006s

FAILED (failures=1)
Destroying test database for alias 'default'...

Basestring is not defined. Python3

Two issues:

In forms/fields.py, we are matching the values agains tuple, if the value list type it fails.

        if not isinstance(value, tuple) or len(value) != 2:
            raise Exception(
                "Invalid money input, expected amount and currency, got: %s." % value)

If changed to:

        if not isinstance(value, (tuple, list)) or len(value) != 2:

Works correctly as it consider the case of a list value. Later in the same scope, the following exception is raised

global name 'basestring' is not defined

forms/field.py line 32

        if not isinstance(currency, basestring):
            raise forms.ValidationError(
                _("Unrecognized currency type '%s'." % currency))

Updating the code above to:

        from django.utils.six import text_type

        if not isinstance(currency, text_type):
            raise forms.ValidationError(
                _("Unrecognized currency type '%s'." % currency))

Fix the problem.

No manager for class with "MoneyField" attribute

Hello,

I'm using django-money, latest version from github. I've set up my class as follow:

class Account(models.Model):
    name = models.CharField(_(u'Name'), max_length=100)
    description = models.CharField(_(u'Description'), max_length=255,
                                   null=True, blank=True, default="")
    type = models.CharField(_(u'Type'), max_length=1, choices=ACCOUNT_TYPES)
    owner = models.ForeignKey(AUTH_USER_MODEL)

    initial_balance = MoneyField(
        max_digits=10, 
        decimal_places=2, 
        default_currency=DEFAULT_CURRENCY,
        default=Money(10, ARS)
    )

And when I try to query all objects (Account.objects.all()) I get the following error

AttributeError at /accounting/accounts/
type object 'Account' has no attribute 'objects'

Traceback:

Traceback:
File "D:\proyectos\kaki\lib\site-packages\django\core\handlers\base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "D:\proyectos\kaki\lib\site-packages\django\views\generic\base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)
File "D:\proyectos\kaki\lib\site-packages\braces\views.py" in dispatch
  107.             request, *args, **kwargs)
File "D:\proyectos\kaki\lib\site-packages\django\views\decorators\csrf.py" in wrapped_view
  77.         return view_func(*args, **kwargs)
File "D:\proyectos\kaki\lib\site-packages\rest_framework\views.py" in dispatch
  399.             response = self.handle_exception(exc)
File "D:\proyectos\kaki\lib\site-packages\rest_framework\views.py" in dispatch
  396.             response = handler(request, *args, **kwargs)
File "D:\proyectos\kaki\kaki\kaki\accounting\views\accounts.py" in get
  14.         queryset = Account.objects.filter(owner=self.request.user)

Exception Type: AttributeError at /accounting/accounts/
Exception Value: type object 'Account' has no attribute 'objects'

Besides this, I'm able to create an Account object and save it using:

account_groceries = Account()
account_groceries.name = 'Groceries'
account_groceries.type = 'E'
account_groceries.owner = admin
account_groceries.initial_balance = Money(200, 'ARS')
account_groceries.save() 

Regards,

$ and , in money field cause validation error?

Hey there, first of all THANK ya so much for this module!

Having a problem, whenever I put "$" or "," in the field, I get validation errors: "Enter a number."

I tried adding clean_price to my form, which was never fired(?) and tried a custom clean like so:

    def clean(self):
        # Replace $ and , with '' so they validate fine
        self.data['price'] = str(self.data['price']).translate(None, '$,')

        return super(ListingForm, self).clean()

But it's still getting an error whenever I put in '$' or ',', I'm sure it's something I don't understand! Thanks for any help!

Accept integer as acceptable value for MoneyField Model field

In djmoney/models/fields.MoneyField you see the current code:

class MoneyField(models.DecimalField):

    def __init__(self, verbose_name=None, name=None,
             max_digits=None, decimal_places=None,
             default=Money(0.0, DEFAULT_CURRENCY),
             default_currency=DEFAULT_CURRENCY,
             currency_choices=CURRENCY_CHOICES, **kwargs):

    if isinstance(default, basestring):
        amount, currency = default.split(" ")
        default = Money(float(amount), Currency(code=currency))
    elif isinstance(default, (float, Decimal)):
        default = Money(default, default_currency)

    if not isinstance(default, Money):
        raise Exception(
            "default value must be an instance of Money, is: %s" % str(
                default))

What happens if I try and initialize the field with a default value of an integer, say 10? Since an integer won't pass as a basestring, float or Decimal, this code will raise an exception. Why not add the following:

if isinstance(default, basestring):
    amount, currency = default.split(" ")
    default = Money(float(amount), Currency(code=currency))
elif isinstance(default, (float, Decimal)):
    default = Money(default, default_currency)
elif isinstance(default, int):
    default = Money(float(default), Currency(code=currency))    

handling operations with an int

Hi,

What about being able to add a money field with an int ? The result will have the currency of the money field.

Because, for example, this fails:
sum([list of MoneyField only])
raises TypeError: Cannot add or subtract a Money and non-Money instance.

They are all MoneyField in the list but I think sum starts with the int zero to add the next element.

Thanks

"Cannot compare Money with different currencies" error because of lack of `__eq__` method

Django-money check that the currency are the same before most operations:

E.G, L91 classes.py:

    if self.currency == other.currency:
        return Money(
            amount=self.amount + other.amount,
            currency=self.currency)

    raise TypeError('Cannot add or subtract two Money ' +
                    'instances with different currencies.')

But because the Currency doesn't declare any __eq__ method, the check is implemented by Python this way:

self.currency == other.currency 

is turned into

id(self.currency) == id(other.currency)

It works most of the time, as by importing a currency from the same module, you DO use the same object, and therefor it DOES have the same ID:

from moneyed import USD

But It won't work if you are comparing instances that have been created in one interpreter, and compared in another, like in the case where you use queue managers like Celery.

There is an easy fix: just declare an __eq__ method giving an universal way of comparing currencies:

def __eq__(self, other):
    return self.code == other.code

While waiting for the patch, people that encounter this bug can monkey patch their own moneyed lib dynamically by adding this in THEIR app's __init__.py:

from moneyed import Currency

def __eq__(self, other):
    return self.code == other.code

Currency.__eq__ = __eq__

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

setup.py fails

with the following error:

running install_egg_info

error: error in 'egg_base' option: '' does not exist or is not a directory

----------------------------------------
Command /usr/local/Cellar/python/2.7.2/bin/python -c "import setuptools;__file__='/var/folders/t6/zdx_cx1j3hv_gn1kwc2w271r0000gn/T/pip-cihYFz-build/setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --single-version-externally-managed --record /var/folders/t6/zdx_cx1j3hv_gn1kwc2w271r0000gn/T/pip-bK_fUj-record/install-record.txt failed with error code 1
Storing complete log in /Users/tijs/.pip/pip.log

Proxy Model with MoneyField returns wrong class...

This is an ugly one... I took me hours to find out that this was caused by django-money.

class ProxyModel(SomeModelWithMoneyField):
    class Meta:
        proxy=True

The model manager now returns the original class instead of the proxy

>>> type(ProxyModel.objects.first())
<class 'some.models.SomeModelWithMoneyField'>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.