Skip to content

Commit 62335cf

Browse files
committed
feat: BinIntegerField and COERCE_BIGINT_TO_STRING setting, bigint now can have string api representation
1 parent 1472848 commit 62335cf

File tree

7 files changed

+117
-3
lines changed

7 files changed

+117
-3
lines changed

docs/api-guide/fields.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,18 @@ Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.
269269
* `max_value` Validate that the number provided is no greater than this value.
270270
* `min_value` Validate that the number provided is no less than this value.
271271

272+
## BigIntegerField
273+
274+
An biginteger representation.
275+
276+
Corresponds to `django.db.models.fields.BigIntegerField`.
277+
278+
**Signature**: `BigIntegerField(max_value=None, min_value=None, coerce_to_string=None)`
279+
280+
* `max_value` Validate that the number provided is no greater than this value.
281+
* `min_value` Validate that the number provided is no less than this value.
282+
* `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 `True` unless overridden. If `BigInterger` objects are returned by the serializer, then the final output format will be determined by the renderer.
283+
272284
## FloatField
273285

274286
A floating point representation.

docs/api-guide/settings.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,14 @@ When set to `True`, the serializer `DecimalField` class will return strings inst
371371

372372
Default: `True`
373373

374+
#### COERCE_BIGINT_TO_STRING
375+
376+
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.
377+
378+
When set to `True`, the serializer `BigIntegerField` class 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.
379+
380+
Default: `False`
381+
374382
---
375383

376384
## View names and descriptions

rest_framework/fields.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,36 @@ def to_representation(self, value):
921921
return int(value)
922922

923923

924+
class BigIntegerField(IntegerField):
925+
926+
default_error_messages = {
927+
'invalid': _('A valid biginteger is required.'),
928+
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
929+
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
930+
'max_string_length': _('String value too large.')
931+
}
932+
933+
def __init__(self, coerce_to_string=None, **kwargs):
934+
super().__init__(**kwargs)
935+
936+
if coerce_to_string is not None:
937+
self.coerce_to_string = coerce_to_string
938+
939+
def to_representation(self, value):
940+
coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_BIGINT_TO_STRING)
941+
942+
if value is None:
943+
if coerce_to_string:
944+
return ''
945+
else:
946+
return None
947+
948+
if coerce_to_string:
949+
return str(value)
950+
else:
951+
return int(value)
952+
953+
924954
class FloatField(Field):
925955
default_error_messages = {
926956
'invalid': _('A valid number is required.'),

rest_framework/serializers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
get_referenced_base_fields_from_q, postgres_fields
3131
)
3232
from rest_framework.exceptions import ErrorDetail, ValidationError
33-
from rest_framework.fields import get_error_detail
33+
from rest_framework.fields import get_error_detail, BigIntegerField
3434
from rest_framework.settings import api_settings
3535
from rest_framework.utils import html, model_meta, representation
3636
from rest_framework.utils.field_mapping import (
@@ -906,7 +906,8 @@ class ModelSerializer(Serializer):
906906
"""
907907
serializer_field_mapping = {
908908
models.AutoField: IntegerField,
909-
models.BigIntegerField: IntegerField,
909+
models.BigAutoField: BigIntegerField,
910+
models.BigIntegerField: BigIntegerField,
910911
models.BooleanField: BooleanField,
911912
models.CharField: CharField,
912913
models.CommaSeparatedIntegerField: CharField,

rest_framework/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
'COMPACT_JSON': True,
117117
'STRICT_JSON': True,
118118
'COERCE_DECIMAL_TO_STRING': True,
119+
'COERCE_BIGINT_TO_STRING': False,
119120
'UPLOADED_FILES_USE_URL': True,
120121

121122
# Browsable API

tests/test_fields.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,68 @@ class TestMinMaxIntegerField(FieldValues):
10991099
field = serializers.IntegerField(min_value=1, max_value=3)
11001100

11011101

1102+
class TestBigIntegerField(FieldValues):
1103+
"""
1104+
Valid and invalid values for `BigIntegerField`.
1105+
"""
1106+
valid_inputs = {
1107+
'1': 1,
1108+
'0': 0,
1109+
1: 1,
1110+
0: 0,
1111+
123: 123,
1112+
-123: -123,
1113+
'999999999999999999999999999': 999999999999999999999999999,
1114+
-999999999999999999999999999: -999999999999999999999999999,
1115+
1.0: 1,
1116+
0.0: 0,
1117+
'1.0': 1
1118+
}
1119+
invalid_inputs = {
1120+
0.5: ['A valid biginteger is required.'],
1121+
'abc': ['A valid biginteger is required.'],
1122+
'0.5': ['A valid biginteger is required.']
1123+
}
1124+
outputs = {
1125+
'1': 1,
1126+
'0': 0,
1127+
1: 1,
1128+
0: 0,
1129+
1.0: 1,
1130+
0.0: 0,
1131+
'999999999999999999999999999': 999999999999999999999999999,
1132+
-999999999999999999999999999: -999999999999999999999999999
1133+
}
1134+
field = serializers.BigIntegerField()
1135+
1136+
1137+
class TestMinMaxBigIntegerField(FieldValues):
1138+
"""
1139+
Valid and invalid values for `IntegerField` with min and max limits.
1140+
"""
1141+
valid_inputs = {
1142+
'1': 1,
1143+
'3': 3,
1144+
1: 1,
1145+
3: 3,
1146+
}
1147+
invalid_inputs = {
1148+
0: ['Ensure this value is greater than or equal to 1.'],
1149+
4: ['Ensure this value is less than or equal to 3.'],
1150+
'0': ['Ensure this value is greater than or equal to 1.'],
1151+
'4': ['Ensure this value is less than or equal to 3.'],
1152+
}
1153+
outputs = {}
1154+
field = serializers.BigIntegerField(min_value=1, max_value=3)
1155+
1156+
1157+
class TestCoercionBigIntegerField(TestCase):
1158+
1159+
def test_force_coerce_to_string(self):
1160+
field = serializers.BigIntegerField(coerce_to_string=True)
1161+
assert isinstance(field.to_representation(int('1')), str)
1162+
1163+
11021164
class TestFloatField(FieldValues):
11031165
"""
11041166
Valid and invalid values for `FloatField`.

tests/test_model_serializer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class Meta:
171171
expected = dedent(r"""
172172
TestSerializer\(\):
173173
auto_field = IntegerField\(read_only=True\)
174-
big_integer_field = IntegerField\(.*\)
174+
big_integer_field = BigIntegerField\(.*\)
175175
boolean_field = BooleanField\(required=False\)
176176
char_field = CharField\(max_length=100\)
177177
comma_separated_integer_field = CharField\(max_length=100, validators=\[<django.core.validators.RegexValidator object>\]\)

0 commit comments

Comments
 (0)