Coverage for tests/test_nullable.py: 100%

68 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-27 00:03 +0000

1from typing import Optional 1uvwxyzABCD

2 

3import pytest 1uvwxyzABCD

4from sqlalchemy.exc import IntegrityError 1uvwxyzABCD

5from sqlmodel import Field, Session, SQLModel, create_engine 1uvwxyzABCD

6 

7 

8def test_nullable_fields(clear_sqlmodel, caplog): 1uvwxyzABCD

9 class Hero(SQLModel, table=True): 1abcdefghij

10 primary_key: Optional[int] = Field( 1abcdefghij

11 default=None, 

12 primary_key=True, 

13 ) 

14 required_value: str 1abcdefghij

15 optional_default_ellipsis: Optional[str] = Field(default=...) 1abcdefghij

16 optional_default_none: Optional[str] = Field(default=None) 1abcdefghij

17 optional_non_nullable: Optional[str] = Field( 1abcdefghij

18 nullable=False, 

19 ) 

20 optional_nullable: Optional[str] = Field( 1abcdefghij

21 nullable=True, 

22 ) 

23 optional_default_ellipses_non_nullable: Optional[str] = Field( 1abcdefghij

24 default=..., 

25 nullable=False, 

26 ) 

27 optional_default_ellipses_nullable: Optional[str] = Field( 1abcdefghij

28 default=..., 

29 nullable=True, 

30 ) 

31 optional_default_none_non_nullable: Optional[str] = Field( 1abcdefghij

32 default=None, 

33 nullable=False, 

34 ) 

35 optional_default_none_nullable: Optional[str] = Field( 1abcdefghij

36 default=None, 

37 nullable=True, 

38 ) 

39 default_ellipses_non_nullable: str = Field(default=..., nullable=False) 1abcdefghij

40 optional_default_str: Optional[str] = "default" 1abcdefghij

41 optional_default_str_non_nullable: Optional[str] = Field( 1abcdefghij

42 default="default", nullable=False 

43 ) 

44 optional_default_str_nullable: Optional[str] = Field( 1abcdefghij

45 default="default", nullable=True 

46 ) 

47 str_default_str: str = "default" 1abcdefghij

48 str_default_str_non_nullable: str = Field(default="default", nullable=False) 1abcdefghij

49 str_default_str_nullable: str = Field(default="default", nullable=True) 1abcdefghij

50 str_default_ellipsis_non_nullable: str = Field(default=..., nullable=False) 1abcdefghij

51 str_default_ellipsis_nullable: str = Field(default=..., nullable=True) 1abcdefghij

52 

53 engine = create_engine("sqlite://", echo=True) 1abcdefghij

54 SQLModel.metadata.create_all(engine) 1abcdefghij

55 

56 create_table_log = [ 1abcdefghij

57 message for message in caplog.messages if "CREATE TABLE hero" in message 

58 ][0] 

59 assert "primary_key INTEGER NOT NULL," in create_table_log 1abcdefghij

60 assert "required_value VARCHAR NOT NULL," in create_table_log 1abcdefghij

61 assert "optional_default_ellipsis VARCHAR," in create_table_log 1abcdefghij

62 assert "optional_default_none VARCHAR," in create_table_log 1abcdefghij

63 assert "optional_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

64 assert "optional_nullable VARCHAR," in create_table_log 1abcdefghij

65 assert ( 1abcdefghij

66 "optional_default_ellipses_non_nullable VARCHAR NOT NULL," in create_table_log 

67 ) 

68 assert "optional_default_ellipses_nullable VARCHAR," in create_table_log 1abcdefghij

69 assert "optional_default_none_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

70 assert "optional_default_none_nullable VARCHAR," in create_table_log 1abcdefghij

71 assert "default_ellipses_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

72 assert "optional_default_str VARCHAR," in create_table_log 1abcdefghij

73 assert "optional_default_str_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

74 assert "optional_default_str_nullable VARCHAR," in create_table_log 1abcdefghij

75 assert "str_default_str VARCHAR NOT NULL," in create_table_log 1abcdefghij

76 assert "str_default_str_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

77 assert "str_default_str_nullable VARCHAR," in create_table_log 1abcdefghij

78 assert "str_default_ellipsis_non_nullable VARCHAR NOT NULL," in create_table_log 1abcdefghij

79 assert "str_default_ellipsis_nullable VARCHAR," in create_table_log 1abcdefghij

80 

81 

82# Test for regression in https://github.com/tiangolo/sqlmodel/issues/420 

83def test_non_nullable_optional_field_with_no_default_set(clear_sqlmodel, caplog): 1uvwxyzABCD

84 class Hero(SQLModel, table=True): 1klmnopqrst

85 primary_key: Optional[int] = Field( 1klmnopqrst

86 default=None, 

87 primary_key=True, 

88 ) 

89 

90 optional_non_nullable_no_default: Optional[str] = Field(nullable=False) 1klmnopqrst

91 

92 engine = create_engine("sqlite://", echo=True) 1klmnopqrst

93 SQLModel.metadata.create_all(engine) 1klmnopqrst

94 

95 create_table_log = [ 1klmnopqrst

96 message for message in caplog.messages if "CREATE TABLE hero" in message 

97 ][0] 

98 assert "primary_key INTEGER NOT NULL," in create_table_log 1klmnopqrst

99 assert "optional_non_nullable_no_default VARCHAR NOT NULL," in create_table_log 1klmnopqrst

100 

101 # We can create a hero with `None` set for the optional non-nullable field 

102 hero = Hero(primary_key=123, optional_non_nullable_no_default=None) 1klmnopqrst

103 # But we cannot commit it. 

104 with Session(engine) as session: 1klmnopqrst

105 session.add(hero) 1klmnopqrst

106 with pytest.raises(IntegrityError): 1klmnopqrst

107 session.commit() 1klmnopqrst

108 

109 

110def test_nullable_primary_key(clear_sqlmodel, caplog): 1uvwxyzABCD

111 # Probably the weirdest corner case, it shouldn't happen anywhere, but let's test it 

112 class Hero(SQLModel, table=True): 1EFGHIJKLMN

113 nullable_integer_primary_key: Optional[int] = Field( 1EFGHIJKLMN

114 default=None, 

115 primary_key=True, 

116 nullable=True, 

117 ) 

118 

119 engine = create_engine("sqlite://", echo=True) 1EFGHIJKLMN

120 SQLModel.metadata.create_all(engine) 1EFGHIJKLMN

121 

122 create_table_log = [ 1EFGHIJKLMN

123 message for message in caplog.messages if "CREATE TABLE hero" in message 

124 ][0] 

125 assert "nullable_integer_primary_key INTEGER," in create_table_log 1EFGHIJKLMN