﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
26963	Improve error message when trying to insert a value that overflows DecimalField	Floris den Hengst	nobody	"[https://docs.djangoproject.com/en/1.9/ref/models/fields/#decimalfield) DecimalField] accepts `max_digit` parameter that specifies the amount of digit positions used by the `DecimalField`.

Consider the following model that allows for only one digit before the decimal separator:
{{{#!python
from django.db.models import Model
class MyModel(Model):
    my_field = DecimalField(max_digits=2, decimal_places=1)
}}}
A value of `Decimal(""10.1"")` is not supported, as it requires 3 decimal places, however its input is accepted until the value is being inserted in the database.

[https://github.com/django/django/blob/cd2e4293cbf3416af6c478c5aaab711cae88892c/django/db/backends/utils.py#L204 This line] in `django.db.backends.utils.format_number` fails when any insertion or update is attempted  (e.g. using `save()` , `bulk_create` etc. ), because `quantize` raises an `InvalidOperation` when there are not enough positions to fit the rounded value (according to `Decimal.quantize` [https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize docs]).

The error message is not very informative (0).

[https://github.com/florisdenhengst/django/commit/0f109a9bca5b20361e11a54c12fa786a4263bc4e This commit] contains a test that triggers the error. Note that overflow might also happen because of rounding during the quantization (e.g. values of 9.99999 could result in 10.0 during quantization, depending on the [https://docs.python.org/3/library/decimal.html#decimal.Context Context] used).
For a copy of the commit see (1).


(0) Example error message
{{{
Traceback (most recent call last):
  File ""/path_to_project/file.py"", line 123, in my_failing_function
    overflowing_instance.save()
  File ""/path_to_django/db/models/base.py"", line 796, in save
    force_update=force_update, update_fields=update_fields)
  File ""/path_to_django/db/models/base.py"", line 824, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File ""/path_to_django/db/models/base.py"", line 908, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File ""/path_to_django/db/models/base.py"", line 947, in _do_insert
    using=using, raw=raw)
  File ""/path_to_django/db/models/manager.py"", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File ""/path_to_django/db/models/query.py"", line 1046, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File ""/path_to_django/db/models/sql/compiler.py"", line 1053, in execute_sql
    for sql, params in self.as_sql():
  File ""/path_to_django/db/models/sql/compiler.py"", line 1006, in as_sql
    for obj in self.query.objs
  File ""/path_to_django/db/models/sql/compiler.py"", line 1006, in <listcomp>
    for obj in self.query.objs
  File ""/path_to_django/db/models/sql/compiler.py"", line 1005, in <listcomp>
    [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]
  File ""/path_to_django/db/models/sql/compiler.py"", line 945, in prepare_value
    value = field.get_db_prep_save(value, connection=self.connection)
  File ""/path_to_django/db/models/fields/__init__.py"", line 1587, in get_db_prep_save
    return connection.ops.adapt_decimalfield_value(self.to_python(value), self.max_digits, self.decimal_places)
  File ""/path_to_django/db/backends/base/operations.py"", line 495, in adapt_decimalfield_value
    return utils.format_number(value, max_digits, decimal_places)
  File ""/path_to_django/db/backends/utils.py"", line 204, in format_number
    value = value.quantize(decimal.Decimal("".1"") ** decimal_places, context=context)
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
}}}

(1)Test that triggers the error (add to `django.tests.model_fields.test_decimalfield.DecimalFieldTests`)
{{{#!python
    def test_save_with_max_digits_overflow(self):
        """"""
        Ensure overflowing decimals yield a meaningful error.
        """"""
        overflowing_value = Decimal(10 ** 6)
        expected_message = ""Not enough digit positions in field 'd' to represent {}"".format(overflowing_value) # some meaningful error message
        overflowing_instance = Foo(a='a', d=overflowing_value)
        with self.assertRaisesMessage(ValidationError, # some meaningful error
            expected_message):
            overflowing_instance.save()
}}}

"	Cleanup/optimization	closed	Database layer (models, ORM)	dev	Normal	invalid	DecimalField, ValidationError, quantize		Unreviewed	0	0	0	0	0	0
