Skip to content

Commit 51aefc7

Browse files
committed
Add coerce_numbers_to_str param to Field, add tests
1 parent 5611bda commit 51aefc7

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

sqlmodel/main.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import builtins
44
import ipaddress
55
import uuid
6+
import warnings
67
import weakref
78
from collections.abc import Mapping, Sequence, Set
89
from datetime import date, datetime, time, timedelta
@@ -214,6 +215,7 @@ def Field(
214215
exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
215216
include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
216217
const: Optional[bool] = None,
218+
coerce_numbers_to_str: Optional[bool] = None,
217219
gt: Optional[float] = None,
218220
ge: Optional[float] = None,
219221
lt: Optional[float] = None,
@@ -257,6 +259,7 @@ def Field(
257259
exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
258260
include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
259261
const: Optional[bool] = None,
262+
coerce_numbers_to_str: Optional[bool] = None,
260263
gt: Optional[float] = None,
261264
ge: Optional[float] = None,
262265
lt: Optional[float] = None,
@@ -309,6 +312,7 @@ def Field(
309312
exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
310313
include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
311314
const: Optional[bool] = None,
315+
coerce_numbers_to_str: Optional[bool] = None,
312316
gt: Optional[float] = None,
313317
ge: Optional[float] = None,
314318
lt: Optional[float] = None,
@@ -342,6 +346,7 @@ def Field(
342346
exclude: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
343347
include: Union[Set[Union[int, str]], Mapping[Union[int, str], Any], Any] = None,
344348
const: Optional[bool] = None,
349+
coerce_numbers_to_str: Optional[bool] = None,
345350
gt: Optional[float] = None,
346351
ge: Optional[float] = None,
347352
lt: Optional[float] = None,
@@ -371,16 +376,26 @@ def Field(
371376
schema_extra: Optional[dict[str, Any]] = None,
372377
) -> Any:
373378
current_schema_extra = schema_extra or {}
379+
380+
for param_name in ("coerce_numbers_to_str",):
381+
if param_name in current_schema_extra:
382+
msg = f"Pass `{param_name}` parameter directly to Field instead of passing it via `schema_extra`"
383+
warnings.warn(msg, UserWarning, stacklevel=2)
384+
374385
# Extract possible alias settings from schema_extra so we can control precedence
375386
schema_validation_alias = current_schema_extra.pop("validation_alias", None)
376387
schema_serialization_alias = current_schema_extra.pop("serialization_alias", None)
388+
current_coerce_numbers_to_str = coerce_numbers_to_str or current_schema_extra.pop(
389+
"coerce_numbers_to_str", None
390+
)
377391
field_info_kwargs = {
378392
"alias": alias,
379393
"title": title,
380394
"description": description,
381395
"exclude": exclude,
382396
"include": include,
383397
"const": const,
398+
"coerce_numbers_to_str": current_coerce_numbers_to_str,
384399
"gt": gt,
385400
"ge": ge,
386401
"lt": lt,

tests/test_pydantic/test_field.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,36 @@ class Model(SQLModel):
5454

5555
instance = Model(id=123, foo="bar")
5656
assert "foo=" not in repr(instance)
57+
58+
59+
def test_coerce_numbers_to_str_true():
60+
class Model(SQLModel):
61+
val: str = Field(coerce_numbers_to_str=True)
62+
63+
assert Model.model_validate({"val": 123}).val == "123"
64+
assert Model.model_validate({"val": 45.67}).val == "45.67"
65+
66+
67+
@pytest.mark.parametrize("coerce_numbers_to_str", [None, False])
68+
def test_coerce_numbers_to_str_false(coerce_numbers_to_str: Optional[bool]):
69+
class Model2(SQLModel):
70+
val: str = Field(coerce_numbers_to_str=coerce_numbers_to_str)
71+
72+
with pytest.raises(ValidationError):
73+
Model2.model_validate({"val": 123})
74+
75+
76+
def test_coerce_numbers_to_str_via_schema_extra(): # Current workaround. Remove after some time
77+
with pytest.warns(
78+
UserWarning,
79+
match=(
80+
"Pass `coerce_numbers_to_str` parameter directly to Field instead of passing "
81+
"it via `schema_extra`"
82+
),
83+
):
84+
85+
class Model(SQLModel):
86+
val: str = Field(schema_extra={"coerce_numbers_to_str": True})
87+
88+
assert Model.model_validate({"val": 123}).val == "123"
89+
assert Model.model_validate({"val": 45.67}).val == "45.67"

0 commit comments

Comments
 (0)