Coverage for tests / test_nullable.py: 100%
67 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-06-10 09:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-06-10 09:40 +0000
1import pytest 1qrstuvwx
2from sqlalchemy.exc import IntegrityError 1qrstuvwx
3from sqlmodel import Field, Session, SQLModel, create_engine 1qrstuvwx
6def test_nullable_fields(clear_sqlmodel, caplog): 1qrstuvwx
7 class Hero(SQLModel, table=True): 1abcdefgh
8 primary_key: int | None = Field( 1abcdefgh
9 default=None,
10 primary_key=True,
11 )
12 required_value: str 1abcdefgh
13 optional_default_ellipsis: str | None = Field(default=...) 1abcdefgh
14 optional_default_none: str | None = Field(default=None) 1abcdefgh
15 optional_non_nullable: str | None = Field( 1abcdefgh
16 nullable=False,
17 )
18 optional_nullable: str | None = Field( 1abcdefgh
19 nullable=True,
20 )
21 optional_default_ellipses_non_nullable: str | None = Field( 1abcdefgh
22 default=...,
23 nullable=False,
24 )
25 optional_default_ellipses_nullable: str | None = Field( 1abcdefgh
26 default=...,
27 nullable=True,
28 )
29 optional_default_none_non_nullable: str | None = Field( 1abcdefgh
30 default=None,
31 nullable=False,
32 )
33 optional_default_none_nullable: str | None = Field( 1abcdefgh
34 default=None,
35 nullable=True,
36 )
37 default_ellipses_non_nullable: str = Field(default=..., nullable=False) 1abcdefgh
38 optional_default_str: str | None = "default" 1abcdefgh
39 optional_default_str_non_nullable: str | None = Field( 1abcdefgh
40 default="default", nullable=False
41 )
42 optional_default_str_nullable: str | None = Field( 1abcdefgh
43 default="default", nullable=True
44 )
45 str_default_str: str = "default" 1abcdefgh
46 str_default_str_non_nullable: str = Field(default="default", nullable=False) 1abcdefgh
47 str_default_str_nullable: str = Field(default="default", nullable=True) 1abcdefgh
48 str_default_ellipsis_non_nullable: str = Field(default=..., nullable=False) 1abcdefgh
49 str_default_ellipsis_nullable: str = Field(default=..., nullable=True) 1abcdefgh
51 engine = create_engine("sqlite://", echo=True) 1abcdefgh
52 SQLModel.metadata.create_all(engine) 1abcdefgh
54 create_table_log = [ 1abcdefgh
55 message for message in caplog.messages if "CREATE TABLE hero" in message
56 ][0]
57 assert "primary_key INTEGER NOT NULL," in create_table_log 1abcdefgh
58 assert "required_value VARCHAR NOT NULL," in create_table_log 1abcdefgh
59 assert "optional_default_ellipsis VARCHAR," in create_table_log 1abcdefgh
60 assert "optional_default_none VARCHAR," in create_table_log 1abcdefgh
61 assert "optional_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
62 assert "optional_nullable VARCHAR," in create_table_log 1abcdefgh
63 assert ( 1abcdefgh
64 "optional_default_ellipses_non_nullable VARCHAR NOT NULL," in create_table_log
65 )
66 assert "optional_default_ellipses_nullable VARCHAR," in create_table_log 1abcdefgh
67 assert "optional_default_none_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
68 assert "optional_default_none_nullable VARCHAR," in create_table_log 1abcdefgh
69 assert "default_ellipses_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
70 assert "optional_default_str VARCHAR," in create_table_log 1abcdefgh
71 assert "optional_default_str_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
72 assert "optional_default_str_nullable VARCHAR," in create_table_log 1abcdefgh
73 assert "str_default_str VARCHAR NOT NULL," in create_table_log 1abcdefgh
74 assert "str_default_str_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
75 assert "str_default_str_nullable VARCHAR," in create_table_log 1abcdefgh
76 assert "str_default_ellipsis_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefgh
77 assert "str_default_ellipsis_nullable VARCHAR," in create_table_log 1abcdefgh
80# Test for regression in https://github.com/tiangolo/sqlmodel/issues/420
81def test_non_nullable_optional_field_with_no_default_set(clear_sqlmodel, caplog): 1qrstuvwx
82 class Hero(SQLModel, table=True): 1ijklmnop
83 primary_key: int | None = Field( 1ijklmnop
84 default=None,
85 primary_key=True,
86 )
88 optional_non_nullable_no_default: str | None = Field(nullable=False) 1ijklmnop
90 engine = create_engine("sqlite://", echo=True) 1ijklmnop
91 SQLModel.metadata.create_all(engine) 1ijklmnop
93 create_table_log = [ 1ijklmnop
94 message for message in caplog.messages if "CREATE TABLE hero" in message
95 ][0]
96 assert "primary_key INTEGER NOT NULL," in create_table_log 1ijklmnop
97 assert "optional_non_nullable_no_default VARCHAR NOT NULL," in create_table_log 1ijklmnop
99 # We can create a hero with `None` set for the optional non-nullable field
100 hero = Hero(primary_key=123, optional_non_nullable_no_default=None) 1ijklmnop
101 # But we cannot commit it.
102 with Session(engine) as session: 1ijklmnop
103 session.add(hero) 1ijklmnop
104 with pytest.raises(IntegrityError): 1ijklmnop
105 session.commit() 1ijklmnop
108def test_nullable_primary_key(clear_sqlmodel, caplog): 1qrstuvwx
109 # Probably the weirdest corner case, it shouldn't happen anywhere, but let's test it
110 class Hero(SQLModel, table=True): 1yzABCDEF
111 nullable_integer_primary_key: int | None = Field( 1yzABCDEF
112 default=None,
113 primary_key=True,
114 nullable=True,
115 )
117 engine = create_engine("sqlite://", echo=True) 1yzABCDEF
118 SQLModel.metadata.create_all(engine) 1yzABCDEF
120 create_table_log = [ 1yzABCDEF
121 message for message in caplog.messages if "CREATE TABLE hero" in message
122 ][0]
123 assert "nullable_integer_primary_key INTEGER," in create_table_log 1yzABCDEF