Skip to content
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ We get a `hero_id` from the path parameter and verify if it exists, just as we d

And if we actually find a hero, we just delete it with the **session**.

{* ./docs_src/tutorial/fastapi/delete/tutorial001_py310.py ln[89:97] hl[89:97] *}
{* ./docs_src/tutorial/fastapi/delete/tutorial001_py310.py ln[91:99] hl[91:99] *}

After deleting it successfully, we just return a response of:

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/fastapi/limit-and-offset.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ By default, we will return the first results from the database, so `offset` will

And by default, we will return a maximum of `100` heroes, so `limit` will have a default value of `100`.

{* ./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py ln[1:2,52:56] hl[1,53,55] *}
{* ./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py ln[3:4,55:59] hl[3,56,58] *}

We want to allow clients to set different `offset` and `limit` values.

Expand Down
20 changes: 10 additions & 10 deletions docs/tutorial/fastapi/multiple-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ And we want to have a `HeroPublic` with the `id` field, but this time with a typ

The simplest way to solve it could be to create **multiple models**, each one with all the corresponding fields:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[5:22] hl[5:9,12:15,18:22] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[7:24] hl[7:11,14:17,20:24] *}

Here's the important detail, and probably the most important feature of **SQLModel**: only `Hero` is declared with `table = True`.

Expand All @@ -131,13 +131,13 @@ Let's now see how to use these new models in the FastAPI application.

Let's first check how is the process to create a hero now:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[44:51] hl[44:45,47] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[47:54] hl[47:48,50] *}

Let's check that in detail.

Now we use the type annotation `HeroCreate` for the request JSON data in the `hero` parameter of the **path operation function**.

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[45] hl[45] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[48] hl[48] *}

Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.model_validate()`.

Expand All @@ -151,15 +151,15 @@ In versions of **SQLModel** before `0.0.14` you would use the method `.from_orm(

We can now create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[47] hl[47] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[50] hl[50] *}

Then we just `add` it to the **session**, `commit`, and `refresh` it, and finally, we return the same `db_hero` variable that has the just refreshed `Hero` instance.

Because it is just refreshed, it has the `id` field set with a new ID taken from the database.

And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[44] hl[44] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[47] hl[47] *}

This will validate that all the data that we promised is there and will remove any data we didn't declare.

Expand Down Expand Up @@ -211,7 +211,7 @@ We can see from above that they all share some **base** fields:

So let's create a **base** model `HeroBase` that the others can inherit from:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:8] hl[5:8] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[7:10] hl[7:10] *}

As you can see, this is *not* a **table model**, it doesn't have the `table = True` config.

Expand All @@ -221,7 +221,7 @@ But now we can create the **other models inheriting from it**, they will all sha

Let's start with the only **table model**, the `Hero`:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:12] hl[11:12] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[7:14] hl[13:14] *}

Notice that `Hero` now doesn't inherit from `SQLModel`, but from `HeroBase`.

Expand All @@ -237,7 +237,7 @@ And those inherited fields will also be in the **autocompletion** and **inline e

Notice that the parent model `HeroBase` is not a **table model**, but still, we can declare `name` and `age` using `Field(index=True)`.

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:12] hl[6,8,11] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[7:14] hl[8,10,13] *}

This won't affect this parent **data model** `HeroBase`.

Expand All @@ -249,7 +249,7 @@ Now let's see the `HeroCreate` model that will be used to define the data that w

This is a fun one:

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:16] hl[15:16] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[7:18] hl[17:18] *}

What's happening here?

Expand All @@ -269,7 +269,7 @@ Now let's check the `HeroPublic` model.

This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.

{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:20] hl[19:20] *}
{* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[7:22] hl[21:22] *}

## Review the Updated Docs UI

Expand Down
6 changes: 3 additions & 3 deletions docs/tutorial/fastapi/read-one.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ If you need to refresh how *path parameters* work, including their data validati

///

{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[1:2,59:65] hl[59] *}
{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[3:4,62:68] hl[62] *}

For example, to get the hero with ID `2` we would send a `GET` request to:

Expand All @@ -34,15 +34,15 @@ And to use it, we first import `HTTPException` from `fastapi`.

This will let the client know that they probably made a mistake on their side and requested a hero that doesn't exist in the database.

{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[1:2,59:65] hl[1,62:64] *}
{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[3:4,63:68] hl[3,65:67] *}

## Return the Hero

Then, if the hero exists, we return it.

And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.

{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[1:2,59:65] hl[59,65] *}
{* ./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py ln[3:4,62:68] hl[62,68] *}

## Check the Docs UI

Expand Down
8 changes: 4 additions & 4 deletions docs/tutorial/fastapi/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ It's because we declared the `HeroPublic` with only the same base fields of the

And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.

{* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[5:7,20:21,29:34,43:44] hl[5:7,20:21,29:34,43:44] *}
{* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[7:9,22:23,31:36,45:46] hl[7:9,22:23,31:36,45:46] *}

Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?

In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

{* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[102:107,155:160] hl[102,107,155,160] *}
{* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[105:110,158:163] hl[105,110,158,163] *}

## Don't Include All the Data

Expand Down Expand Up @@ -132,7 +132,7 @@ Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.

We'll add them **after** the other models so that we can easily reference the previous models.

{* ./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py ln[59:64] hl[59:60,63:64] *}
{* ./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py ln[61:66] hl[61:62,65:66] *}

These two models are very **simple in code**, but there's a lot happening here. Let's check it out.

Expand Down Expand Up @@ -166,7 +166,7 @@ This will tell **FastAPI** to take the object that we return from the *path oper

In the case of the hero, this tells FastAPI to extract the `team` too. And in the case of the team, to extract the list of `heroes` too.

{* ./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py ln[111:116,164:169] hl[111,116,164,169] *}
{* ./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py ln[114:119,167:172] hl[114,119,167,172] *}

## Check It Out in the Docs UI

Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial/fastapi/response-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ We can use `response_model` to tell FastAPI the schema of the data we want to se

For example, we can pass the same `Hero` **SQLModel** class (because it is also a Pydantic model):

{* ./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py ln[31:37] hl[31] *}
{* ./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py ln[34:40] hl[34] *}

## List of Heroes in `response_model`

We can also use other type annotations, the same way we can use with Pydantic fields. For example, we can pass a list of `Hero`s.

To do so, we declare the `response_model` with `list[Hero]`:

{* ./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py ln[40:44] hl[40] *}
{* ./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py ln[43:47] hl[43] *}

## FastAPI and Response Model

Expand Down
12 changes: 6 additions & 6 deletions docs/tutorial/fastapi/session-with-dependency.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Before we keep adding things, let's change a bit how we get the session for each

Up to now, we have been creating a session in each *path operation*, in a `with` block.

{* ./docs_src/tutorial/fastapi/delete/tutorial001_py310.py ln[48:55] hl[50] *}
{* ./docs_src/tutorial/fastapi/delete/tutorial001_py310.py ln[51:58] hl[53] *}

That's perfectly fine, but in many use cases we would want to use <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI Dependencies</a>, for example to **verify** that the client is **logged in** and get the **current user** before executing any other code in the *path operation*.

Expand All @@ -20,15 +20,15 @@ A **FastAPI** dependency is very simple, it's just a function that returns a val

It could use `yield` instead of `return`, and in that case **FastAPI** will make sure it executes all the code **after** the `yield`, once it is done with the request.

{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[40:42] hl[40:42] *}
{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[42:44] hl[42:44] *}

## Use the Dependency

Now let's make FastAPI execute a dependency and get its value in the *path operation*.

We import `Depends()` from `fastapi`. Then we use it in the *path operation function* in a **parameter**, the same way we declared parameters to get JSON bodies, path parameters, etc.

{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[1:2,40:42,53:59] hl[1,54] *}
{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[3:4,42:44,56:61] hl[3,57] *}

/// tip

Expand Down Expand Up @@ -56,13 +56,13 @@ And because dependencies can use `yield`, FastAPI will make sure to run the code

This means that in the main code of the *path operation function*, it will work equivalently to the previous version with the explicit `with` block.

{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[1:2,40:42,53:59] hl[55:59] *}
{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[3:4,42:44,56:62] hl[57:62] *}

In fact, you could think that all that block of code inside of the `create_hero()` function is still inside a `with` block for the **session**, because this is more or less what's happening behind the scenes.

But now, the `with` block is not explicitly in the function, but in the dependency above:

{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[1:2,40:42,53:59] hl[41:42] *}
{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[3:4,42:44,56:62] hl[43:44] *}

We will see how this is very useful when testing the code later. ✅

Expand All @@ -78,7 +78,7 @@ session: Session = Depends(get_session)

And then we remove the previous `with` block with the old **session**.

{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[1:2,40:42,53:104] hl[54,65,74,83,98] *}
{* ./docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py ln[3:5,42:44,56:107] hl[57,68,77,86,101] *}

## Recap

Expand Down
14 changes: 7 additions & 7 deletions docs/tutorial/fastapi/simple-hero-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ We will start with the **simplest version**, with just heroes (no teams yet).

This is almost the same code we have seen up to now in previous examples:

{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[2,5:20] hl[19:20] *}
{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[4,7:22] hl[21:22] *}

There's only one change here from the code we have used before, the `check_same_thread` in the `connect_args`.

Expand All @@ -54,17 +54,17 @@ The next step is to create the **FastAPI** app.

We will import the `FastAPI` class from `fastapi`.

And then create an `app` object that is an instance of that `FastAPI` class:
And then create an `app` object that is an instance of that `FastAPI` class, using a `lifespan` configuration that we will see next:

{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[1:2,23] hl[1,23] *}
{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[3:4,31] hl[3,31] *}

## Create Database and Tables on `startup`

We want to make sure that once the app starts running, the function `create_db_and_tables` is called. To create the database and tables.

This should be called only once at startup, not before every request, so we put it in the function to handle the `"startup"` event:
This should be called only once at startup, not before every request, so we use FastAPI's `lifespan` functionality:

{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[23:28] hl[26:28] *}
{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[21:31] hl[25:28] *}

## Create Heroes *Path Operation*

Expand All @@ -78,7 +78,7 @@ Let's create the **path operation** code to create a new hero.

It will be called when a user sends a request with a `POST` **operation** to the `/heroes/` **path**:

{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[23:37] hl[31:32] *}
{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[25:40] hl[34:35] *}

/// info

Expand Down Expand Up @@ -112,7 +112,7 @@ We will improve this further later, but for now, it already shows the power of h

Now let's add another **path operation** to read all the heroes:

{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[23:44] hl[40:44] *}
{* ./docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py ln[21:47] hl[43:47] *}

This is pretty straightforward.

Expand Down
10 changes: 5 additions & 5 deletions docs/tutorial/fastapi/update-extra-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The `Hero` table model will now store a new field `hashed_password`.

And the data models for `HeroCreate` and `HeroUpdate` will also have a new field `password` that will contain the plain text password sent by clients.

{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[5:28] hl[13,17,28] *}
{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[7:30] hl[15,19,30] *}

When a client is creating a new hero, they will send the `password` in the request body.

Expand All @@ -50,7 +50,7 @@ The app will receive the data from the client using the `HeroCreate` model.

This contains the `password` field with the plain text password, and we cannot use that one. So we need to generate a hash from it.

{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[42:44,55:57] hl[57] *}
{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[44:46,58:60] hl[60] *}

## Create an Object with Extra Data

Expand Down Expand Up @@ -106,7 +106,7 @@ So now, `db_user_dict` has the updated `age` field with `32` instead of `None` a

Similar to how dictionaries have an `update` method, **SQLModel** models have a parameter `update` in `Hero.model_validate()` that takes a dictionary with extra data, or data that should take precedence:

{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[55:64] hl[60] *}
{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[58:67] hl[63] *}

Now, `db_hero` (which is a *table model* `Hero`) will extract its values from `hero` (which is a *data model* `HeroCreate`), and then it will **`update`** its values with the extra data from the dictionary `extra_data`.

Expand All @@ -120,7 +120,7 @@ Now let's say we want to **update a hero** that already exists in the database.

The same way as before, to avoid removing existing data, we will use `exclude_unset=True` when calling `hero.model_dump()`, to get a dictionary with only the data sent by the client.

{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[83:89] hl[89] *}
{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[86:92] hl[92] *}

Now, this `hero_data` dictionary could contain a `password`. We need to check it, and if it's there, we need to generate the `hashed_password`.

Expand All @@ -130,7 +130,7 @@ And then we can update the `db_hero` object using the method `db_hero.sqlmodel_u

It takes a model object or dictionary with the data to update the object and also an **additional `update` argument** with extra data.

{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[83:99] hl[95] *}
{* ./docs_src/tutorial/fastapi/update/tutorial002_py310.py ln[86:102] hl[98] *}

/// tip

Expand Down
10 changes: 5 additions & 5 deletions docs/tutorial/fastapi/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Because each field is **actually different** (we just set a default value of `No

So, let's create this new `HeroUpdate` model:

{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[5:26] hl[23:26] *}
{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[7:28] hl[25:28] *}

This is almost the same as `HeroBase`, but all the fields are optional, so we can't simply inherit from `HeroBase`.

Expand All @@ -32,7 +32,7 @@ Now let's use this model in the *path operation* to update a hero.

We will use a `PATCH` HTTP operation. This is used to **partially update data**, which is what we are doing.

{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[74:89] hl[74:75] *}
{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[77:91] hl[77:78] *}

We also read the `hero_id` from the *path parameter* and the request body, a `HeroUpdate`.

Expand All @@ -42,7 +42,7 @@ We take a `hero_id` with the **ID** of the hero **we want to update**.

So, we need to read the hero from the database, with the **same logic** we used to **read a single hero**, checking if it exists, possibly raising an error for the client if it doesn't exist, etc.

{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[74:89] hl[77:79] *}
{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[77:91] hl[80:82] *}

### Get the New Data

Expand Down Expand Up @@ -94,7 +94,7 @@ Then the dictionary we would get in Python using `hero.model_dump(exclude_unset=

Then we use that to get the data that was actually sent by the client:

{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[74:89] hl[80] *}
{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[77:91] hl[83] *}

/// tip
Before SQLModel 0.0.14, the method was called `hero.dict(exclude_unset=True)`, but it was renamed to `hero.model_dump(exclude_unset=True)` to be consistent with Pydantic v2.
Expand All @@ -104,7 +104,7 @@ Before SQLModel 0.0.14, the method was called `hero.dict(exclude_unset=True)`, b

Now that we have a **dictionary with the data sent by the client**, we can use the method `db_hero.sqlmodel_update()` to update the object `db_hero`.

{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[74:89] hl[81] *}
{* ./docs_src/tutorial/fastapi/update/tutorial001_py310.py ln[77:91] hl[84] *}

/// tip

Expand Down
Loading