Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/advanced/datetime.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Pydantic provides [`AwareDatetime` and `NaiveDatetime`](https://docs.pydantic.de

We can use them as field types:

{* ./docs_src/advanced/datetime/tutorial002_py310.py ln[1:8] hl[1,7:8] *}
{* ./docs_src/advanced/datetime/tutorial002.py ln[1:8] hl[1,7:8] *}

Here, `starts_at` requires timezone information during validation. It uses the same database type as `datetime`: `UTCDateTime`.

Expand All @@ -77,7 +77,7 @@ These types also work with `| None` and with `Annotated`, for example `Annotated

We can use `Event.model_validate()` to validate the data before creating an event:

{* ./docs_src/advanced/datetime/tutorial002_py310.py ln[11:17] hl[13:14,16] *}
{* ./docs_src/advanced/datetime/tutorial002.py ln[11:17] hl[13:14,16] *}

The `Z` at the end of `starts_at` means **UTC**. The value for `local_time` has no timezone, so both values pass validation.

Expand Down Expand Up @@ -137,13 +137,13 @@ Back up the database and test schema changes, data conversions, and application

Change the field annotation to `NaiveDatetime`:

{* ./docs_src/advanced/datetime/tutorial005_py310.py ln[1:9] hl[3,9] *}
{* ./docs_src/advanced/datetime/tutorial005.py ln[1:9] hl[3,9] *}

This keeps the previous database column type, so no database migration is needed for that field. It also adds the validation constraint described above.

To retain the previous database type **and** plain `datetime` validation behavior, declare the database type explicitly:

{* ./docs_src/advanced/datetime/tutorial003_py310.py ln[1:9] hl[3,9] *}
{* ./docs_src/advanced/datetime/tutorial003.py ln[1:9] hl[3,9] *}

### Adopt Timezone-Aware Storage on PostgreSQL

Expand All @@ -153,7 +153,7 @@ To retain the previous database type **and** plain `datetime` validation behavio

For example, if `event.created_at` contains naive **UTC** values, the operations in the generated revision can be:

{* ./docs_src/advanced/datetime/tutorial004_py310.py hl[9:11,19:21] *}
{* ./docs_src/advanced/datetime/tutorial004.py hl[9:11,19:21] *}

This migration uses `sa.DateTime(timezone=True)`, the underlying database type of `UTCDateTime`.

Expand Down
6 changes: 3 additions & 3 deletions docs/advanced/decimal.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ For the database, **SQLModel** will use [SQLAlchemy's `DECIMAL` type](https://do

Let's say that each hero in the database will have an amount of money. We can define that field as a `Decimal` and configure the maximum number of digits and decimal places using the parameters of `Field()`:

{* ./docs_src/advanced/decimal/tutorial001_py310.py ln[1:11] hl[11] *}
{* ./docs_src/advanced/decimal/tutorial001.py ln[1:11] hl[11] *}

Here we are saying that `money` can have at most `5` digits with `max_digits`, **this includes the integers** (to the left of the decimal dot) **and the decimals** (to the right of the decimal dot).

Expand Down Expand Up @@ -67,13 +67,13 @@ Make sure you adjust the number of digits and decimal places for your own needs,

When creating new models you can actually pass normal (`float`) numbers, Pydantic will automatically convert them to `Decimal` types, and **SQLModel** will store them as `Decimal` types in the database (using SQLAlchemy).

{* ./docs_src/advanced/decimal/tutorial001_py310.py ln[24:34] hl[25:27] *}
{* ./docs_src/advanced/decimal/tutorial001.py ln[24:34] hl[25:27] *}

## Select Decimal data

Then, when working with Decimal types, you can confirm that they indeed avoid those rounding errors from floats:

{* ./docs_src/advanced/decimal/tutorial001_py310.py ln[37:50] hl[49:50] *}
{* ./docs_src/advanced/decimal/tutorial001.py ln[37:50] hl[49:50] *}

## Review the results

Expand Down
8 changes: 4 additions & 4 deletions docs/advanced/uuid.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ We don't call `uuid.uuid4()` ourselves in the code (we don't put the parenthesis

This means that the UUID will be generated in the Python code, **before sending the data to the database**.

{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[1:10] hl[1,7] *}
{* ./docs_src/advanced/uuid/tutorial001.py ln[1:10] hl[1,7] *}

Pydantic has support for [`UUID` types](https://docs.pydantic.dev/latest/api/standard_library_types/#uuid).

Expand All @@ -94,15 +94,15 @@ As `uuid.uuid4` will be called when creating the model instance, even before sen

And that **same ID (a UUID)** will be saved in the database.

{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[23:34] hl[25,27,29,34] *}
{* ./docs_src/advanced/uuid/tutorial001.py ln[23:34] hl[25,27,29,34] *}

### Select a Hero

We can do the same operations we could do with other fields.

For example we can **select a hero by ID**:

{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[37:54] hl[49] *}
{* ./docs_src/advanced/uuid/tutorial001.py ln[37:54] hl[49] *}

/// tip

Expand All @@ -116,7 +116,7 @@ SQLModel (actually SQLAlchemy) will take care of making it work. ✨

We could also select by ID with `session.get()`:

{* ./docs_src/advanced/uuid/tutorial002_py310.py ln[37:53] hl[49] *}
{* ./docs_src/advanced/uuid/tutorial002.py ln[37:53] hl[49] *}

The same way as with other fields, we could update, delete, etc. 🚀

Expand Down
22 changes: 9 additions & 13 deletions docs/tutorial/automatic-id-none-refresh.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Now let's talk a bit about why the `id` field **can't be `NULL`** on the databas

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)`:

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

Next, I'll show you a bit more about the synchronization of data between the database and the Python code.

Expand All @@ -16,7 +16,7 @@ When do we get an actual `int` from the database in that `id` field? Let's see a

When we create a new `Hero` instance, we don't set the `id`:

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

### How `int | None` Helps

Expand Down Expand Up @@ -44,7 +44,7 @@ But by declaring it with `int | None`, the editor will help us to avoid writing

We can confirm that by printing our heroes before adding them to the database:

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[21:29] hl[27:29] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[21:29] hl[27:29] *}

That will output:

Expand Down Expand Up @@ -75,7 +75,7 @@ After we add the `Hero` instance objects to the **session**, the IDs are *still*

We can verify by creating a session using a `with` block and adding the objects. And then printing them again:

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[21:39] hl[37:39] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[21:39] hl[37:39] *}

This will, again, output the `id`s of the objects as `None`:

Expand All @@ -100,7 +100,7 @@ As we saw before, the **session** is smart and doesn't talk to the database ever

Then we can `commit` the changes in the session, and print again:

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[31:46] hl[41,44:46] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[31:46] hl[41,44:46] *}

And now, something unexpected happens, look at the output, it seems as if the `Hero` instance objects had no data at all:

Expand Down Expand Up @@ -158,7 +158,7 @@ We didn't access the object's attributes, like `hero.name`. We only accessed the

To confirm and understand how this **automatic expiration and refresh** of data when accessing attributes work, we can print some individual fields (instance attributes):

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[31:56] hl[49:51,54:56] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[31:56] hl[49:51,54:56] *}

Now we are actually accessing the attributes, because instead of printing the whole object `hero_1`:

Expand Down Expand Up @@ -239,7 +239,7 @@ But what if you want to **explicitly refresh** the data?

You can do that too with `session.refresh(object)`:

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[31:65] hl[58:60,63:65] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[31:65] hl[58:60,63:65] *}

When Python executes this code:

Expand Down Expand Up @@ -297,7 +297,7 @@ Now, as a final experiment, we can also print data after the **session** is clos

There are no surprises here, it still works:

{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py ln[31:70] hl[68:70] *}
{* ./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py ln[31:70] hl[68:70] *}

And the output shows again the same data:

Expand Down Expand Up @@ -332,16 +332,12 @@ And as we created the **engine** with `echo=True`, we can see the SQL statements

///

//// tab | Python 3.10+

```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py!}
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial002.py!}
```

{!./docs_src/tutorial/automatic_id_none_refresh/annotations/en/tutorial002.md!}

////

And here's all the output generated by running this program, all together:

<div class="termy">
Expand Down
12 changes: 6 additions & 6 deletions docs/tutorial/code-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ We can use these relative imports because, for example, in the file `app.py` (th

You could put all the database Models in a single Python module (a single Python file), for example `models.py`:

{* ./docs_src/tutorial/code_structure/tutorial001_py310/models.py *}
{* ./docs_src/tutorial/code_structure/tutorial001/models.py *}

This way, you wouldn't have to deal with circular imports for other models.

Expand All @@ -77,15 +77,15 @@ And then you could import the models from this file/module in any other file/mod

Then you could put the code creating the **engine** and the function to create all the tables (if you are not using migrations) in another file `database.py`:

{* ./docs_src/tutorial/code_structure/tutorial001_py310/database.py *}
{* ./docs_src/tutorial/code_structure/tutorial001/database.py *}

This file would also be imported by your application code, to use the shared **engine** and to get and call the function `create_db_and_tables()`.

### Application File

Finally, you could put the code to create the **app** in another file `app.py`:

{* ./docs_src/tutorial/code_structure/tutorial001_py310/app.py hl[3:4] *}
{* ./docs_src/tutorial/code_structure/tutorial001/app.py hl[3:4] *}

Here we import the models, the engine, and the function to create all the tables and then we can use them all internally.

Expand Down Expand Up @@ -201,23 +201,23 @@ So, we can use it in an `if` block and import things inside the `if` block. And

Using that trick of `TYPE_CHECKING` we can "import" the `Team` in `hero_model.py`:

{* ./docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py hl[1,5:6,16] *}
{* ./docs_src/tutorial/code_structure/tutorial002/hero_model.py hl[1,5:6,16] *}

Keep in mind that now we *have* to put the annotation of `Team` as a string: `"Team"`, so that Python doesn't have errors at runtime.

### Team Model File

We use the same trick in the `team_model.py` file:

{* ./docs_src/tutorial/code_structure/tutorial002_py310/team_model.py hl[1,5:6,14] *}
{* ./docs_src/tutorial/code_structure/tutorial002/team_model.py hl[1,5:6,14] *}

Now we get editor support, autocompletion, inline errors, and **SQLModel** keeps working. 🎉

### App File

Now, just for completeness, the `app.py` file would import the models from both modules:

{* ./docs_src/tutorial/code_structure/tutorial002_py310/app.py hl[4:5,10,12:14] *}
{* ./docs_src/tutorial/code_structure/tutorial002/app.py hl[4:5,10,12:14] *}

And of course, all the tricks with `TYPE_CHECKING` and type annotations in strings are **only needed in the files with circular imports**.

Expand Down
12 changes: 6 additions & 6 deletions docs/tutorial/connect/create-connected-rows.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ We will later update **Spider-Boy** to add him to the **Preventers** team too, b

We will continue with the code in the previous example and we will add more things to it.

{* ./docs_src/tutorial/connect/create_tables/tutorial001_py310.py ln[0] *}
{* ./docs_src/tutorial/connect/create_tables/tutorial001.py ln[0] *}

Make sure you remove the `database.db` file before running the examples to get the same results.

Expand All @@ -57,7 +57,7 @@ And now we will also create the teams there. 🎉

Let's start by creating two teams:

{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[29:35] hl[29:35] *}
{* ./docs_src/tutorial/connect/insert/tutorial001.py ln[29:35] hl[29:35] *}

This would hopefully look already familiar.

Expand All @@ -73,7 +73,7 @@ And finally we **commit** the session to save the changes to the database.

Let's not forget to add this function `create_heroes()` to the `main()` function so that we run it when calling the program from the command line:

{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[61:63] hl[63] *}
{* ./docs_src/tutorial/connect/insert/tutorial001.py ln[61:63] hl[63] *}

## Run it

Expand Down Expand Up @@ -106,7 +106,7 @@ Now let's create one hero object to start.

As the `Hero` class model now has a field (column, attribute) `team_id`, we can set it by using the ID field from the `Team` objects we just created before:

{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[29:39] hl[38] *}
{* ./docs_src/tutorial/connect/insert/tutorial001.py ln[29:39] hl[38] *}

We haven't committed this hero to the database yet, but there are already a couple of things to pay **attention** to.

Expand All @@ -130,7 +130,7 @@ INFO Engine [generated in 0.00025s] (2,)

Let's now create two more heroes:

{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[29:50] hl[40:46] *}
{* ./docs_src/tutorial/connect/insert/tutorial001.py ln[29:50] hl[40:46] *}

When creating `hero_rusty_man`, we are accessing `team_preventers.id`, so that will also trigger a refresh of its data, generating an output of:

Expand Down Expand Up @@ -161,7 +161,7 @@ INFO Engine COMMIT

Now let's refresh and print those new heroes to see their new ID pointing to their teams:

{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[29:58] hl[52:54,56:58] *}
{* ./docs_src/tutorial/connect/insert/tutorial001.py ln[29:58] hl[52:54,56:58] *}

If we execute that in the command line, it will output:

Expand Down
8 changes: 4 additions & 4 deletions docs/tutorial/connect/create-connected-tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Let's start by creating the tables in code.

Import the things we need from `sqlmodel` and create a new `Team` model:

{* ./docs_src/tutorial/connect/create_tables/tutorial001_py310.py ln[1:7] hl[4:7] *}
{* ./docs_src/tutorial/connect/create_tables/tutorial001.py ln[1:7] hl[4:7] *}

This is very similar to what we have been doing with the `Hero` model.

Expand All @@ -76,7 +76,7 @@ Now let's create the `hero` table.

This is the same model we have been using up to now, we are just adding the new column `team_id`:

{* ./docs_src/tutorial/connect/create_tables/tutorial001_py310.py ln[1:16] hl[16] *}
{* ./docs_src/tutorial/connect/create_tables/tutorial001.py ln[1:16] hl[16] *}

Most of that should look familiar:

Expand Down Expand Up @@ -110,11 +110,11 @@ You can learn about setting a custom table name for a model in the Advanced User

Now we can add the same code as before to create the engine and the function to create the tables:

{* ./docs_src/tutorial/connect/create_tables/tutorial001_py310.py ln[19:26] hl[19:20,22,25:26] *}
{* ./docs_src/tutorial/connect/create_tables/tutorial001.py ln[19:26] hl[19:20,22,25:26] *}

And as before, we'll call this function from another function `main()`, and we'll add that function `main()` to the main block of the file:

{* ./docs_src/tutorial/connect/create_tables/tutorial001_py310.py ln[29:34] hl[29:30,33:34] *}
{* ./docs_src/tutorial/connect/create_tables/tutorial001.py ln[29:34] hl[29:30,33:34] *}

## Run the Code

Expand Down
Loading
Loading