You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
That class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.
@@ -149,17 +147,15 @@ And **inline errors**:
149
147
150
148
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:
151
149
152
-
```Python hl_lines="18 21 23-27"
153
-
from typing import Optional
154
-
150
+
```Python hl_lines="16 19 21-25"
155
151
from sqlmodel import Field, Session, SQLModel, create_engine
***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.
That class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.
@@ -162,17 +160,15 @@ And **inline errors**:
162
160
163
161
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:
164
162
165
-
```Python hl_lines="18 21 23-27"
166
-
from typing import Optional
167
-
163
+
```Python hl_lines="16 19 21-25"
168
164
from sqlmodel import Field, Session, SQLModel, create_engine
Copy file name to clipboardExpand all lines: docs/tutorial/automatic-id-none-refresh.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ In the previous chapter, we saw how to add rows to the database using **SQLModel
4
4
5
5
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)`.
6
6
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)`:
Because we don't set the `id`, it takes the Python's default value of `None` that we set in `Field(default=None)`.
24
24
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`.
26
26
27
27
Because at this point in the code, **before interacting with the database**, the Python value could actually be `None`.
28
28
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:
30
30
31
31
```Python
32
32
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
38
38
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
39
39
```
40
40
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`. 🔍
Copy file name to clipboardExpand all lines: docs/tutorial/create-db-and-table.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -67,11 +67,9 @@ And the type of each of them will also be the type of table column:
67
67
68
68
Let's now see with more detail these field/column declarations.
69
69
70
-
### Optional Fields, Nullable Columns
70
+
### `None` Fields, Nullable Columns
71
71
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`.
75
73
76
74
That is the standard way to declare that something "could be an `int` or `None`" in Python.
77
75
@@ -81,21 +79,23 @@ And we also set the default value of `age` to `None`.
81
79
82
80
/// tip
83
81
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.
85
83
86
84
///
87
85
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`:
89
87
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`).
91
90
92
-
So, this column is "nullable" (can be set to `NULL`).
91
+
And because there's a default value `= None`:
93
92
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.
95
95
96
-
In terms of **Pydantic**, `age` is an **optional field**.
96
+
/// tip
97
97
98
-
In terms of **SQLAlchemy**, `age` is a **nullable column**.
98
+
The default value could have been something else, like `= 42`.
99
99
100
100
///
101
101
@@ -111,7 +111,7 @@ To do that, we use the special `Field` function from `sqlmodel` and set the argu
111
111
112
112
That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
113
113
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`?
115
115
116
116
The `id` will be required in the database, but it will be *generated by the database*, not by our code.
117
117
@@ -128,7 +128,7 @@ somehow_save_in_db(my_hero)
128
128
do_something(my_hero.id) # Now my_hero.id has a value generated in DB 🎉
129
129
```
130
130
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`.
Copy file name to clipboardExpand all lines: docs/tutorial/fastapi/multiple-models.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ For input, we have:
16
16
17
17
If we pay attention, it shows that the client *could* send an `id` in the JSON body of the request.
18
18
19
-
This means that the client could try to use the same ID that already exists in the database for another hero.
19
+
This means that the client could try to use the same ID that already exists in the database to create another hero.
20
20
21
21
That's not what we want.
22
22
@@ -51,7 +51,7 @@ The `age` is optional, we don't have to return it, or it could be `None` (or `nu
51
51
52
52
Here's the weird thing, the `id` currently seems also "optional". 🤔
53
53
54
-
This is because in our **SQLModel** class we declare the `id` with `Optional[int]`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
54
+
This is because in our **SQLModel** class we declare the `id` with a default value of `= None`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
55
55
56
56
But in the responses, we always send a model from the database, so it **always has an ID**. So the `id` in the responses can be declared as required.
57
57
@@ -71,7 +71,7 @@ And in most of the cases, the developer of the client for that API **will also b
71
71
72
72
### So Why is it Important to Have Required IDs
73
73
74
-
Now, what's the matter with having one **`id` field marked as "optional"** in a response when in reality it is always required?
74
+
Now, what's the matter with having one **`id` field marked as "optional"** in a response when in reality it is always available (required)?
75
75
76
76
For example, **automatically generated clients** in other languages (or also in Python) would have some declaration that this field `id` is optional.
77
77
@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
98
98
*`secret_name`, required
99
99
*`age`, optional
100
100
101
-
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
101
+
And we want to have a `HeroPublic` with the `id` field, but this time with a type of `id: int`, instead of `id: int | None`, to make it clear that it will always have an `int` in responses **read** from the clients:
102
102
103
103
*`id`, required
104
104
*`name`, required
@@ -225,7 +225,7 @@ Let's start with the only **table model**, the `Hero`:
225
225
226
226
Notice that `Hero` now doesn't inherit from `SQLModel`, but from `HeroBase`.
227
227
228
-
And now we only declare one single field directly, the `id`, that here is `Optional[int]`, and is a `primary_key`.
228
+
And now we only declare one single field directly, the `id`, that here is `int | None`, and is a `primary_key`.
229
229
230
230
And even though we don't declare the other fields **explicitly**, because they are inherited, they are also part of this `Hero` model.
Copy file name to clipboardExpand all lines: docs/tutorial/fastapi/update.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,15 +8,15 @@ We want clients to be able to update the `name`, the `secret_name`, and the `age
8
8
9
9
But we don't want them to have to include all the data again just to **update a single field**.
10
10
11
-
So, we need to have all those fields **marked as optional**.
11
+
So, we need to make all those fields **optional**.
12
12
13
-
And because the `HeroBase` has some of them as *required*and not optional, we will need to **create a new model**.
13
+
And because the `HeroBase` has some of them *required*(without a default value), we will need to **create a new model**.
14
14
15
15
/// tip
16
16
17
17
Here is one of those cases where it probably makes sense to use an **independent model** instead of trying to come up with a complex tree of models inheriting from each other.
18
18
19
-
Because each field is **actually different** (we just change it to `Optional`, but that's already making it different), it makes sense to have them in their own model.
19
+
Because each field is **actually different** (we just set a default value of `None`, but that's already making it different), it makes sense to have them in their own model.
Copy file name to clipboardExpand all lines: docs/tutorial/relationship-attributes/define-relationships-attributes.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -68,15 +68,15 @@ if hero.team:
68
68
print(hero.team.name)
69
69
```
70
70
71
-
## Optional Relationship Attributes
71
+
## Relationship Attributes or `None`
72
72
73
-
Notice that in the `Hero` class, the type annotation for `team` is `Optional[Team]`.
73
+
Notice that in the `Hero` class, the type annotation for `team` is `Team | None`.
74
74
75
75
This means that this attribute could be `None`, or it could be a full `Team` object.
76
76
77
77
This is because the related **`team_id` could also be `None`** (or `NULL` in the database).
78
78
79
-
If it was required for a `Hero` instance to belong to a `Team`, then the `team_id` would be `int` instead of `Optional[int]`, its `Field` would be `Field(foreign_key="team.id")` instead of `Field(default=None, foreign_key="team.id")` and the `team` attribute would be a `Team` instead of `Optional[Team]`.
79
+
If it was required for a `Hero` instance to belong to a `Team`, then the `team_id` would be `int` instead of `int | None`, its `Field` would be `Field(foreign_key="team.id")` instead of `Field(default=None, foreign_key="team.id")` and the `team` attribute would be a `Team` instead of `Team | None`.
0 commit comments