Skip to content

Commit 6152386

Browse files
authored
📝 Update all docs references to Optional to use the new syntax in Python 3.10, e.g. int | None (#1351)
1 parent 0e5e197 commit 6152386

File tree

15 files changed

+52
-66
lines changed

15 files changed

+52
-66
lines changed

.github/DISCUSSION_TEMPLATE/questions.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,14 @@ body:
6464
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
6565
6666
placeholder: |
67-
from typing import Optional
68-
6967
from sqlmodel import Field, Session, SQLModel, create_engine
7068
7169
7270
class Hero(SQLModel, table=True):
73-
id: Optional[int] = Field(default=None, primary_key=True)
71+
id: int | None = Field(default=None, primary_key=True)
7472
name: str
7573
secret_name: str
76-
age: Optional[int] = None
74+
age: int | None = None
7775
7876
7977
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")

README.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,14 @@ And you want it to have this data:
105105
Then you could create a **SQLModel** model like this:
106106

107107
```Python
108-
from typing import Optional
109-
110108
from sqlmodel import Field, SQLModel
111109

112110

113111
class Hero(SQLModel, table=True):
114-
id: Optional[int] = Field(default=None, primary_key=True)
112+
id: int | None = Field(default=None, primary_key=True)
115113
name: str
116114
secret_name: str
117-
age: Optional[int] = None
115+
age: int | None = None
118116
```
119117

120118
That class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.
@@ -149,17 +147,15 @@ And **inline errors**:
149147

150148
You can learn a lot more about **SQLModel** by quickly following the **tutorial**, but if you need a taste right now of how to put all that together and save to the database, you can do this:
151149

152-
```Python hl_lines="18 21 23-27"
153-
from typing import Optional
154-
150+
```Python hl_lines="16 19 21-25"
155151
from sqlmodel import Field, Session, SQLModel, create_engine
156152

157153

158154
class Hero(SQLModel, table=True):
159-
id: Optional[int] = Field(default=None, primary_key=True)
155+
id: int | None = Field(default=None, primary_key=True)
160156
name: str
161157
secret_name: str
162-
age: Optional[int] = None
158+
age: int | None = None
163159

164160

165161
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
@@ -185,17 +181,15 @@ That will save a **SQLite** database with the 3 heroes.
185181

186182
Then you could write queries to select from that same database, for example with:
187183

188-
```Python hl_lines="15-18"
189-
from typing import Optional
190-
184+
```Python hl_lines="13-17"
191185
from sqlmodel import Field, Session, SQLModel, create_engine, select
192186

193187

194188
class Hero(SQLModel, table=True):
195-
id: Optional[int] = Field(default=None, primary_key=True)
189+
id: int | None = Field(default=None, primary_key=True)
196190
name: str
197191
secret_name: str
198-
age: Optional[int] = None
192+
age: int | None = None
199193

200194

201195
engine = create_engine("sqlite:///database.db")

docs/db-to-code.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,10 @@ For example this class is part of that **Object** Oriented Programming:
252252

253253
```Python
254254
class Hero(SQLModel):
255-
id: Optional[int] = Field(default=None, primary_key=True)
255+
id: int | None = Field(default=None, primary_key=True)
256256
name: str
257257
secret_name: str
258-
age: Optional[int] = None
258+
age: int | None = None
259259
```
260260

261261
* **Relational**: refers to the **SQL Databases**. Remember that they are also called **Relational Databases**, because each of those tables is also called a "**relation**"? That's where the "**Relational**" comes from.

docs/img/index/autocompletion01.png

48.1 KB
Loading

docs/img/index/autocompletion02.png

104 KB
Loading

docs/img/index/inline-errors01.png

59.7 KB
Loading

docs/index.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,14 @@ And you want it to have this data:
118118
Then you could create a **SQLModel** model like this:
119119

120120
```Python
121-
from typing import Optional
122-
123121
from sqlmodel import Field, SQLModel
124122

125123

126124
class Hero(SQLModel, table=True):
127-
id: Optional[int] = Field(default=None, primary_key=True)
125+
id: int | None = Field(default=None, primary_key=True)
128126
name: str
129127
secret_name: str
130-
age: Optional[int] = None
128+
age: int | None = None
131129
```
132130

133131
That class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.
@@ -162,17 +160,15 @@ And **inline errors**:
162160

163161
You can learn a lot more about **SQLModel** by quickly following the **tutorial**, but if you need a taste right now of how to put all that together and save to the database, you can do this:
164162

165-
```Python hl_lines="18 21 23-27"
166-
from typing import Optional
167-
163+
```Python hl_lines="16 19 21-25"
168164
from sqlmodel import Field, Session, SQLModel, create_engine
169165

170166

171167
class Hero(SQLModel, table=True):
172-
id: Optional[int] = Field(default=None, primary_key=True)
168+
id: int | None = Field(default=None, primary_key=True)
173169
name: str
174170
secret_name: str
175-
age: Optional[int] = None
171+
age: int | None = None
176172

177173

178174
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
@@ -198,17 +194,15 @@ That will save a **SQLite** database with the 3 heroes.
198194

199195
Then you could write queries to select from that same database, for example with:
200196

201-
```Python hl_lines="15-18"
202-
from typing import Optional
203-
197+
```Python hl_lines="13-17"
204198
from sqlmodel import Field, Session, SQLModel, create_engine, select
205199

206200

207201
class Hero(SQLModel, table=True):
208-
id: Optional[int] = Field(default=None, primary_key=True)
202+
id: int | None = Field(default=None, primary_key=True)
209203
name: str
210204
secret_name: str
211-
age: Optional[int] = None
205+
age: int | None = None
212206

213207

214208
engine = create_engine("sqlite:///database.db")

docs/tutorial/automatic-id-none-refresh.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ In the previous chapter, we saw how to add rows to the database using **SQLModel
44

55
Now let's talk a bit about why the `id` field **can't be `NULL`** on the database because it's a **primary key**, and we declare it using `Field(primary_key=True)`.
66

7-
But the same `id` field actually **can be `None`** in the Python code, so we declare the type with `int | None (or Optional[int])`, and set the default value to `Field(default=None)`:
7+
But the same `id` field actually **can be `None`** in the Python code, so we declare the type with `int | None`, and set the default value to `Field(default=None)`:
88

99
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[4:8] hl[5] *}
1010

@@ -18,15 +18,15 @@ When we create a new `Hero` instance, we don't set the `id`:
1818

1919
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[21:24] hl[21:24] *}
2020

21-
### How `Optional` Helps
21+
### How `int | None` Helps
2222

2323
Because we don't set the `id`, it takes the Python's default value of `None` that we set in `Field(default=None)`.
2424

25-
This is the only reason why we define it with `Optional` and with a default value of `None`.
25+
This is the only reason why we define it with `int | None` and with a default value of `None`.
2626

2727
Because at this point in the code, **before interacting with the database**, the Python value could actually be `None`.
2828

29-
If we assumed that the `id` was *always* an `int` and added the type annotation without `Optional`, we could end up writing broken code, like:
29+
If we assumed that the `id` was *always* an `int` and added the type annotation without `int | None`, we could end up writing broken code, like:
3030

3131
```Python
3232
next_hero_id = hero_1.id + 1
@@ -38,7 +38,7 @@ If we ran this code before saving the hero to the database and the `hero_1.id` w
3838
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
3939
```
4040

41-
But by declaring it with `Optional[int]`, the editor will help us to avoid writing broken code by showing us a warning telling us that the code could be invalid if `hero_1.id` is `None`. 🔍
41+
But by declaring it with `int | None`, the editor will help us to avoid writing broken code by showing us a warning telling us that the code could be invalid if `hero_1.id` is `None`. 🔍
4242

4343
## Print the Default `id` Values
4444

docs/tutorial/connect/create-connected-rows.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ WHERE team.id = ?
141141
INFO Engine [cached since 0.001795s ago] (1,)
142142
```
143143

144-
There's something else to note. We marked `team_id` as `Optional[int]`, meaning that this could be `NULL` on the database (and `None` in Python).
144+
There's something else to note. We marked `team_id` as `int | None`, meaning that this could be `NULL` on the database (and `None` in Python).
145145

146146
That means that a hero doesn't have to have a team. And in this case, **Spider-Boy** doesn't have one.
147147

docs/tutorial/create-db-and-table.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,9 @@ And the type of each of them will also be the type of table column:
6767

6868
Let's now see with more detail these field/column declarations.
6969

70-
### Optional Fields, Nullable Columns
70+
### `None` Fields, Nullable Columns
7171

72-
Let's start with `age`, notice that it has a type of `int | None (or Optional[int])`.
73-
74-
And we import that `Optional` from the `typing` standard module.
72+
Let's start with `age`, notice that it has a type of `int | None`.
7573

7674
That is the standard way to declare that something "could be an `int` or `None`" in Python.
7775

@@ -81,21 +79,23 @@ And we also set the default value of `age` to `None`.
8179

8280
/// tip
8381

84-
We also define `id` with `Optional`. But we will talk about `id` below.
82+
We also define `id` with `int | None`. But we will talk about `id` below.
8583

8684
///
8785

88-
This way, we tell **SQLModel** that `age` is not required when validating data and that it has a default value of `None`.
86+
Because the type is `int | None`:
8987

90-
And we also tell it that, in the SQL database, the default value of `age` is `NULL` (the SQL equivalent to Python's `None`).
88+
* When validating data, `None` will be an allowed value for `age`.
89+
* In the database, the column for `age` will be allowed to have `NULL` (the SQL equivalent to Python's `None`).
9190

92-
So, this column is "nullable" (can be set to `NULL`).
91+
And because there's a default value `= None`:
9392

94-
/// info
93+
* When validating data, this `age` field won't be required, it will be `None` by default.
94+
* When saving to the database, the `age` column will have a `NULL` value by default.
9595

96-
In terms of **Pydantic**, `age` is an **optional field**.
96+
/// tip
9797

98-
In terms of **SQLAlchemy**, `age` is a **nullable column**.
98+
The default value could have been something else, like `= 42`.
9999

100100
///
101101

@@ -111,7 +111,7 @@ To do that, we use the special `Field` function from `sqlmodel` and set the argu
111111

112112
That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
113113

114-
But inside the SQL database, it is **always required** and can't be `NULL`. Why should we declare it with `Optional`?
114+
But inside the SQL database, it is **always required** and can't be `NULL`. Why should we declare it with `int | None`?
115115

116116
The `id` will be required in the database, but it will be *generated by the database*, not by our code.
117117

@@ -128,7 +128,7 @@ somehow_save_in_db(my_hero)
128128
do_something(my_hero.id) # Now my_hero.id has a value generated in DB 🎉
129129
```
130130

131-
So, because in *our code* (not in the database) the value of `id` *could be* `None`, we use `Optional`. This way **the editor will be able to help us**, for example, if we try to access the `id` of an object that we haven't saved in the database yet and would still be `None`.
131+
So, because in *our code* (not in the database) the value of `id` *could be* `None`, we use `int | None`. This way **the editor will be able to help us**, for example, if we try to access the `id` of an object that we haven't saved in the database yet and would still be `None`.
132132

133133
<img class="shadow" src="/img/create-db-and-table/inline-errors01.png">
134134

0 commit comments

Comments
 (0)