diff --git a/.files b/.files index e1bde5f..ea16f64 100644 Binary files a/.files and b/.files differ diff --git a/.rev b/.rev index 1c07619..a4c0ec4 100644 --- a/.rev +++ b/.rev @@ -16,4 +16,12 @@ 893761 + + e8fe1696d49ceed48b2b47109a640cd9 + 2.1.1 + + dimstar_suse + + 976603 + diff --git a/django-money-1.3.1.tar.gz b/django-money-1.3.1.tar.gz deleted file mode 120000 index 7d1beb8..0000000 --- a/django-money-1.3.1.tar.gz +++ /dev/null @@ -1 +0,0 @@ -/ipfs/bafkreibmo3qo5rsnp37lgobznomw44sy3mxg556ywfpht77c6ayrkoy26i \ No newline at end of file diff --git a/django-money-2.1.1.tar.gz b/django-money-2.1.1.tar.gz new file mode 120000 index 0000000..655087b --- /dev/null +++ b/django-money-2.1.1.tar.gz @@ -0,0 +1 @@ +/ipfs/bafkreihjwox3wqsk2qzbc63rznksvo4wyvj4cn7obuth2sglsx35v3h4ve \ No newline at end of file diff --git a/merged_pr_657.patch b/merged_pr_657.patch new file mode 100644 index 0000000..726a0a2 --- /dev/null +++ b/merged_pr_657.patch @@ -0,0 +1,168 @@ +From b140c16ca8f9ed0227f5295878c3f6b346a8472c Mon Sep 17 00:00:00 2001 +From: David Szotten +Date: Wed, 19 Jan 2022 17:53:07 +0000 +Subject: [PATCH 1/5] failing test to expose issue + +--- + tests/test_models.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/tests/test_models.py b/tests/test_models.py +index fb5b55cc..0ae6ba4e 100644 +--- a/tests/test_models.py ++++ b/tests/test_models.py +@@ -5,6 +5,7 @@ + """ + import datetime + from copy import copy ++from decimal import Decimal + + from django import VERSION + from django.core.exceptions import ValidationError +@@ -373,6 +374,12 @@ def test_fails_with_null_currency(self): + assert str(exc.value) == "Missing currency value" + assert not ModelWithNullableCurrency.objects.exists() + ++ def test_fails_with_null_currency_decimal(self): ++ with pytest.raises(ValueError) as exc: ++ ModelWithNullableCurrency.objects.create(money=Decimal(10)) ++ assert str(exc.value) == "Missing currency value" ++ assert not ModelWithNullableCurrency.objects.exists() ++ + def test_fails_with_nullable_but_no_default(self): + with pytest.raises(IntegrityError) as exc: + ModelWithTwoMoneyFields.objects.create() + +From 2ccaadc4e1d3a7ca06ba96ee683fb9057daa8d94 Mon Sep 17 00:00:00 2001 +From: David Szotten +Date: Wed, 19 Jan 2022 17:54:30 +0000 +Subject: [PATCH 2/5] suggested better fix for + https://github.com/django-money/django-money/pull/427/ + +TODO: is the currency field guaranteed to appear before the amount (and main) +field? yes because of the creation_counters? +--- + djmoney/models/fields.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/djmoney/models/fields.py b/djmoney/models/fields.py +index 8eb710ba..3bfd7dda 100644 +--- a/djmoney/models/fields.py ++++ b/djmoney/models/fields.py +@@ -104,7 +104,12 @@ def __get__(self, obj, type=None): + return data[self.field.name] + + def __set__(self, obj, value): # noqa +- if value is not None and self.field._currency_field.null and not isinstance(value, MONEY_CLASSES + (Decimal,)): ++ if ( ++ value is not None ++ and self.field._currency_field.null ++ and not isinstance(value, MONEY_CLASSES) ++ and not obj.__dict__[self.currency_field_name] ++ ): + # For nullable fields we need either both NULL amount and currency or both NOT NULL + raise ValueError("Missing currency value") + if isinstance(value, BaseExpression): + +From 952ac5a75b43a632febe733d0aa1a5a716b7735c Mon Sep 17 00:00:00 2001 +From: David Szotten +Date: Wed, 19 Jan 2022 22:09:06 +0000 +Subject: [PATCH 3/5] fix django error message change + +fix for +https://github.com/django/django/commit/08d8bccbf1b0764a0de68325569ee47da256e206 +--- + tests/test_models.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/tests/test_models.py b/tests/test_models.py +index 0ae6ba4e..53def7cb 100644 +--- a/tests/test_models.py ++++ b/tests/test_models.py +@@ -719,7 +719,9 @@ def test_override_decorator(): + def test_properties_access(): + with pytest.raises(TypeError) as exc: + ModelWithVanillaMoneyField(money=Money(1, "USD"), bla=1) +- if VERSION[:2] > (2, 1): ++ if VERSION[:2] > (4, 0): ++ assert str(exc.value) == "ModelWithVanillaMoneyField() got unexpected keyword arguments: 'bla'" ++ elif VERSION[:2] > (2, 1): + assert str(exc.value) == "ModelWithVanillaMoneyField() got an unexpected keyword argument 'bla'" + else: + assert str(exc.value) == "'bla' is an invalid keyword argument for this function" + +From 620af5355a22ca1da0cb43c8e5787c3e8b76f995 Mon Sep 17 00:00:00 2001 +From: David Szotten +Date: Thu, 20 Jan 2022 08:59:37 +0000 +Subject: [PATCH 4/5] better match the signature of input() + +for compat with django 0ab58c120939093fea90822f376e1866fc714d1f +--- + tests/migrations/helpers.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/migrations/helpers.py b/tests/migrations/helpers.py +index f38067fe..cbf2c7fe 100644 +--- a/tests/migrations/helpers.py ++++ b/tests/migrations/helpers.py +@@ -12,7 +12,7 @@ def makemigrations(): + from django.db.migrations import questioner + + # We should answer yes for all migrations questioner questions +- questioner.input = lambda x: "y" ++ questioner.input = lambda prompt=None: "y" + + os.system("find . -name \\*.pyc -delete") + call_command("makemigrations", "money_app", name=MIGRATION_NAME) + +From faf4da5f96193fc1a5e0b2b838f2a13189975abf Mon Sep 17 00:00:00 2001 +From: David Szotten +Date: Thu, 27 Jan 2022 17:47:01 +0000 +Subject: [PATCH 5/5] changelog + +--- + docs/changes.rst | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/docs/changes.rst b/docs/changes.rst +index 1968af75..5eab8fd1 100644 +--- a/docs/changes.rst ++++ b/docs/changes.rst +@@ -1,6 +1,14 @@ + Changelog + ========= + ++`Unreleased`_ - TBA ++------------------- ++ ++**Fixed** ++ ++- Improve the internal check for whether a currency is provided `#657`_ (`davidszotten`_) ++- Fix test suite for django main branch `#657`_ (`davidszotten`_) ++ + `2.1.1`_ - 2022-01-02 + --------------------- + +@@ -709,8 +717,7 @@ wrapping with ``money_manager``. + + - Initial public release + +-# .. _Unreleased: https:///github.com/django-money/django-money/compare/2.1.1...HEAD +- ++.. _Unreleased: https:///github.com/django-money/django-money/compare/2.1.1...HEAD + .. _2.1.1: https:///github.com/django-money/django-money/compare/2.1...2.1.1 + .. _2.1: https:///github.com/django-money/django-money/compare/2.0.3...2.1 + .. _2.0.3: https://github.com/django-money/django-money/compare/2.0.2...2.0.3 +@@ -773,6 +780,7 @@ wrapping with ``money_manager``. + .. _0.3: https://github.com/django-money/django-money/compare/0.2...0.3 + .. _0.2: https://github.com/django-money/django-money/compare/0.2...a6d90348085332a393abb40b86b5dd9505489b04 + ++.. _#657: https://github.com/django-money/django-money/issues/657 + .. _#648: https://github.com/django-money/django-money/issues/648 + .. _#646: https://github.com/django-money/django-money/issues/646 + .. _#637: https://github.com/django-money/django-money/issues/637 +@@ -966,3 +974,4 @@ wrapping with ``money_manager``. + .. _washeck: https://github.com/washeck + .. _fara: https://github.com/fara + .. _wearebasti: https://github.com/wearebasti ++.. _davidszotten: https://github.com/davidszotten diff --git a/pr_638.patch b/pr_638.patch new file mode 100644 index 0000000..82a3593 --- /dev/null +++ b/pr_638.patch @@ -0,0 +1,779 @@ +diff --git a/djmoney/__init__.py b/djmoney/__init__.py +index d483c54..3358017 100644 +--- a/djmoney/__init__.py ++++ b/djmoney/__init__.py +@@ -1,2 +1,2 @@ +-__version__ = "2.1.1" ++__version__ = "3.0.0" + default_app_config = "djmoney.apps.MoneyConfig" +diff --git a/djmoney/models/fields.py b/djmoney/models/fields.py +index 3bfd7dd..582eec2 100644 +--- a/djmoney/models/fields.py ++++ b/djmoney/models/fields.py +@@ -104,14 +104,6 @@ class MoneyFieldProxy: + return data[self.field.name] + + def __set__(self, obj, value): # noqa +- if ( +- value is not None +- and self.field._currency_field.null +- and not isinstance(value, MONEY_CLASSES) +- and not obj.__dict__[self.currency_field_name] +- ): +- # For nullable fields we need either both NULL amount and currency or both NOT NULL +- raise ValueError("Missing currency value") + if isinstance(value, BaseExpression): + if isinstance(value, Value): + value = self.prepare_value(obj, value.value) +@@ -120,6 +112,14 @@ class MoneyFieldProxy: + prepare_expression(value) + else: + value = self.prepare_value(obj, value) ++ if ( ++ value is not None ++ and self.field._currency_field.null ++ and not isinstance(value, MONEY_CLASSES) ++ and not obj.__dict__[self.currency_field_name] ++ ): ++ # For nullable fields we need either both NULL amount and currency or both NOT NULL ++ raise ValueError("Missing currency value") + obj.__dict__[self.field.name] = value + + def prepare_value(self, obj, value): +@@ -178,7 +178,7 @@ class MoneyField(models.DecimalField): + currency_max_length=CURRENCY_CODE_MAX_LENGTH, + currency_field_name=None, + money_descriptor_class=MoneyFieldProxy, +- **kwargs ++ **kwargs, + ): + nullable = kwargs.get("null", False) + default = self.setup_default(default, default_currency, nullable) +@@ -213,7 +213,7 @@ class MoneyField(models.DecimalField): + elif isinstance(default, OldMoney): + default = Money(default.amount, default.currency) + if default is not None and default is not NOT_PROVIDED and not isinstance(default, Money): +- raise ValueError("default value must be an instance of Money, is: %s" % default) ++ raise ValueError(f"default value must be an instance of Money, is: {default}") + return default + + def to_python(self, value): +@@ -261,7 +261,7 @@ class MoneyField(models.DecimalField): + default=self.default_currency, + editable=False, + choices=self.currency_choices, +- null=self.default_currency is None, ++ null=self.null, + ) + currency_field.creation_counter = self.creation_counter - 1 + currency_field_name = get_currency_field_name(name, self) +diff --git a/djmoney/money.py b/djmoney/money.py +index f11ca38..95a154d 100644 +--- a/djmoney/money.py ++++ b/djmoney/money.py +@@ -1,5 +1,3 @@ +-import warnings +-from functools import partial + from types import MappingProxyType + + from django.conf import settings +@@ -10,20 +8,13 @@ from django.utils.html import avoid_wrapping, conditional_escape + from django.utils.safestring import mark_safe + + import moneyed.l10n +-import moneyed.localization + from moneyed import Currency, Money as DefaultMoney + +-from .settings import DECIMAL_PLACES, DECIMAL_PLACES_DISPLAY, IS_DECIMAL_PLACES_DISPLAY_SET, MONEY_FORMAT ++from .settings import DECIMAL_PLACES, MONEY_FORMAT + + + __all__ = ["Money", "Currency"] + +-_warn_decimal_places_display_deprecated = partial( +- warnings.warn, +- "`Money.decimal_places_display` is deprecated and will be removed in django-money 3.0.", +- DeprecationWarning, +-) +- + + @deconstructible + class Money(DefaultMoney): +@@ -33,27 +24,11 @@ class Money(DefaultMoney): + + use_l10n = None + +- def __init__(self, *args, decimal_places_display=None, format_options=None, **kwargs): ++ def __init__(self, *args, format_options=None, **kwargs): + self.decimal_places = kwargs.pop("decimal_places", DECIMAL_PLACES) +- self._decimal_places_display = decimal_places_display +- if decimal_places_display is not None: +- _warn_decimal_places_display_deprecated() + self.format_options = MappingProxyType(format_options) if format_options is not None else None + super().__init__(*args, **kwargs) + +- @property +- def decimal_places_display(self): +- _warn_decimal_places_display_deprecated() +- if self._decimal_places_display is None: +- return DECIMAL_PLACES_DISPLAY.get(self.currency.code, self.decimal_places) +- return self._decimal_places_display +- +- @decimal_places_display.setter +- def decimal_places_display(self, value): +- """ Set number of digits being displayed - `None` resets to `DECIMAL_PLACES_DISPLAY` setting """ +- _warn_decimal_places_display_deprecated() +- self._decimal_places_display = value +- + def _copy_attributes(self, source, target): + """Copy attributes to the new `Money` instance. + +@@ -111,20 +86,7 @@ class Money(DefaultMoney): + # https://github.com/py-moneyed/py-moneyed/blob/c518745dd9d7902781409daec1a05699799474dd/moneyed/classes.py#L217-L218 + raise TypeError("Cannot divide non-Money by a Money instance.") + +- @property +- def is_localized(self): +- if self.use_l10n is None: +- return settings.USE_L10N +- return self.use_l10n +- + def __str__(self): +- if self._decimal_places_display is not None or IS_DECIMAL_PLACES_DISPLAY_SET: +- kwargs = {"money": self, "decimal_places": self.decimal_places_display} +- if self.is_localized: +- locale = get_current_locale(for_babel=False) +- if locale: +- kwargs["locale"] = locale +- return moneyed.localization.format_money(**kwargs) + format_options = { + **MONEY_FORMAT, + **(self.format_options or {}), +@@ -175,22 +137,10 @@ class Money(DefaultMoney): + __rmul__ = __mul__ + + +-def get_current_locale(for_babel=True): ++def get_current_locale(): + # get_language can return None starting from Django 1.8 + language = translation.get_language() or settings.LANGUAGE_CODE +- locale = translation.to_locale(language) +- +- if for_babel: +- return locale +- +- if locale.upper() in moneyed.localization._FORMATTER.formatting_definitions: +- return locale +- +- locale = f"{locale}_{locale}".upper() +- if locale in moneyed.localization._FORMATTER.formatting_definitions: +- return locale +- +- return "" ++ return translation.to_locale(language) + + + def maybe_convert(value, currency): +diff --git a/djmoney/settings.py b/djmoney/settings.py +index 3509ed2..4aa7e85 100644 +--- a/djmoney/settings.py ++++ b/djmoney/settings.py +@@ -1,15 +1,14 @@ + import operator +-import warnings + from types import MappingProxyType + + from django.conf import settings + +-from moneyed import CURRENCIES, DEFAULT_CURRENCY, DEFAULT_CURRENCY_CODE ++from moneyed import CURRENCIES, Currency + + + # The default currency, you can define this in your project's settings module + # This has to be a currency object imported from moneyed +-DEFAULT_CURRENCY = getattr(settings, "DEFAULT_CURRENCY", DEFAULT_CURRENCY) ++DEFAULT_CURRENCY: Currency = getattr(settings, "DEFAULT_CURRENCY", None) + + + # The default currency choices, you can define this in your project's +@@ -21,18 +20,10 @@ if CURRENCY_CHOICES is None: + if PROJECT_CURRENCIES: + CURRENCY_CHOICES = [(code, CURRENCIES[code].name) for code in PROJECT_CURRENCIES] + else: +- CURRENCY_CHOICES = [(c.code, c.name) for i, c in CURRENCIES.items() if c.code != DEFAULT_CURRENCY_CODE] ++ CURRENCY_CHOICES = [(c.code, c.name) for i, c in CURRENCIES.items() if c != DEFAULT_CURRENCY] + + CURRENCY_CHOICES.sort(key=operator.itemgetter(1, 0)) + DECIMAL_PLACES = getattr(settings, "CURRENCY_DECIMAL_PLACES", 2) +-_decimal_display_value = getattr(settings, "CURRENCY_DECIMAL_PLACES_DISPLAY", None) +-if _decimal_display_value is not None: +- warnings.warn( +- "`CURRENCY_DECIMAL_PLACES_DISPLAY` is deprecated and will be removed in django-money 3.0.", +- DeprecationWarning, +- ) +-DECIMAL_PLACES_DISPLAY = _decimal_display_value or {currency[0]: DECIMAL_PLACES for currency in CURRENCY_CHOICES} +-IS_DECIMAL_PLACES_DISPLAY_SET = _decimal_display_value is not None + + OPEN_EXCHANGE_RATES_URL = getattr(settings, "OPEN_EXCHANGE_RATES_URL", "https://openexchangerates.org/api/latest.json") + OPEN_EXCHANGE_RATES_APP_ID = getattr(settings, "OPEN_EXCHANGE_RATES_APP_ID", None) +diff --git a/docs/changes.rst b/docs/changes.rst +index 5eab8fd..4b4ee01 100644 +--- a/docs/changes.rst ++++ b/docs/changes.rst +@@ -4,11 +4,19 @@ Changelog + `Unreleased`_ - TBA + ------------------- + ++**Changed** ++ ++- Update py-moneyed to 2.0. ++ + **Fixed** + + - Improve the internal check for whether a currency is provided `#657`_ (`davidszotten`_) + - Fix test suite for django main branch `#657`_ (`davidszotten`_) + ++**Removed** ++- Remove the deprecated ``Money.decimal_places_display`` property and argument. ++- Remove the deprecated ``CURRENCY_DECIMAL_PLACES_DISPLAY`` setting. ++ + `2.1.1`_ - 2022-01-02 + --------------------- + +diff --git a/pytest.ini b/pytest.ini +index 46fd704..dccdd07 100644 +--- a/pytest.ini ++++ b/pytest.ini +@@ -2,6 +2,3 @@ + DJANGO_SETTINGS_MODULE=tests.settings + filterwarnings = + error::DeprecationWarning +- ignore:This module and all its contents is deprecated in favour of new moneyed.l10n.format_money\.:DeprecationWarning +- ignore:`Money\.decimal_places_display` is deprecated and will be removed in django-money 3\.0\.:DeprecationWarning +- ignore:`CURRENCY_DECIMAL_PLACES_DISPLAY` is deprecated and will be removed in django-money 3\.0\.:DeprecationWarning +diff --git a/setup.py b/setup.py +index c6c1fb6..623b713 100644 +--- a/setup.py ++++ b/setup.py +@@ -66,7 +66,7 @@ setup( + maintainer_email="greg@reinbach.com", + license="BSD", + packages=find_packages(include=["djmoney", "djmoney.*"]), +- install_requires=["setuptools", "Django>=2.2", "py-moneyed>=1.2,<2.0"], ++ install_requires=["setuptools", "Django>=2.2", "py-moneyed>=2.0"], + python_requires=">=3.6", + platforms=["Any"], + keywords=["django", "py-money", "money"], +diff --git a/tests/conftest.py b/tests/conftest.py +index 666e969..7f8baee 100644 +--- a/tests/conftest.py ++++ b/tests/conftest.py +@@ -1,5 +1,3 @@ +-from unittest import mock +- + import pytest + + from djmoney.contrib.exchange.models import ExchangeBackend, Rate, get_default_backend_name +@@ -31,9 +29,3 @@ def concrete_instance(m2m_object): + + + pytest_plugins = "pytester" +- +- +-@pytest.yield_fixture +-def legacy_formatting(): +- with mock.patch("djmoney.money.IS_DECIMAL_PLACES_DISPLAY_SET", True): +- yield +diff --git a/tests/contrib/test_django_rest_framework.py b/tests/contrib/test_django_rest_framework.py +index aef52bd..e373910 100644 +--- a/tests/contrib/test_django_rest_framework.py ++++ b/tests/contrib/test_django_rest_framework.py +@@ -67,7 +67,7 @@ class TestMoneyField: + (NullMoneyFieldModel, "field", {"default_currency": "EUR"}, 10, Money(10, "EUR")), + (ModelWithVanillaMoneyField, "money", None, Money(10, "USD"), Money(10, "USD")), + (ModelWithVanillaMoneyField, "money", {"default_currency": "EUR"}, Money(10, "USD"), Money(10, "USD")), +- (ModelWithVanillaMoneyField, "money", None, 10, Money(10, "XYZ")), ++ # (ModelWithVanillaMoneyField, "money", None, 10, Money(10, "XYZ")), # TODO + (ModelWithVanillaMoneyField, "money", {"default_currency": "EUR"}, 10, Money(10, "EUR")), + ), + ) +diff --git a/tests/settings.py b/tests/settings.py +index de5ce53..eb6a7db 100644 +--- a/tests/settings.py ++++ b/tests/settings.py +@@ -1,8 +1,6 @@ + import warnings +-from decimal import ROUND_HALF_EVEN + + import moneyed +-from moneyed.localization import _FORMATTER, DEFAULT + + + DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} +@@ -36,20 +34,6 @@ SECRET_KEY = "foobar" + USE_L10N = True + + +-_FORMATTER.add_sign_definition("pl_PL", moneyed.PLN, suffix=" zł") +-_FORMATTER.add_sign_definition(DEFAULT, moneyed.PLN, suffix=" zł") +-_FORMATTER.add_formatting_definition( +- "pl_PL", +- group_size=3, +- group_separator=" ", +- decimal_point=",", +- positive_sign="", +- trailing_positive_sign="", +- negative_sign="-", +- trailing_negative_sign="", +- rounding_method=ROUND_HALF_EVEN, +-) +- + moneyed.add_currency("USDT", "000", "Tether", None) + + OPEN_EXCHANGE_RATES_APP_ID = "test" +diff --git a/tests/test_admin.py b/tests/test_admin.py +index 068700b..5b952cb 100644 +--- a/tests/test_admin.py ++++ b/tests/test_admin.py +@@ -11,29 +11,11 @@ MONEY_FIELD = ModelWithVanillaMoneyField._meta.get_field("money") + INTEGER_FIELD = ModelWithVanillaMoneyField._meta.get_field("integer") + + +-@pytest.mark.parametrize( +- "value, expected", +- ( +- (Money(10, "RUB"), "10.00 руб."), # Issue 232 +- (Money(1234), "1,234.00 XYZ"), # Issue 220 +- (Money(1000, "SAR"), "ر.س1,000.00"), # Issue 196 +- (Money(1000, "PLN"), "1,000.00 zł"), # Issue 102 +- (Money("3.33", "EUR"), "3.33 €"), # Issue 90 +- ), +-) +-def test_display_for_field_with_legacy_formatting(legacy_formatting, settings, value, expected): +- settings.USE_L10N = True +- # This locale has no definitions in py-moneyed, so it will work for localized money representation. +- settings.LANGUAGE_CODE = "cs" +- settings.DECIMAL_PLACES_DISPLAY = {} +- assert admin_utils.display_for_field(value, MONEY_FIELD, "") == expected +- +- + @pytest.mark.parametrize( + "value, expected", + ( + (Money(10, "RUB"), "10,00\xa0RUB"), # Issue 232 +- (Money(1234), "1\xa0234,00\xa0XYZ"), # Issue 220 ++ (Money(1234, "USD"), "1\xa0234,00\xa0US$"), # Issue 220 + (Money(1000, "SAR"), "1\xa0000,00\xa0SAR"), # Issue 196 + (Money(1000, "PLN"), "1\xa0000,00\xa0PLN"), # Issue 102 + (Money("3.33", "EUR"), "3,33\xa0€"), # Issue 90 +diff --git a/tests/test_models.py b/tests/test_models.py +index 53def7c..c1ef0db 100644 +--- a/tests/test_models.py ++++ b/tests/test_models.py +@@ -39,6 +39,8 @@ from .testapp.models import ( + ModelWithDefaultAsOldMoney, + ModelWithDefaultAsString, + ModelWithDefaultAsStringWithCurrency, ++ ModelWithDefaultCurrencyOnly, ++ ModelWithDefaultValueAndCurrency, + ModelWithNonMoneyField, + ModelWithNullableCurrency, + ModelWithSharedCurrency, +@@ -60,8 +62,8 @@ class TestVanillaMoneyField: + @pytest.mark.parametrize( + "model_class, kwargs, expected", + ( +- (ModelWithVanillaMoneyField, {"money": Money("100.0")}, Money("100.0")), +- (ModelWithVanillaMoneyField, {"money": OldMoney("100.0")}, Money("100.0")), ++ (ModelWithVanillaMoneyField, {"money": Money("100.0", "USD")}, Money("100.0", "USD")), ++ (ModelWithVanillaMoneyField, {"money": OldMoney("100.0", "USD")}, Money("100.0", "USD")), + (BaseModel, {}, Money(0, "USD")), + (BaseModel, {"money": "111.2"}, Money("111.2", "USD")), + (BaseModel, {"money": Money("123", "PLN")}, Money("123", "PLN")), +@@ -116,7 +118,7 @@ class TestVanillaMoneyField: + @pytest.mark.parametrize( + "model_class, other_value", + ( +- (ModelWithVanillaMoneyField, Money("100.0")), ++ (ModelWithDefaultValueAndCurrency, Money("100.0", "USD")), + (BaseModel, Money(0, "USD")), + (ModelWithDefaultAsMoney, Money("0.01", "RUB")), + (ModelWithDefaultAsFloat, OldMoney("12.05", "PLN")), +@@ -151,7 +153,11 @@ class TestVanillaMoneyField: + @pytest.mark.parametrize("money_class", (Money, OldMoney)) + @pytest.mark.parametrize("field_name", ("money", "second_money")) + def test_save_new_value(self, field_name, money_class): +- ModelWithVanillaMoneyField.objects.create(**{field_name: money_class("100.0")}) ++ kwargs = { ++ "money": money_class("100.0", "USD"), ++ } ++ kwargs[field_name] = money_class("100.0", "USD") ++ ModelWithVanillaMoneyField.objects.create(**kwargs) + + # Try setting the value directly + retrieved = ModelWithVanillaMoneyField.objects.get() +@@ -162,14 +168,14 @@ class TestVanillaMoneyField: + assert getattr(retrieved, field_name) == Money(1, "DKK") + + def test_rounding(self): +- money = Money("100.0623456781123219") ++ money = Money("100.0623456781123219", "EUR") + + instance = ModelWithVanillaMoneyField.objects.create(money=money) + # TODO. Should instance.money be rounded too? + + retrieved = ModelWithVanillaMoneyField.objects.get(pk=instance.pk) + +- assert retrieved.money == Money("100.06") ++ assert retrieved.money == Money("100.06", "EUR") + + @pytest.fixture(params=[Money, OldMoney]) + def objects_setup(self, request): +@@ -238,7 +244,7 @@ class TestVanillaMoneyField: + assert ModelWithTwoMoneyFields.objects.filter(**kwargs).count() == expected + + def test_exact_match(self): +- money = Money("100.0") ++ money = Money("100.0", "EUR") + + instance = ModelWithVanillaMoneyField.objects.create(money=money) + retrieved = ModelWithVanillaMoneyField.objects.get(money=money) +@@ -251,9 +257,9 @@ class TestVanillaMoneyField: + ModelIssue300.objects.filter(money__created__gt=date) + + def test_range_search(self): +- money = Money("3") ++ money = Money("3", "EUR") + +- instance = ModelWithVanillaMoneyField.objects.create(money=Money("100.0")) ++ instance = ModelWithVanillaMoneyField.objects.create(money=Money("100.0", "EUR")) + retrieved = ModelWithVanillaMoneyField.objects.get(money__gt=money) + + assert instance.pk == retrieved.pk +@@ -307,9 +313,9 @@ class TestGetOrCreate: + @pytest.mark.parametrize( + "model, field_name, kwargs, currency", + ( +- (ModelWithVanillaMoneyField, "money", {"money_currency": "PLN"}, "PLN"), +- (ModelWithVanillaMoneyField, "money", {"money": Money(0, "EUR")}, "EUR"), +- (ModelWithVanillaMoneyField, "money", {"money": OldMoney(0, "EUR")}, "EUR"), ++ (ModelWithDefaultValueAndCurrency, "money", {"money_currency": "PLN"}, "PLN"), ++ (ModelWithDefaultValueAndCurrency, "money", {"money": Money(0, "EUR")}, "EUR"), ++ (ModelWithDefaultValueAndCurrency, "money", {"money": OldMoney(0, "EUR")}, "EUR"), + (ModelWithSharedCurrency, "first", {"first": 10, "second": 15, "currency": "CZK"}, "CZK"), + ), + ) +@@ -382,8 +388,8 @@ class TestNullableCurrency: + + def test_fails_with_nullable_but_no_default(self): + with pytest.raises(IntegrityError) as exc: +- ModelWithTwoMoneyFields.objects.create() +- assert str(exc.value) == "NOT NULL constraint failed: testapp_modelwithtwomoneyfields.amount1" ++ ModelWithDefaultCurrencyOnly.objects.create() ++ assert str(exc.value) == "NOT NULL constraint failed: testapp_modelwithdefaultcurrencyonly.money" + + def test_query_not_null(self): + money = Money(100, "EUR") +@@ -527,7 +533,7 @@ class TestExpressions: + assert ModelWithVanillaMoneyField.objects.get(integer=1).money == Money(0, "USD") + + def test_create_func(self): +- instance = ModelWithVanillaMoneyField.objects.create(money=Func(Value(-10), function="ABS")) ++ instance = ModelWithDefaultValueAndCurrency.objects.create(money=Func(Value(-10), function="ABS")) + instance.refresh_from_db() + assert instance.money.amount == 10 + +@@ -560,7 +566,7 @@ def test_find_models_related_to_money_models(): + def test_allow_expression_nodes_without_money(): + """Allow querying on expression nodes that are not Money""" + desc = "hundred" +- ModelWithNonMoneyField.objects.create(money=Money(100.0), desc=desc) ++ ModelWithNonMoneyField.objects.create(money=Money(100.0, "USD"), desc=desc) + instance = ModelWithNonMoneyField.objects.filter(desc=F("desc")).get() + assert instance.desc == desc + +@@ -787,17 +793,6 @@ def test_distinct_through_wrapper(): + assert queryset.count() == 3 + + +-def test_mixer_blend(): +- try: +- from mixer.backend.django import mixer +- except AttributeError: +- pass # mixer doesn't work with pypy +- else: +- instance = mixer.blend(ModelWithTwoMoneyFields) +- assert isinstance(instance.amount1, Money) +- assert isinstance(instance.amount2, Money) +- +- + @pytest.mark.parametrize( + ("attribute", "build_kwargs", "expected"), + [ +@@ -827,3 +822,13 @@ def test_deconstruct_includes(attribute, build_kwargs, expected): + new = MoneyField(*args, **kwargs) + assert getattr(new, attribute) == getattr(instance, attribute) + assert getattr(new, attribute) == expected ++ ++ ++def test_mixer_blend(): ++ try: ++ from mixer.backend.django import mixer ++ except AttributeError: ++ pass # mixer doesn't work with pypy ++ else: ++ instance = mixer.blend(ModelWithDefaultCurrencyOnly) ++ assert isinstance(instance.money, Money) +diff --git a/tests/test_money.py b/tests/test_money.py +index 9bca69c..789ac6d 100644 +--- a/tests/test_money.py ++++ b/tests/test_money.py +@@ -9,24 +9,16 @@ def test_repr(): + assert repr(Money("10.5", "USD")) == "Money('10.5', 'USD')" + + +-def test_legacy_repr(): +- assert repr(Money("10.5", "USD", decimal_places_display=2)) == "Money('10.5', 'USD')" +- +- + def test_html_safe(): + assert Money("10.5", "EUR").__html__() == "€10.50" + + +-def test_legacy_html_safe(): +- assert Money("10.5", "EUR", decimal_places_display=2).__html__() == "10.50\xa0€" +- +- + def test_html_unsafe(): + class UnsafeMoney(Money): + def __str__(self): + return "