Coverage for tests / test_pydantic / test_field.py: 99%
73 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-11-09 00:02 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-11-09 00:02 +0000
1from decimal import Decimal 1stuvwxyzA
2from typing import Annotated, Any, Literal 1stuvwxyzA
4import pytest 1stuvwxyzA
5from pydantic import ValidationError 1stuvwxyzA
6from sqlmodel import Discriminator, Field, SQLModel, Tag 1stuvwxyzA
9def test_decimal(): 1stuvwxyzA
10 class Model(SQLModel): 1BCDEFGHIJ
11 dec: Decimal = Field(max_digits=4, decimal_places=2) 1BCDEFGHIJ
13 Model(dec=Decimal("3.14")) 1BCDEFGHIJ
14 Model(dec=Decimal("69.42")) 1BCDEFGHIJ
16 with pytest.raises(ValidationError): 1BCDEFGHIJ
17 Model(dec=Decimal("3.142")) 1BCDEFGHIJ
18 with pytest.raises(ValidationError): 1BCDEFGHIJ
19 Model(dec=Decimal("0.069")) 1BCDEFGHIJ
20 with pytest.raises(ValidationError): 1BCDEFGHIJ
21 Model(dec=Decimal("420")) 1BCDEFGHIJ
24def test_discriminator(): 1stuvwxyzA
25 # Example adapted from
26 # [Pydantic docs](https://pydantic-docs.helpmanual.io/usage/types/#discriminated-unions-aka-tagged-unions):
28 class Cat(SQLModel): 1abcdefghi
29 pet_type: Literal["cat"] 1abcdefghi
30 meows: int 1abcdefghi
32 class Dog(SQLModel): 1abcdefghi
33 pet_type: Literal["dog"] 1abcdefghi
34 barks: float 1abcdefghi
36 class Lizard(SQLModel): 1abcdefghi
37 pet_type: Literal["reptile", "lizard"] 1abcdefghi
38 scales: bool 1abcdefghi
40 class Model(SQLModel): 1abcdefghi
41 pet: Cat | Dog | Lizard = Field(..., discriminator="pet_type") 1abcdefghi
42 n: int 1abcdefghi
44 Model(pet={"pet_type": "dog", "barks": 3.14}, n=1) # type: ignore[arg-type] 1abcdefghi
46 with pytest.raises(ValidationError): 1abcdefghi
47 Model(pet={"pet_type": "dog"}, n=1) # type: ignore[arg-type] 1abcdefghi
50def test_discriminator_callable(): 1stuvwxyzA
51 # Example adapted from
52 # [Pydantic docs](https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions-with-callable-discriminator):
54 class Pie(SQLModel): 1jklmnopqr
55 pass 1jklmnopqr
57 class ApplePie(Pie): 1jklmnopqr
58 fruit: Literal["apple"] = "apple" 1jklmnopqr
60 class PumpkinPie(Pie): 1jklmnopqr
61 filling: Literal["pumpkin"] = "pumpkin" 1jklmnopqr
63 def get_discriminator_value(v: Any) -> str: 1jklmnopqr
64 if isinstance(v, dict): 1jklmnopqr
65 return v.get("fruit", v.get("filling")) 1jklmnopqr
66 return getattr(v, "fruit", getattr(v, "filling", None))
68 class ThanksgivingDinner(SQLModel): 1jklmnopqr
69 dessert: ( 1jklmnopqr
70 Annotated[ApplePie, Tag("apple")] | Annotated[PumpkinPie, Tag("pumpkin")]
71 ) = Field(
72 discriminator=Discriminator(get_discriminator_value),
73 )
75 apple_pie = ThanksgivingDinner.model_validate({"dessert": {"fruit": "apple"}}) 1jklmnopqr
76 assert isinstance(apple_pie.dessert, ApplePie) 1jklmnopqr
78 pumpkin_pie = ThanksgivingDinner.model_validate({"dessert": {"filling": "pumpkin"}}) 1jklmnopqr
79 assert isinstance(pumpkin_pie.dessert, PumpkinPie) 1jklmnopqr
82def test_repr(): 1stuvwxyzA
83 class Model(SQLModel): 123456789!
84 id: int | None = Field(primary_key=True) 123456789!
85 foo: str = Field(repr=False) 123456789!
87 instance = Model(id=123, foo="bar") 123456789!
88 assert "foo=" not in repr(instance) 123456789!
91def test_min_items(): 1stuvwxyzA
92 with pytest.warns( 1KLMNOPQRS
93 DeprecationWarning,
94 match="`min_items` is deprecated and will be removed, use `min_length` instead",
95 ):
97 class Model(SQLModel): 1KLMNOPQRS
98 items: list[int] = Field(min_items=2) 1KLMNOPQRS
100 Model(items=[1, 2]) 1KLMNOPQRS
102 with pytest.raises(ValidationError) as exc_info: 1KLMNOPQRS
103 Model(items=[1]) 1KLMNOPQRS
104 assert len(exc_info.value.errors()) == 1 1KLMNOPQRS
105 assert exc_info.value.errors()[0]["type"] == "too_short" 1KLMNOPQRS
108def test_max_items(): 1stuvwxyzA
109 with pytest.warns( 1TUVWXYZ01
110 DeprecationWarning,
111 match="`max_items` is deprecated and will be removed, use `max_length` instead",
112 ):
114 class Model(SQLModel): 1TUVWXYZ01
115 items: list[int] = Field(max_items=2) 1TUVWXYZ01
117 Model(items=[1, 2]) 1TUVWXYZ01
119 with pytest.raises(ValidationError) as exc_info: 1TUVWXYZ01
120 Model(items=[1, 2, 3]) 1TUVWXYZ01
121 assert len(exc_info.value.errors()) == 1 1TUVWXYZ01
122 assert exc_info.value.errors()[0]["type"] == "too_long" 1TUVWXYZ01