Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-django-money for openSUSE:Factory checked in at 2022-05-12 23:00:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-money (Old) and /work/SRC/openSUSE:Factory/.python-django-money.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-money" Thu May 12 23:00:55 2022 rev:3 rq:976603 version:2.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-money/python-django-money.changes 2021-05-17 18:46:05.164427245 +0200 +++ /work/SRC/openSUSE:Factory/.python-django-money.new.1538/python-django-money.changes 2022-05-12 23:01:55.972923482 +0200 @@ -1,0 +2,30 @@ +Thu May 12 11:45:54 UTC 2022 - John Vandenberg <jay...@gmail.com> + +- Update to v2.1.1 + * Make Django REST Framework integration always raise lower-level + errors as ValidationError. + * False positives in Migration changes, improvements to + MoneyField.deconstruct. +- from v2.1 + * Add support for Django 3.2. + * Drop support for Django 1.11, 2.1 and 3.0. + * Drop support for Python 3.5. +- from v2.0.3 + * Inconsistent Money._copy_attributes behaviour when non-Money + instances are involved. +- from v2.0.2 + * Inconsistent Money._copy_attributes behaviour. +- from v2.0.1 + * Invalid deprecation warning behavior. +- from v2.0 + * New setting CURRENCY_CODE_MAX_LENGTH configures default + max_length for MoneyField and exchange app models. + * Update py-moneyed to >=1.2,<2. It uses babel to format Money, + which formats it differently than py-moneyed<1. + * Money.decimal_places_display to be removed in django-money 3.0. + * CURRENCY_DECIMAL_PLACES_DISPLAY to be removed in django-money 3.0. +- Add patches for py-moneyed v2 support + * merged_pr_657.patch + * pr_638.patch + +------------------------------------------------------------------- Old: ---- django-money-1.3.1.tar.gz New: ---- django-money-2.1.1.tar.gz merged_pr_657.patch pr_638.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-money.spec ++++++ --- /var/tmp/diff_new_pack.5AoRI8/_old 2022-05-12 23:01:56.496924186 +0200 +++ /var/tmp/diff_new_pack.5AoRI8/_new 2022-05-12 23:01:56.504924196 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-django-money # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,13 +19,15 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-django-money -Version: 1.3.1 +Version: 2.1.1 Release: 0 Summary: Django support for using money and currency fields License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/django-money/django-money Source: https://github.com/django-money/django-money/archive/%{version}.tar.gz#/django-money-%{version}.tar.gz +Patch0: merged_pr_657.patch +Patch1: pr_638.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -52,6 +54,8 @@ %prep %setup -q -n django-money-%{version} +%patch0 -p1 +%patch1 -p1 %build %python_build @@ -63,11 +67,12 @@ %check export DJANGO_SETTINGS_MODULE=tests.settings # we don't have python-mixer and it is needed only for tests -%pytest -k "not mixer" +%pytest -k "not (mixer or test_no_deprecation_warning)" %files %{python_files} %doc README.rst %license LICENSE.txt -%{python_sitelib}/* +%{python_sitelib}/djmoney/ +%{python_sitelib}/django_money*/ %changelog ++++++ django-money-1.3.1.tar.gz -> django-money-2.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/.github/workflows/main.yml new/django-money-2.1.1/.github/workflows/main.yml --- old/django-money-1.3.1/.github/workflows/main.yml 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/.github/workflows/main.yml 2022-01-02 18:39:41.000000000 +0100 @@ -17,6 +17,7 @@ - run: pip install pre-commit - run: pre-commit run --all-files + docs: name: docs runs-on: ubuntu-latest @@ -27,89 +28,47 @@ - uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.9 - run: pip install tox - name: Run docs tox job run: tox -e docs - lint: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - - run: pip install tox - - - name: Run lint tox job - run: tox -e lint tests: strategy: matrix: include: - python: pypy3 - tox_env: django111-pypy3 - - python: pypy3 - tox_env: django21-pypy3 - - python: pypy3 tox_env: django22-pypy3 - python: pypy3 - tox_env: django30-pypy3 - - python: pypy3 tox_env: django31-pypy3 - - python: 3.5 - tox_env: django111-py35 - - python: 3.5 - tox_env: django21-py35 - - python: 3.5 - tox_env: django22-py35 - - python: 3.6 - tox_env: django111-py36 - - python: 3.6 - tox_env: django21-py36 - python: 3.6 tox_env: django22-py36 - python: 3.6 - tox_env: django30-py36 - - python: 3.6 tox_env: django31-py36 - python: 3.6 - tox_env: django_master-py36 - - python: 3.6 tox_env: no_rest_framework - python: 3.7 - tox_env: django21-py37 - - python: 3.7 tox_env: django22-py37 - python: 3.7 - tox_env: django30-py37 - - python: 3.7 tox_env: django31-py37 - - python: 3.7 - tox_env: django_master-py37 - python: 3.8 tox_env: django22-py38 - python: 3.8 - tox_env: django30-py38 - - python: 3.8 tox_env: django31-py38 - python: 3.8 - tox_env: django_master-py38 + tox_env: django32-py38 + - python: 3.8 + tox_env: django_main-py38 - python: 3.9 tox_env: django22-py39 - python: 3.9 - tox_env: django30-py39 - - python: 3.9 tox_env: django31-py39 - python: 3.9 - tox_env: django_master-py39 + tox_env: django32-py39 + - python: 3.9 + tox_env: django_main-py39 name: ${{ matrix.tox_env }} runs-on: ubuntu-latest steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/.pre-commit-config.yaml new/django-money-2.1.1/.pre-commit-config.yaml --- old/django-money-1.3.1/.pre-commit-config.yaml 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/.pre-commit-config.yaml 2022-01-02 18:39:41.000000000 +0100 @@ -1,6 +1,6 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 + rev: v3.4.0 hooks: - id: trailing-whitespace - id: check-yaml @@ -11,26 +11,39 @@ args: - --remove +- repo: https://github.com/asottile/pyupgrade + rev: v2.12.0 + hooks: + - id: pyupgrade + args: + - --py36-plus + - repo: https://github.com/asottile/seed-isort-config rev: v2.2.0 hooks: - id: seed-isort-config - repo: https://github.com/pre-commit/mirrors-isort - rev: v5.6.4 + rev: v5.8.0 hooks: - id: isort - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.1 hooks: - id: flake8 exclude: ^docs - repo: https://github.com/myint/rstcheck - rev: master + rev: 3f92957 hooks: - id: rstcheck additional_dependencies: - sphinx==3.1.2 args: [--ignore-directives=code] + +- repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + exclude: ^docs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/README.rst new/django-money-2.1.1/README.rst --- old/django-money-1.3.1/README.rst 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/README.rst 2022-01-02 18:39:41.000000000 +0100 @@ -5,8 +5,8 @@ :target: https://github.com/django-money/django-money/actions :alt: Build Status -.. image:: http://codecov.io/github/django-money/django-money/coverage.svg?branch=master - :target: http://codecov.io/github/django-money/django-money?branch=master +.. image:: http://codecov.io/github/django-money/django-money/coverage.svg?branch=main + :target: http://codecov.io/github/django-money/django-money?branch=main :alt: Coverage Status .. image:: https://readthedocs.org/projects/django-money/badge/?version=latest @@ -17,16 +17,16 @@ :target: https://pypi.python.org/pypi/django-money :alt: PyPI -A little Django app that uses ``py-moneyed`` to add support for Money +A little Django app that uses `py-moneyed <https://github.com/py-moneyed/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 +* Django versions supported: 2.2, 3.1, 3.2 +* Python versions supported: 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 <https://django-money.readthedocs.io/en/latest/changes.html>`__. -Through the dependency ``py-moneyed``, ``django-money`` gets: +Through the dependency `py-moneyed <https://github.com/py-moneyed/py-moneyed>`__, ``django-money`` gets: * Support for proper Money value handling (using the standard Money design pattern) @@ -42,7 +42,7 @@ $ pip install django-money -This automatically installs ``py-moneyed`` v0.8 (or later). +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. @@ -94,6 +94,11 @@ 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 ---------------- @@ -141,15 +146,12 @@ 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 +To add a new currency available on all the project, you can simply add +these few lines to your ``settings.py`` file: .. code:: python import moneyed - from moneyed.localization import _FORMATTER - from decimal import ROUND_HALF_EVEN - BOB = moneyed.add_currency( code='BOB', @@ -158,20 +160,6 @@ 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`` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/__init__.py new/django-money-2.1.1/djmoney/__init__.py --- old/django-money-1.3.1/djmoney/__init__.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/__init__.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,2 +1,2 @@ -__version__ = "1.3.1" +__version__ = "2.1.1" default_app_config = "djmoney.apps.MoneyConfig" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/apps.py new/django-money-2.1.1/djmoney/apps.py --- old/django-money-1.3.1/djmoney/apps.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/apps.py 2022-01-02 18:39:41.000000000 +0100 @@ -3,6 +3,7 @@ class MoneyConfig(AppConfig): name = "djmoney" + default_auto_field = "django.db.models.AutoField" def ready(self): try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/contrib/django_rest_framework/fields.py new/django-money-2.1.1/djmoney/contrib/django_rest_framework/fields.py --- old/django-money-1.3.1/djmoney/contrib/django_rest_framework/fields.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/contrib/django_rest_framework/fields.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,4 +1,5 @@ from django.core.validators import MaxValueValidator, MinValueValidator +from django.utils.translation import gettext_lazy as _ from rest_framework.fields import empty from rest_framework.serializers import DecimalField, ModelSerializer @@ -7,6 +8,20 @@ from djmoney.models.validators import MaxMoneyValidator, MinMoneyValidator from djmoney.money import Money from djmoney.utils import MONEY_CLASSES, get_currency_field_name +from moneyed.classes import CurrencyDoesNotExist + + +class _PrimitiveMoney: + """ + A container for ``Money`` data that does not do any validation of said data. + It conveniently holds the amount and currency attributes to ease transformation into a valid ``Money`` instance. + """ + + __slots__ = ("amount", "currency") + + def __init__(self, amount, currency): + self.amount = amount + self.currency = currency class MoneyField(DecimalField): @@ -15,6 +30,10 @@ does decimal's validation during transformation to native value. """ + default_error_messages = { + "invalid_currency": _("{currency!r} is not a valid currency"), + } + def __init__(self, *args, **kwargs): self.default_currency = kwargs.pop("default_currency", None) super().__init__(*args, **kwargs) @@ -36,16 +55,20 @@ return super().to_representation(obj) def to_internal_value(self, data): - if isinstance(data, MONEY_CLASSES): + if isinstance(data, MONEY_CLASSES + (_PrimitiveMoney,)): amount = super().to_internal_value(data.amount) - return Money(amount, data.currency) + try: + return Money(amount, data.currency) + except CurrencyDoesNotExist: + self.fail("invalid_currency", currency=data.currency) + return super().to_internal_value(data) def get_value(self, data): amount = super().get_value(data) currency = data.get(get_currency_field_name(self.field_name), self.default_currency) if currency and amount is not None and not isinstance(amount, MONEY_CLASSES) and amount is not empty: - return Money(amount, currency) + return _PrimitiveMoney(amount=amount, currency=currency) return amount diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/contrib/exchange/migrations/0001_initial.py new/django-money-2.1.1/djmoney/contrib/exchange/migrations/0001_initial.py --- old/django-money-1.3.1/djmoney/contrib/exchange/migrations/0001_initial.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/contrib/exchange/migrations/0001_initial.py 2022-01-02 18:39:41.000000000 +0100 @@ -3,6 +3,8 @@ import django.db.models.deletion from django.db import migrations, models +from djmoney.settings import CURRENCY_CODE_MAX_LENGTH + class Migration(migrations.Migration): @@ -16,14 +18,14 @@ fields=[ ("name", models.CharField(max_length=255, primary_key=True, serialize=False)), ("last_update", models.DateTimeField(auto_now=True)), - ("base_currency", models.CharField(max_length=3)), + ("base_currency", models.CharField(max_length=CURRENCY_CODE_MAX_LENGTH)), ], ), migrations.CreateModel( name="Rate", fields=[ ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("currency", models.CharField(max_length=3)), + ("currency", models.CharField(max_length=CURRENCY_CODE_MAX_LENGTH)), ("value", models.DecimalField(decimal_places=6, max_digits=20)), ( "backend", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/contrib/exchange/models.py new/django-money-2.1.1/djmoney/contrib/exchange/models.py --- old/django-money-1.3.1/djmoney/contrib/exchange/models.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/contrib/exchange/models.py 2022-01-02 18:39:41.000000000 +0100 @@ -4,7 +4,7 @@ from django.db import models from django.utils.module_loading import import_string -from djmoney.settings import EXCHANGE_BACKEND, RATES_CACHE_TIMEOUT +from djmoney.settings import CURRENCY_CODE_MAX_LENGTH, EXCHANGE_BACKEND, RATES_CACHE_TIMEOUT from .exceptions import MissingRate @@ -12,7 +12,7 @@ class ExchangeBackend(models.Model): name = models.CharField(max_length=255, primary_key=True) last_update = models.DateTimeField(auto_now=True) - base_currency = models.CharField(max_length=3) + base_currency = models.CharField(max_length=CURRENCY_CODE_MAX_LENGTH) def __str__(self): return self.name @@ -22,7 +22,7 @@ class Rate(models.Model): - currency = models.CharField(max_length=3) + currency = models.CharField(max_length=CURRENCY_CODE_MAX_LENGTH) value = models.DecimalField(max_digits=20, decimal_places=6) backend = models.ForeignKey(ExchangeBackend, on_delete=models.CASCADE, related_name="rates") @@ -42,7 +42,7 @@ """ if backend is None: backend = get_default_backend_name() - key = "djmoney:get_rate:%s:%s:%s" % (source, target, backend) + key = f"djmoney:get_rate:{source}:{target}:{backend}" result = cache.get(key) if result is not None: return result @@ -57,7 +57,7 @@ return 1 rates = Rate.objects.filter(currency__in=(source, target), backend=backend).select_related("backend") if not rates: - raise MissingRate("Rate %s -> %s does not exist" % (source, target)) + raise MissingRate(f"Rate {source} -> {target} does not exist") if len(rates) == 1: return _try_to_get_rate_directly(source, target, rates[0]) return _get_rate_via_base(rates, target) @@ -74,7 +74,7 @@ elif rate.backend.base_currency == target and rate.currency == source: return 1 / rate.value # Case when target or source is not a base currency - raise MissingRate("Rate %s -> %s does not exist" % (source, target)) + raise MissingRate(f"Rate {source} -> {target} does not exist") def _get_rate_via_base(rates, target): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/forms/fields.py new/django-money-2.1.1/djmoney/forms/fields.py --- old/django-money-1.3.1/djmoney/forms/fields.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/forms/fields.py 2022-01-02 18:39:41.000000000 +0100 @@ -32,7 +32,7 @@ min_value=min_value, max_digits=max_digits, decimal_places=decimal_places, - **kwargs + **kwargs, ) currency_field = ChoiceField(choices=currency_choices) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/models/fields.py new/django-money-2.1.1/djmoney/models/fields.py --- old/django-money-1.3.1/djmoney/models/fields.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/models/fields.py 2022-01-02 18:39:41.000000000 +0100 @@ -14,7 +14,7 @@ from moneyed import Money as OldMoney from .._compat import setup_managers -from ..settings import CURRENCY_CHOICES, DECIMAL_PLACES, DEFAULT_CURRENCY +from ..settings import CURRENCY_CHOICES, CURRENCY_CODE_MAX_LENGTH, DECIMAL_PLACES, DEFAULT_CURRENCY from ..utils import MONEY_CLASSES, get_currency_field_name, prepare_expression @@ -149,7 +149,7 @@ def __init__(self, price_field=None, default=DEFAULT_CURRENCY, **kwargs): if isinstance(default, Currency): default = default.code - kwargs.setdefault("max_length", 3) + kwargs.setdefault("max_length", CURRENCY_CODE_MAX_LENGTH) self.price_field = price_field super().__init__(default=default, **kwargs) @@ -170,7 +170,7 @@ default=NOT_PROVIDED, default_currency=DEFAULT_CURRENCY, currency_choices=CURRENCY_CHOICES, - currency_max_length=3, + currency_max_length=CURRENCY_CODE_MAX_LENGTH, currency_field_name=None, money_descriptor_class=MoneyFieldProxy, **kwargs @@ -298,12 +298,17 @@ if self._has_default: kwargs["default"] = self.default.amount - if self.default_currency is not None and self.default_currency != DEFAULT_CURRENCY: - kwargs["default_currency"] = str(self.default_currency) + if self.default_currency != DEFAULT_CURRENCY: + if self.default_currency is not None: + kwargs["default_currency"] = str(self.default_currency) + else: + kwargs["default_currency"] = None if self.currency_choices != CURRENCY_CHOICES: kwargs["currency_choices"] = self.currency_choices if self.currency_field_name: kwargs["currency_field_name"] = self.currency_field_name + if self.currency_max_length != CURRENCY_CODE_MAX_LENGTH: + kwargs["currency_max_length"] = self.currency_max_length return name, path, args, kwargs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/money.py new/django-money-2.1.1/djmoney/money.py --- old/django-money-1.3.1/djmoney/money.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/money.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,3 +1,7 @@ +import warnings +from functools import partial +from types import MappingProxyType + from django.conf import settings from django.db.models import F from django.utils import translation @@ -5,14 +9,21 @@ 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 moneyed.localization import _FORMATTER, format_money -from .settings import DECIMAL_PLACES, DECIMAL_PLACES_DISPLAY +from .settings import DECIMAL_PLACES, DECIMAL_PLACES_DISPLAY, IS_DECIMAL_PLACES_DISPLAY_SET, 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): @@ -22,21 +33,25 @@ use_l10n = None - def __init__(self, *args, decimal_places_display=None, **kwargs): + def __init__(self, *args, decimal_places_display=None, 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): @@ -50,9 +65,15 @@ When it comes to what number of decimal places to choose, we take the maximum number. """ - for attribute_name in ("decimal_places", "decimal_places_display"): - value = max([getattr(candidate, attribute_name, 0) for candidate in (self, source)]) - setattr(target, attribute_name, value) + for attribute_name in ("decimal_places", "_decimal_places_display"): + selection = [ + getattr(candidate, attribute_name, None) + for candidate in (self, source) + if getattr(candidate, attribute_name, None) is not None + ] + if selection: + value = max(selection) + setattr(target, attribute_name, value) def __add__(self, other): if isinstance(other, F): @@ -97,13 +118,21 @@ return self.use_l10n def __str__(self): - kwargs = {"money": self, "decimal_places": self.decimal_places_display} - if self.is_localized: - locale = get_current_locale() - if locale: - kwargs["locale"] = locale - - return format_money(**kwargs) + 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 {}), + } + locale = get_current_locale() + if locale: + format_options["locale"] = locale + return moneyed.l10n.format_money(self, **format_options) def __html__(self): return mark_safe(avoid_wrapping(conditional_escape(str(self)))) @@ -146,16 +175,19 @@ __rmul__ = __mul__ -def get_current_locale(): +def get_current_locale(for_babel=True): # get_language can return None starting from Django 1.8 language = translation.get_language() or settings.LANGUAGE_CODE locale = translation.to_locale(language) - if locale.upper() in _FORMATTER.formatting_definitions: + if for_babel: + return locale + + if locale.upper() in moneyed.localization._FORMATTER.formatting_definitions: return locale - locale = ("%s_%s" % (locale, locale)).upper() - if locale in _FORMATTER.formatting_definitions: + locale = f"{locale}_{locale}".upper() + if locale in moneyed.localization._FORMATTER.formatting_definitions: return locale return "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/serializers.py new/django-money-2.1.1/djmoney/serializers.py --- old/django-money-1.3.1/djmoney/serializers.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/serializers.py 2022-01-02 18:39:41.000000000 +0100 @@ -45,7 +45,9 @@ continue field = Model._meta.get_field(field_name) if isinstance(field, MoneyField) and field_value is not None: - money_fields[field_name] = Money(field_value, obj["fields"][get_currency_field_name(field_name)]) + money_fields[field_name] = Money( + field_value, obj["fields"][get_currency_field_name(field_name, field)] + ) else: fields[field_name] = field_value obj["fields"] = fields diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/djmoney/settings.py new/django-money-2.1.1/djmoney/settings.py --- old/django-money-1.3.1/djmoney/settings.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/djmoney/settings.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,4 +1,6 @@ import operator +import warnings +from types import MappingProxyType from django.conf import settings @@ -23,9 +25,14 @@ CURRENCY_CHOICES.sort(key=operator.itemgetter(1, 0)) DECIMAL_PLACES = getattr(settings, "CURRENCY_DECIMAL_PLACES", 2) -DECIMAL_PLACES_DISPLAY = getattr( - settings, "CURRENCY_DECIMAL_PLACES_DISPLAY", {currency[0]: DECIMAL_PLACES for currency in CURRENCY_CHOICES} -) +_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) @@ -34,3 +41,7 @@ BASE_CURRENCY = getattr(settings, "BASE_CURRENCY", "USD") EXCHANGE_BACKEND = getattr(settings, "EXCHANGE_BACKEND", "djmoney.contrib.exchange.backends.OpenExchangeRatesBackend") RATES_CACHE_TIMEOUT = getattr(settings, "RATES_CACHE_TIMEOUT", 600) + +CURRENCY_CODE_MAX_LENGTH = getattr(settings, "CURRENCY_CODE_MAX_LENGTH", 3) + +MONEY_FORMAT = MappingProxyType(getattr(settings, "MONEY_FORMAT", {})) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/docs/changes.rst new/django-money-2.1.1/docs/changes.rst --- old/django-money-1.3.1/docs/changes.rst 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/docs/changes.rst 2022-01-02 18:39:41.000000000 +0100 @@ -1,9 +1,67 @@ Changelog ========= -`Unreleased`_ - TBD +`2.1.1`_ - 2022-01-02 +--------------------- + +**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`_) + +`2.1`_ - 2021-09-17 ------------------- +**Added** + +- Add support for Django 3.2. `#612`_ (`antonagestam`_) + +**Removed** + +- Drop support for Django 1.11, 2.1 and 3.0. `#612`_ (`antonagestam`_) +- Drop support for Python 3.5. `#612`_ (`antonagestam`_) + +`2.0.3`_ - 2021-09-04 +--------------------- + +**Fixed** + +- Inconsistent ``Money._copy_attributes`` behaviour when non-``Money`` instances are involved. `#630`_ (`tned73`_) + +`2.0.2`_ - 2021-09-04 +--------------------- + +**Fixed** + +- Inconsistent ``Money._copy_attributes`` behaviour. `#629`_ (`tned73`_) + +`2.0.1`_ - 2021-07-09 +--------------------- + +**Fixed** + +- Invalid deprecation warning behavior. `#624`_ (`nerdoc`_) + +`2.0`_ - 2021-05-23 +------------------- + +**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. + `1.3.1`_ - 2021-02-04 --------------------- @@ -651,7 +709,14 @@ - Initial public release -.. _Unreleased: https:///github.com/django-money/django-money/compare/1.3.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 +.. _2.0.2: https://github.com/django-money/django-money/compare/2.0.1...2.0.2 +.. _2.0.1: https://github.com/django-money/django-money/compare/2.0...2.0.1 +.. _2.0: https://github.com/django-money/django-money/compare/1.3.1...2.0 .. _1.3.1: https://github.com/django-money/django-money/compare/1.3...1.3.1 .. _1.3: https://github.com/django-money/django-money/compare/1.2.2...1.3 .. _1.2.2: https://github.com/django-money/django-money/compare/1.2.1...1.2.2 @@ -708,12 +773,21 @@ .. _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 +.. _#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 +.. _#630: https://github.com/django-money/django-money/pull/630 +.. _#629: https://github.com/django-money/django-money/pull/629 +.. _#624: https://github.com/django-money/django-money/issues/624 +.. _#612: https://github.com/django-money/django-money/pull/612 .. _#603: https://github.com/django-money/django-money/issues/603 +.. _#601: https://github.com/django-money/django-money/issues/601 .. _#595: https://github.com/django-money/django-money/issues/595 .. _#593: https://github.com/django-money/django-money/issues/593 .. _#586: https://github.com/django-money/django-money/issues/586 .. _#585: https://github.com/django-money/django-money/pull/585 .. _#583: https://github.com/django-money/django-money/issues/583 +.. _#567: https://github.com/django-money/django-money/issues/567 .. _#553: https://github.com/django-money/django-money/issues/553 .. _#541: https://github.com/django-money/django-money/issues/541 .. _#534: https://github.com/django-money/django-money/issues/534 @@ -825,6 +899,7 @@ .. _akumria: https://github.com/akumria .. _alexhayes: https://github.com/alexhayes .. _andytwoods: https://github.com/andytwoods +.. _antonagestam: https://github.com/antonagestam .. _arthurk: https://github.com/arthurk .. _astutejoe: https://github.com/astutejoe .. _benjaoming: https://github.com/benjaoming @@ -840,6 +915,7 @@ .. _eriktelepovsky: https://github.com/eriktelepovsky .. _evenicoulddoit: https://github.com/evenicoulddoit .. _f213: https://github.com/f213 +.. _flaeppe: https://github.com/flaeppe .. _Formulka: https://github.com/Formulka .. _glarrain: https://github.com/glarrain .. _graik: https://github.com/graik @@ -866,6 +942,7 @@ .. _msgre: https://github.com/msgre .. _mstarostik: https://github.com/mstarostik .. _niklasb: https://github.com/niklasb +.. _nerdoc: https://github.com/nerdoc .. _pjdelport: https://github.com/pjdelport .. _plumdog: https://github.com/plumdog .. _rach: https://github.com/rach diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/docs/conf.py new/django-money-2.1.1/docs/conf.py --- old/django-money-1.3.1/docs/conf.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/docs/conf.py 2022-01-02 18:39:41.000000000 +0100 @@ -52,9 +52,9 @@ master_doc = 'index' # General information about the project. -project = u'Django-money' -copyright = u'2016, Jacob Hansson' -author = u'Jacob Hansson' +project = 'Django-money' +copyright = '2016, Jacob Hansson' +author = 'Jacob Hansson' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -227,8 +227,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Django-money.tex', u'Django-money Documentation', - u'Jacob Hansson', 'manual'), + (master_doc, 'Django-money.tex', 'Django-money Documentation', + 'Jacob Hansson', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -257,7 +257,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'django-money', u'Django-money Documentation', + (master_doc, 'django-money', 'Django-money Documentation', [author], 1) ] @@ -271,7 +271,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Django-money', u'Django-money Documentation', + (master_doc, 'Django-money', 'Django-money Documentation', author, 'Django-money', 'One line description of project.', 'Miscellaneous'), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/pyproject.toml new/django-money-2.1.1/pyproject.toml --- old/django-money-1.3.1/pyproject.toml 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/pyproject.toml 2022-01-02 18:39:41.000000000 +0100 @@ -1,3 +1,7 @@ [build-system] requires = ["setuptools >= 40.6.0", "wheel"] build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 120 +target-version = ["py35"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/pytest.ini new/django-money-2.1.1/pytest.ini --- old/django-money-1.3.1/pytest.ini 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/pytest.ini 2022-01-02 18:39:41.000000000 +0100 @@ -1,2 +1,7 @@ [pytest] 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/setup.py new/django-money-2.1.1/setup.py --- old/django-money-1.3.1/setup.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/setup.py 2022-01-02 18:39:41.000000000 +0100 @@ -55,8 +55,10 @@ setup( name="django-money", version=find_version(), - description="Adds support for using money and currency fields in django models and forms. " - "Uses py-moneyed as the money implementation.", + description=( + "Adds support for using money and currency fields in django models and forms. " + "Uses py-moneyed as the money implementation." + ), long_description=read("README.rst"), long_description_content_type="text/x-rst", url="https://github.com/django-money/django-money", @@ -64,8 +66,8 @@ maintainer_email="g...@reinbach.com", license="BSD", packages=find_packages(include=["djmoney", "djmoney.*"]), - install_requires=["setuptools", "Django>=1.11", "py-moneyed>=0.8,<1.0"], - python_requires=">=3.5", + install_requires=["setuptools", "Django>=2.2", "py-moneyed>=1.2,<2.0"], + python_requires=">=3.6", platforms=["Any"], keywords=["django", "py-money", "money"], classifiers=[ @@ -74,14 +76,11 @@ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Framework :: Django", - "Framework :: Django :: 1.11", - "Framework :: Django :: 2.1", "Framework :: Django :: 2.2", - "Framework :: Django :: 3.0", "Framework :: Django :: 3.1", + "Framework :: Django :: 3.2", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/conftest.py new/django-money-2.1.1/tests/conftest.py --- old/django-money-1.3.1/tests/conftest.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/conftest.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,3 +1,5 @@ +from unittest import mock + import pytest from djmoney.contrib.exchange.models import ExchangeBackend, Rate, get_default_backend_name @@ -10,12 +12,12 @@ return ModelWithDefaultAsInt.objects.create(money=Money(100, "USD")) -@pytest.fixture() +@pytest.fixture def backend(): return ExchangeBackend.objects.create(name=get_default_backend_name(), base_currency="USD") -@pytest.fixture() +@pytest.fixture def autoconversion(backend, settings): settings.AUTO_CONVERT_MONEY = True Rate.objects.create(currency="EUR", value="0.88", backend=backend) @@ -29,3 +31,9 @@ pytest_plugins = "pytester" + + +@pytest.yield_fixture +def legacy_formatting(): + with mock.patch("djmoney.money.IS_DECIMAL_PLACES_DISPLAY_SET", True): + yield diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/contrib/exchange/test_model.py new/django-money-2.1.1/tests/contrib/exchange/test_model.py --- old/django-money-1.3.1/tests/contrib/exchange/test_model.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/contrib/exchange/test_model.py 2022-01-02 18:39:41.000000000 +0100 @@ -46,13 +46,13 @@ @pytest.mark.parametrize("source, target", (("NOK", "ZAR"), ("ZAR", "NOK"), ("USD", "ZAR"), ("ZAR", "USD"))) @pytest.mark.usefixtures("default_openexchange_rates") def test_unknown_currency_with_partially_exiting_currencies(source, target): - with pytest.raises(MissingRate, match="Rate %s \\-\\> %s does not exist" % (source, target)): + with pytest.raises(MissingRate, match=f"Rate {source} \\-\\> {target} does not exist"): get_rate(source, target) @pytest.mark.parametrize("source, target", (("USD", "EUR"), ("SEK", "ZWL"))) def test_unknown_currency(source, target): - with pytest.raises(MissingRate, match="Rate %s \\-\\> %s does not exist" % (source, target)): + with pytest.raises(MissingRate, match=f"Rate {source} \\-\\> {target} does not exist"): get_rate(source, target) @@ -106,4 +106,4 @@ """ ) ) - result.stdout.fnmatch_lines(["US$1.00"]) + result.stdout.fnmatch_lines(["$1.00"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/contrib/test_django_rest_framework.py new/django-money-2.1.1/tests/contrib/test_django_rest_framework.py --- old/django-money-1.3.1/tests/contrib/test_django_rest_framework.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/contrib/test_django_rest_framework.py 2022-01-02 18:39:41.000000000 +0100 @@ -1,3 +1,4 @@ +from collections import Counter from decimal import Decimal import pytest @@ -10,6 +11,7 @@ pytestmark = pytest.mark.django_db serializers = pytest.importorskip("rest_framework.serializers") fields = pytest.importorskip("rest_framework.fields") +djmoney_fields = pytest.importorskip("djmoney.contrib.django_rest_framework.fields") class TestMoneyField: @@ -111,12 +113,12 @@ @pytest.mark.parametrize( "value, error", ( - (Money(50, "EUR"), u"Ensure this value is greater than or equal to 100.00 ???."), - (Money(1500, "EUR"), u"Ensure this value is less than or equal to 1,000.00 ???."), + (Money(50, "EUR"), "Ensure this value is greater than or equal to ???100.00."), + (Money(1500, "EUR"), "Ensure this value is less than or equal to ???1,000.00."), (Money(40, "USD"), "Ensure this value is greater than or equal to $50.00."), (Money(600, "USD"), "Ensure this value is less than or equal to $500.00."), - (Money(400, "NOK"), "Ensure this value is greater than or equal to 500.00 Nkr."), - (Money(950, "NOK"), "Ensure this value is less than or equal to 900.00 Nkr."), + (Money(400, "NOK"), "Ensure this value is greater than or equal to NOK500.00."), + (Money(950, "NOK"), "Ensure this value is less than or equal to NOK900.00."), (Money(5, "SEK"), "Ensure this value is greater than or equal to 10."), (Money(1600, "SEK"), "Ensure this value is less than or equal to 1500."), ), @@ -144,3 +146,61 @@ ) assert not serializer.is_valid() assert serializer.errors["field"][0] == error + + @pytest.mark.parametrize( + ("data", "error_codes"), + [ + pytest.param( + {"money": "", "money_currency": "XUA"}, + [("money", "invalid")], + id="amount_as_empty_string", + ), + pytest.param( + {"money": None, "money_currency": "XUA"}, + [("money", "null")], + id="amount_as_none", + ), + pytest.param( + {"money": "v", "money_currency": "XUA"}, + [("money", "invalid")], + id="amount_as_invalid_decimal", + ), + pytest.param( + {"money": "0.01", "money_currency": "v"}, + [("money", "invalid_currency")], + id="invalid_currency", + ), + pytest.param( + {"money_currency": "SEK"}, + [("money", "required")], + id="amount_key_not_in_data", + ), + ], + ) + def test_errors_on(self, data, error_codes): + class Serializer(serializers.Serializer): + money = djmoney_fields.MoneyField(max_digits=9, decimal_places=2) + + serializer = Serializer(data=data) + with pytest.raises(serializers.ValidationError) as err: + serializer.is_valid(raise_exception=True) + + assert Counter([(field, code) for field, codes in err.value.get_codes().items() for code in codes]) == Counter( + error_codes + ) + + @pytest.mark.parametrize( + ("data", "expected"), + [ + pytest.param({"money": "0.01", "money_currency": None}, Decimal("0.01"), id="is_none"), + pytest.param({"money": "0.01", "money_currency": ""}, Decimal("0.01"), id="is_empty_string"), + pytest.param({"money": "0.01"}, Decimal("0.01"), id="key_not_in_data"), + ], + ) + def test_returns_decimal_when_currency(self, data, expected): + class Serializer(serializers.Serializer): + money = djmoney_fields.MoneyField(max_digits=9, decimal_places=2) + + serializer = Serializer(data=data) + serializer.is_valid(raise_exception=True) + assert serializer.validated_data["money"] == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/migrations/helpers.py new/django-money-2.1.1/tests/migrations/helpers.py --- old/django-money-1.3.1/tests/migrations/helpers.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/migrations/helpers.py 2022-01-02 18:39:41.000000000 +0100 @@ -19,7 +19,7 @@ def get_migration(name): - return __import__("money_app.migrations.%s_%s" % (name, MIGRATION_NAME), fromlist=["Migration"]).Migration + return __import__(f"money_app.migrations.{name}_{MIGRATION_NAME}", fromlist=["Migration"]).Migration def get_operations(migration_name): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/migrations/test_migrations.py new/django-money-2.1.1/tests/migrations/test_migrations.py --- old/django-money-1.3.1/tests/migrations/test_migrations.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/migrations/test_migrations.py 2022-01-02 18:39:41.000000000 +0100 @@ -66,8 +66,12 @@ ) tests_path = os.path.dirname(os.path.dirname(tests.__file__)) return self.run( - "import sys; sys.path.append('{}');".format(tests_path) - + "from tests.migrations.helpers import makemigrations; makemigrations();" + f""" + import sys + sys.path.append('{tests_path}') + from tests.migrations.helpers import makemigrations + makemigrations() + """ ) def make_default_migration(self, field="MoneyField(max_digits=10, decimal_places=2, null=True)"): @@ -77,31 +81,37 @@ return self.testdir.runpython_c( dedent( """ - import os - os.environ['DJANGO_SETTINGS_MODULE'] = 'app_settings' - from django import setup + import os + os.environ['DJANGO_SETTINGS_MODULE'] = 'app_settings' + from django import setup - setup() - %s - """ - % content + setup() + """ ) + + dedent(content) ) def create_instance(self): self.run( """ - from money_app.models import Model - from djmoney.money import Money + from money_app.models import Model + from djmoney.money import Money - Model.objects.create(field=Money(10, 'USD'))""" + Model.objects.create(field=Money(10, 'USD')) + """ ) def migrate(self): tests_path = os.path.dirname(os.path.dirname(tests.__file__)) return self.run( - "import sys; sys.path.append('{}');".format(tests_path) - + "from tests.migrations.helpers import migrate; migrate();" + dedent( + f""" + import sys + sys.path.append('{tests_path}') + from tests.migrations.helpers import migrate + migrate() + """ + ) ) def assert_migrate(self, output=None): @@ -200,13 +210,13 @@ self.assert_migrate(["*Applying money_app.0002_test... OK*"]) result = self.run( """ - from money_app.models import Model + from money_app.models import Model - instance = Model.objects.get() - print(instance.new_field) - """ + instance = Model.objects.get() + print(instance.new_field) + """ ) - result.stdout.fnmatch_lines(["US$10.00"]) + result.stdout.fnmatch_lines(["$10.00"]) def test_migrate_to_moneyfield(self): self.make_default_migration(field="models.DecimalField(max_digits=10, decimal_places=2, null=True)") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_admin.py new/django-money-2.1.1/tests/test_admin.py --- old/django-money-1.3.1/tests/test_admin.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_admin.py 2022-01-02 18:39:41.000000000 +0100 @@ -21,6 +21,24 @@ (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(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 + ), +) def test_display_for_field(settings, value, expected): settings.USE_L10N = True # This locale has no definitions in py-moneyed, so it will work for localized money representation. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_form.py new/django-money-2.1.1/tests/test_form.py --- old/django-money-1.3.1/tests/test_form.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_form.py 2022-01-02 18:39:41.000000000 +0100 @@ -131,12 +131,12 @@ @pytest.mark.parametrize( "value, error", ( - (Money(50, "EUR"), u"Ensure this value is greater than or equal to 100.00 ???."), - (Money(1500, "EUR"), u"Ensure this value is less than or equal to 1,000.00 ???."), + (Money(50, "EUR"), "Ensure this value is greater than or equal to ???100.00."), + (Money(1500, "EUR"), "Ensure this value is less than or equal to ???1,000.00."), (Money(40, "USD"), "Ensure this value is greater than or equal to $50.00."), (Money(600, "USD"), "Ensure this value is less than or equal to $500.00."), - (Money(400, "NOK"), "Ensure this value is greater than or equal to 500.00 Nkr."), - (Money(950, "NOK"), "Ensure this value is less than or equal to 900.00 Nkr."), + (Money(400, "NOK"), "Ensure this value is greater than or equal to NOK500.00."), + (Money(950, "NOK"), "Ensure this value is less than or equal to NOK900.00."), (Money(5, "SEK"), "Ensure this value is greater than or equal to 10."), (Money(1600, "SEK"), "Ensure this value is less than or equal to 1500."), ), @@ -171,7 +171,7 @@ def test_default_django_validator(self): form = MoneyModelFormWithValidation(data={"balance_0": 0, "balance_1": "GBP"}) assert not form.is_valid() - assert form.errors == {"balance": [u"Ensure this value is greater than or equal to GB??100.00."]} + assert form.errors == {"balance": ["Ensure this value is greater than or equal to ??100.00."]} class TestDisabledField: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_models.py new/django-money-2.1.1/tests/test_models.py --- old/django-money-1.3.1/tests/test_models.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_models.py 2022-01-02 18:39:41.000000000 +0100 @@ -706,7 +706,7 @@ When current locale is changed, Money instances should be represented correctly. """ with override("cs"): - assert str(Money(10, "CZK")) == "10.00 K??" + assert str(Money(10, "CZK")) == "10,00??K??" def test_properties_access(): @@ -787,3 +787,34 @@ instance = mixer.blend(ModelWithTwoMoneyFields) assert isinstance(instance.amount1, Money) assert isinstance(instance.amount2, Money) + + +@pytest.mark.parametrize( + ("attribute", "build_kwargs", "expected"), + [ + pytest.param( + "default_currency", + {"max_digits": 9, "null": True, "default_currency": None}, + None, + id="default_currency_as_none", + ), + pytest.param( + "default_currency", + {"max_digits": 9, "null": True, "default_currency": "SEK"}, + "SEK", + id="default_currency_as_non_default_not_none", + ), + pytest.param( + "currency_max_length", + {"max_digits": 9, "currency_max_length": 4}, + 4, + id="currency_max_length_as_non_default", + ), + ], +) +def test_deconstruct_includes(attribute, build_kwargs, expected): + instance = MoneyField(**build_kwargs) + __, ___, args, kwargs = instance.deconstruct() + new = MoneyField(*args, **kwargs) + assert getattr(new, attribute) == getattr(instance, attribute) + assert getattr(new, attribute) == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_money.py new/django-money-2.1.1/tests/test_money.py --- old/django-money-1.3.1/tests/test_money.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_money.py 2022-01-02 18:39:41.000000000 +0100 @@ -6,11 +6,19 @@ def test_repr(): - assert repr(Money("10.5", "USD")) == "<Money: 10.5 USD>" + 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__() == u"10.50\xa0???" + 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(): @@ -35,7 +43,29 @@ 10 / Money(5, "USD") -@pytest.mark.parametrize("locale, expected", (("pl", "PL_PL"), ("pl_PL", "pl_PL"))) +@pytest.mark.parametrize( + "locale, expected", + ( + ("pl", "PL_PL"), + ("pl_PL", "pl_PL"), + ), +) +def test_legacy_get_current_locale(locale, expected): + with override(locale): + assert get_current_locale(for_babel=False) == expected + + +@pytest.mark.parametrize( + "locale, expected", + ( + ("pl", "pl"), + ("pl-pl", "pl_PL"), + ("sv", "sv"), + ("sv-se", "sv_SE"), + ("en-us", "en_US"), + ("en-gb", "en_GB"), + ), +) def test_get_current_locale(locale, expected): with override(locale): assert get_current_locale() == expected @@ -123,3 +153,59 @@ for bill in bills: total -= bill assert total == Money(-33, "EUR") + + +@pytest.mark.parametrize( + "decimal_places_display, decimal_places", + [ + [None, None], + [0, 0], + [1, 0], + [4, 0], + [0, 1], + [1, 1], + [4, 1], + [0, 4], + [1, 4], + [4, 4], + [None, 4], + [None, 1], + [None, 0], + [4, None], + [1, None], + [0, None], + ], +) +def test_proper_copy_of_attributes(decimal_places_display, decimal_places): + one = Money(1, "EUR", decimal_places_display=decimal_places_display) + + assert one._decimal_places_display is decimal_places_display + assert one.decimal_places == 2, "default value" + + two = Money(2, "EUR", decimal_places=decimal_places) + + assert two._decimal_places_display is None, "default value" + assert two.decimal_places == decimal_places + + result = Money(3, "EUR") + one._copy_attributes(two, result) + + assert result._decimal_places_display == decimal_places_display + assert result.decimal_places == max(2, decimal_places) if decimal_places is not None else 2 + + result = Money(0, "EUR") + one._copy_attributes(Money(1, "EUR", decimal_places_display=3), result) + + assert result._decimal_places_display == max(3, decimal_places_display) if decimal_places_display is not None else 3 + + result = Money(0, "EUR") + one._copy_attributes(1, result) + + assert result._decimal_places_display == decimal_places_display + assert result.decimal_places == 2 + + result = Money(0, "EUR") + two._copy_attributes(1, result) + + assert result._decimal_places_display is None + assert result.decimal_places == decimal_places if decimal_places else 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_serialization.py new/django-money-2.1.1/tests/test_serialization.py --- old/django-money-1.3.1/tests/test_serialization.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_serialization.py 2022-01-02 18:39:41.000000000 +0100 @@ -9,7 +9,7 @@ from djmoney.money import Money from djmoney.serializers import Deserializer, Serializer -from .testapp.models import ModelWithDefaultAsInt +from .testapp.models import ModelWithDefaultAsInt, ModelWithSharedCurrency pytestmark = pytest.mark.django_db @@ -95,3 +95,16 @@ with patch("django.core.serializers.python._get_model", _get_model): loaddata(fixture_file) assert ModelWithDefaultAsInt.objects.get().money == Money(1, "USD") + + +def test_serialize_currency_field(fixture_file): + data = """[ + { + "model": "testapp.modelwithsharedcurrency", + "pk": 1, + "fields": {"first": "1.00", "second": "2.00", "currency": "USD"} + } +]""" + fixture_file.write(data) + loaddata(fixture_file) + assert ModelWithSharedCurrency.objects.get().first == Money(1, "USD") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tests/test_tags.py new/django-money-2.1.1/tests/test_tags.py --- old/django-money-1.3.1/tests/test_tags.py 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tests/test_tags.py 2022-01-02 18:39:41.000000000 +0100 @@ -84,6 +84,49 @@ ), ), ) +def test_tag_with_legacy_formatting(legacy_formatting, string, result, context): + assert_template(string, result, context) + + +@pytest.mark.parametrize( + "string, result, context", + ( + ('{% load djmoney %}{% money_localize "2.5" "PLN" as NEW_M %}{{NEW_M}}', "2,50\xa0z??", {}), + ('{% load djmoney %}{% money_localize "2.5" "PLN" %}', "2,50\xa0z??", {}), + ("{% load djmoney %}{% money_localize amount currency %}", "2,60\xa0z??", {"amount": 2.6, "currency": "PLN"}), + ("{% load djmoney %}{% money_localize money as NEW_M %}{{NEW_M}}", "2,30\xa0z??", {"money": Money(2.3, "PLN")}), + ( + "{% load djmoney %}{% money_localize money off as NEW_M %}{{NEW_M}}", + "2,30\xa0z??", + {"money": Money(2.3, "PLN")}, + ), + ( + "{% load djmoney %}{% money_localize money off as NEW_M %}{{NEW_M}}", + "0,00\xa0z??", + {"money": Money(0, "PLN")}, + ), + ( + # with a tag template "money_localize" + "{% load djmoney %}{% money_localize money %}", + "2,30\xa0z??", + {"money": Money(2.3, "PLN")}, + ), + ( + # without a tag template "money_localize" + "{{ money }}", + "2,30\xa0z??", + {"money": Money(2.3, "PLN")}, + ), + ("{% load djmoney %}{% money_localize money off %}", "2,30\xa0z??", {"money": Money(2.3, "PLN")}), + ("{% load djmoney %}{% money_localize money on %}", "2,30\xa0z??", {"money": Money(2.3, "PLN")}), + ( + # in django 2.0 we fail inside the for loop + '{% load djmoney %}{% for i in "xxx" %}{% money_localize money %} {% endfor %}', + "2,30\xa0z?? 2,30\xa0z?? 2,30\xa0z?? ", + {"money": Money(2.3, "PLN"), "test": "test"}, + ), + ), +) def test_tag(string, result, context): assert_template(string, result, context) @@ -94,16 +137,16 @@ ( # money_localize has a default setting USE_L10N = True "{% load djmoney %}{% money_localize money %}", - "2,30 z??", + "2,30\xa0z??", {"money": Money(2.3, "PLN")}, ), ( # without a tag template "money_localize" "{{ money }}", - "2.30 z??", + "2,30\xa0z??", {"money": Money(2.3, "PLN")}, ), - ("{% load djmoney %}{% money_localize money on %}", "2,30 z??", {"money": Money(2.3, "PLN")}), + ("{% load djmoney %}{% money_localize money on %}", "2,30\xa0z??", {"money": Money(2.3, "PLN")}), ), ) def test_l10n_off(settings, string, result, context): @@ -114,4 +157,4 @@ def test_forced_l10n(): mp = Money(2.3, "PLN") mp.use_l10n = True - assert_template("{{ money }}", "2,30 z??", {"money": mp}) + assert_template("{{ money }}", "2,30\xa0z??", {"money": mp}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-money-1.3.1/tox.ini new/django-money-2.1.1/tox.ini --- old/django-money-1.3.1/tox.ini 2021-02-04 10:32:33.000000000 +0100 +++ new/django-money-2.1.1/tox.ini 2022-01-02 18:39:41.000000000 +0100 @@ -1,11 +1,9 @@ [tox] envlist = - django_master-py{39,38,37,36,py3} + django_main-py{39,38,37,36,py3} + django32-py{39,38,37,36,py3} django31-py{39,38,37,36,py3} - django30-py{39,38,37,36,py3} django22-py{39,38,37,36,py3} - django21-py{37,36,35,py3} - django111-py{36,35,py3} lint docs skipsdist = true @@ -17,51 +15,37 @@ [testenv] deps = .[test,exchange] - django111: {[django]1.11.x} - django21: {[django]2.1.x} django22: {[django]2.2.x} - django30: {[django]3.0.x} django31: {[django]3.1.x} - django_master: {[django]master} -commands = py.test --ds=tests.settings_reversion --cov=./djmoney {posargs} -W error::DeprecationWarning + django32: {[django]3.2.x} + django_main: {[django]main} +commands = py.test --ds=tests.settings_reversion --cov=./djmoney {posargs} usedevelop = false -[testenv:django_master-py{39,38,37,36,py3}] -commands = py.test --ds=tests.settings --cov=./djmoney {posargs} -W error::DeprecationWarning +[testenv:django_main-py{39,38,py3}] +commands = py.test --ds=tests.settings --cov=./djmoney {posargs} [testenv:lint] deps = - flake8 - isort - black + pre-commit commands = - flake8 {toxinidir}/djmoney {toxinidir}/tests - isort -c {toxinidir}/djmoney {toxinidir}/tests - black -l 120 --check --diff {toxinidir}/djmoney {toxinidir}/tests setup.py + pre-commit run --all-files [django] -1.11.x = - Django>=1.11.0,<2.0.0 - django-reversion>=2.0.8,<3.0.8 - djangorestframework>=3.6.2 -2.1.x = - Django>=2.1,<2.2 - django-reversion>=2.0.8 - djangorestframework>=3.7.3 2.2.x = Django>=2.2,<2.3 django-reversion>=2.0.8 djangorestframework>=3.7.3 -3.0.x = - Django>=3.0,<3.1 - django-reversion>=2.0.8 - djangorestframework>=3.7.3 3.1.x = Django>=3.1,<3.2 django-reversion>=3.0.8 djangorestframework>=3.12.0 -master = - https://github.com/django/django/tarball/master +3.2.x = + Django>=3.2,<3.3 + django-reversion>=3.0.8 + djangorestframework>=3.12.0 +main = + https://github.com/django/django/tarball/main djangorestframework>=3.12.0 [testenv:no_rest_framework] @@ -71,7 +55,7 @@ django-reversion>=3.0.8 [testenv:docs] -basepython = python3.6 +allowlist_externals = make changedir = docs deps = sphinx ++++++ merged_pr_657.patch ++++++ >From b140c16ca8f9ed0227f5295878c3f6b346a8472c Mon Sep 17 00:00:00 2001 From: David Szotten <davidszot...@gmail.com> 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 <davidszot...@gmail.com> 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 <davidszot...@gmail.com> 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 <davidszot...@gmail.com> 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 <davidszot...@gmail.com> 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 ++++++ pr_638.patch ++++++ ++++ 780 lines (skipped)