Bigint coerce to string (#9775)

* feat: BinIntegerField and COERCE_BIGINT_TO_STRING setting, bigint now can have string api representation

* fix: wrong import location in serializers.py

* feat: updated with changes requests
- typo fix
- value test fix
- import order fix

* Update tests/test_fields.py

* Update docs/api-guide/fields.md

* Update docs/api-guide/fields.md



* refactor: changed BigIntegerField.to_representation simplified + calling super

---------

Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} <auvipy@gmail.com>
This commit is contained in:
HoodyH 2025-12-02 15:44:53 +01:00 committed by GitHub
parent 1c4af77c3b
commit f9f10e041f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 111 additions and 3 deletions

View File

@ -269,6 +269,18 @@ Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.
* `max_value` Validate that the number provided is no greater than this value.
* `min_value` Validate that the number provided is no less than this value.
## BigIntegerField
A biginteger representation.
Corresponds to `django.db.models.fields.BigIntegerField`.
**Signature**: `BigIntegerField(max_value=None, min_value=None, coerce_to_string=None)`
* `max_value` Validate that the number provided is no greater than this value.
* `min_value` Validate that the number provided is no less than this value.
* `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `BigInteger` objects should be returned. Defaults to the same value as the `COERCE_BIGINT_TO_STRING` settings key, which will be `False` unless overridden. If `BigInteger` objects are returned by the serializer, then the final output format will be determined by the renderer.
## FloatField
A floating point representation.

View File

@ -371,6 +371,14 @@ When set to `True`, the serializer `DecimalField` class will return strings inst
Default: `True`
#### COERCE_BIGINT_TO_STRING
When returning biginteger objects in API representations that do not support numbers up to 2^64, it is best to return the value as a string. This avoids the loss of precision that occurs with biginteger implementations.
When set to `True`, the serializer `BigIntegerField` class (by default) will return strings instead of `BigInteger` objects. When set to `False`, serializers will return `BigInteger` objects, which the default JSON encoder will return as numbers.
Default: `False`
---
## View names and descriptions

View File

@ -921,6 +921,28 @@ class IntegerField(Field):
return int(value)
class BigIntegerField(IntegerField):
default_error_messages = {
'invalid': _('A valid biginteger is required.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'max_string_length': _('String value too large.')
}
def __init__(self, coerce_to_string=None, **kwargs):
super().__init__(**kwargs)
if coerce_to_string is not None:
self.coerce_to_string = coerce_to_string
def to_representation(self, value):
if getattr(self, 'coerce_to_string', api_settings.COERCE_BIGINT_TO_STRING):
return '' if value is None else str(value)
return super().to_representation(value)
class FloatField(Field):
default_error_messages = {
'invalid': _('A valid number is required.'),

View File

@ -53,7 +53,7 @@ from rest_framework.validators import (
# This helps keep the separation between model fields, form fields, and
# serializer fields more explicit.
from rest_framework.fields import ( # NOQA # isort:skip
BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
BigIntegerField, BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,
HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,
ListField, ModelField, MultipleChoiceField, ReadOnlyField,
@ -906,7 +906,8 @@ class ModelSerializer(Serializer):
"""
serializer_field_mapping = {
models.AutoField: IntegerField,
models.BigIntegerField: IntegerField,
models.BigAutoField: BigIntegerField,
models.BigIntegerField: BigIntegerField,
models.BooleanField: BooleanField,
models.CharField: CharField,
models.CommaSeparatedIntegerField: CharField,

View File

@ -116,6 +116,7 @@ DEFAULTS = {
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'COERCE_BIGINT_TO_STRING': False,
'UPLOADED_FILES_USE_URL': True,
# Browsable API

View File

@ -1099,6 +1099,70 @@ class TestMinMaxIntegerField(FieldValues):
field = serializers.IntegerField(min_value=1, max_value=3)
class TestBigIntegerField(FieldValues):
"""
Valid and invalid values for `BigIntegerField`.
"""
valid_inputs = {
'1': 1,
'0': 0,
1: 1,
0: 0,
123: 123,
-123: -123,
'999999999999999999999999999': 999999999999999999999999999,
-999999999999999999999999999: -999999999999999999999999999,
1.0: 1,
0.0: 0,
'1.0': 1
}
invalid_inputs = {
0.5: ['A valid biginteger is required.'],
'abc': ['A valid biginteger is required.'],
'0.5': ['A valid biginteger is required.']
}
outputs = {
'1': 1,
'0': 0,
1: 1,
0: 0,
1.0: 1,
0.0: 0,
'999999999999999999999999999': 999999999999999999999999999,
-999999999999999999999999999: -999999999999999999999999999
}
field = serializers.BigIntegerField()
class TestMinMaxBigIntegerField(FieldValues):
"""
Valid and invalid values for `BigIntegerField` with min and max limits.
"""
valid_inputs = {
'1': 1,
'3': 3,
1: 1,
3: 3,
}
invalid_inputs = {
0: ['Ensure this value is greater than or equal to 1.'],
4: ['Ensure this value is less than or equal to 3.'],
'0': ['Ensure this value is greater than or equal to 1.'],
'4': ['Ensure this value is less than or equal to 3.'],
}
outputs = {}
field = serializers.BigIntegerField(min_value=1, max_value=3)
class TestCoercionBigIntegerField(TestCase):
def test_force_coerce_to_string(self):
field = serializers.BigIntegerField(coerce_to_string=True)
value = field.to_representation(1)
assert isinstance(value, str)
assert value == "1"
class TestFloatField(FieldValues):
"""
Valid and invalid values for `FloatField`.

View File

@ -171,7 +171,7 @@ class TestRegularFieldMappings(TestCase):
expected = dedent(r"""
TestSerializer\(\):
auto_field = IntegerField\(read_only=True\)
big_integer_field = IntegerField\(.*\)
big_integer_field = BigIntegerField\(.*\)
boolean_field = BooleanField\(required=False\)
char_field = CharField\(max_length=100\)
comma_separated_integer_field = CharField\(max_length=100, validators=\[<django.core.validators.RegexValidator object>\]\)