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)

Reply via email to