Coverage for tests / test_nullable.py: 100%
49 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
1import pytest 1stuvwxyzA
2from sqlalchemy import Engine, inspect 1stuvwxyzA
3from sqlalchemy.exc import IntegrityError 1stuvwxyzA
4from sqlmodel import Field, Session, SQLModel, create_engine 1stuvwxyzA
7def test_nullable_fields(database_engine: Engine): 1stuvwxyzA
8 class Hero(SQLModel, table=True): 1abcdefghi
9 primary_key: int | None = Field( 1abcdefghi
10 default=None,
11 primary_key=True,
12 )
13 required_value: str 1abcdefghi
14 optional_default_ellipsis: str | None = Field(default=...) 1abcdefghi
15 optional_default_none: str | None = Field(default=None) 1abcdefghi
16 optional_non_nullable: str | None = Field( 1abcdefghi
17 nullable=False,
18 )
19 optional_nullable: str | None = Field( 1abcdefghi
20 nullable=True,
21 )
22 optional_default_ellipses_non_nullable: str | None = Field( 1abcdefghi
23 default=...,
24 nullable=False,
25 )
26 optional_default_ellipses_nullable: str | None = Field( 1abcdefghi
27 default=...,
28 nullable=True,
29 )
30 optional_default_none_non_nullable: str | None = Field( 1abcdefghi
31 default=None,
32 nullable=False,
33 )
34 optional_default_none_nullable: str | None = Field( 1abcdefghi
35 default=None,
36 nullable=True,
37 )
38 default_ellipses_non_nullable: str = Field(default=..., nullable=False) 1abcdefghi
39 optional_default_str: str | None = "default" 1abcdefghi
40 optional_default_str_non_nullable: str | None = Field( 1abcdefghi
41 default="default", nullable=False
42 )
43 optional_default_str_nullable: str | None = Field( 1abcdefghi
44 default="default", nullable=True
45 )
46 str_default_str: str = "default" 1abcdefghi
47 str_default_str_non_nullable: str = Field(default="default", nullable=False) 1abcdefghi
48 str_default_str_nullable: str = Field(default="default", nullable=True) 1abcdefghi
49 str_default_ellipsis_non_nullable: str = Field(default=..., nullable=False) 1abcdefghi
50 str_default_ellipsis_nullable: str = Field(default=..., nullable=True) 1abcdefghi
52 engine = database_engine 1abcdefghi
53 SQLModel.metadata.create_all(engine) 1abcdefghi
55 nullable = { 1abcdefghi
56 column["name"]: column["nullable"]
57 for column in inspect(engine).get_columns("hero")
58 }
59 assert nullable == { 1abcdefghi
60 "primary_key": False,
61 "required_value": False,
62 "optional_default_ellipsis": True,
63 "optional_default_none": True,
64 "optional_non_nullable": False,
65 "optional_nullable": True,
66 "optional_default_ellipses_non_nullable": False,
67 "optional_default_ellipses_nullable": True,
68 "optional_default_none_non_nullable": False,
69 "optional_default_none_nullable": True,
70 "default_ellipses_non_nullable": False,
71 "optional_default_str": True,
72 "optional_default_str_non_nullable": False,
73 "optional_default_str_nullable": True,
74 "str_default_str": False,
75 "str_default_str_non_nullable": False,
76 "str_default_str_nullable": True,
77 "str_default_ellipsis_non_nullable": False,
78 "str_default_ellipsis_nullable": True,
79 }
82# Test for regression in https://github.com/tiangolo/sqlmodel/issues/420
83def test_non_nullable_optional_field_with_no_default_set(database_engine: Engine): 1stuvwxyzA
84 class Hero(SQLModel, table=True): 1jklmnopqr
85 primary_key: int | None = Field( 1jklmnopqr
86 default=None,
87 primary_key=True,
88 )
90 optional_non_nullable_no_default: str | None = Field(nullable=False) 1jklmnopqr
92 engine = database_engine 1jklmnopqr
93 SQLModel.metadata.create_all(engine) 1jklmnopqr
95 nullable = { 1jklmnopqr
96 column["name"]: column["nullable"]
97 for column in inspect(engine).get_columns("hero")
98 }
99 assert nullable == { 1jklmnopqr
100 "primary_key": False,
101 "optional_non_nullable_no_default": False,
102 }
104 # We can create a hero with `None` set for the optional non-nullable field
105 hero = Hero(primary_key=123, optional_non_nullable_no_default=None) 1jklmnopqr
106 # But we cannot commit it.
107 with Session(engine) as session: 1jklmnopqr
108 session.add(hero) 1jklmnopqr
109 with pytest.raises(IntegrityError): 1jklmnopqr
110 session.commit() 1jklmnopqr
113def test_sqlite_nullable_primary_key(clear_sqlmodel, caplog): 1stuvwxyzA
114 # Probably the weirdest corner case, it shouldn't happen anywhere, but let's test it
115 class Hero(SQLModel, table=True): 1BCDEFGHIJ
116 nullable_integer_primary_key: int | None = Field( 1BCDEFGHIJ
117 default=None,
118 primary_key=True,
119 nullable=True,
120 )
122 engine = create_engine("sqlite://", echo=True) 1BCDEFGHIJ
123 SQLModel.metadata.create_all(engine) 1BCDEFGHIJ
125 create_table_log = [ 1BCDEFGHIJ
126 message for message in caplog.messages if "CREATE TABLE hero" in message
127 ][0]
128 assert "nullable_integer_primary_key INTEGER," in create_table_log 1BCDEFGHIJ