--- /dev/null
+# Advanced User Guide
+
+The **Advanced User Guide** will be coming soon to a <del>theater</del> **documentation** near you... π
+
+I just have to `add` it, `commit` it, and `refresh` it. π
+
+It will include:
+
+* How to use the `async` and `await` with the async session.
+* How to run migrations.
+* How to combine **SQLModel** models with SQLAlchemy.
+* ...and more.
--- /dev/null
+# Alternatives, Inspiration and Comparisons
+
+Coming soon...
--- /dev/null
+# Contributing
+
+First, you might want to see the basic ways to [help SQLModel and get help](help.md){.internal-link target=_blank}.
+
+## Developing
+
+If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
+
+### Python
+
+SQLModel supports Python 3.6 and above, but for development you should have at least **Python 3.7**.
+
+### Poetry
+
+**SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.
+
+You can learn how to install it in the <a href="https://python-poetry.org/docs/#installation" class="external-link" target="_blank">Poetry docs</a>.
+
+After having Poetry available, you can install the development dependencies:
+
+<div class="termy">
+
+```console
+$ poetry install
+
+---> 100%
+```
+
+</div>
+
+It will also create a virtual environment automatically and will install all the dependencies and your local SQLModel in it.
+
+### Poetry Shell
+
+To use your current environment, and to have access to all the tools in it (for example `pytest` for the tests) enter into a Poetry Shell:
+
+<div class="termy">
+
+```console
+$ poetry shell
+```
+
+</div>
+
+That will set up the environment variables needed dand will start a new shell with them.
+
+#### Using your local SQLModel
+
+If you create a Python file that imports and uses SQLModel, and run it with the Python from your local Poetry environment, it will use your local SQLModel source code.
+
+And if you update that local SQLModel source code, when you run that Python file again, it will use the fresh version of SQLModel you just edited.
+
+Poetry takes care of making that work. But of course, it will only work in the current Poetry environment, if you install standard SQLModel in another environment (not from the source in the GitHub repo), that will use the standard SQLModel, not your custom version.
+
+### Format
+
+There is a script that you can run that will format and clean all your code:
+
+<div class="termy">
+
+```console
+$ bash scripts/format.sh
+```
+
+</div>
+
+It will also auto-sort all your imports.
+
+### Format imports
+
+There is another script that formats all the imports and makes sure you don't have unused imports:
+
+<div class="termy">
+
+```console
+$ bash scripts/format-imports.sh
+```
+
+</div>
+
+As it runs one command after the other and modifies and reverts many files, it takes a bit longer to run, so it might be easier to use `scripts/format.sh` frequently and `scripts/format-imports.sh` only before committing.
+
+## Docs
+
+The documentation uses <a href="https://www.mkdocs.org/" class="external-link" target="_blank">MkDocs</a> with <a href="https://squidfunk.github.io/mkdocs-material/" class="external-link" target="_blank">Material for MkDocs</a>.
+
+All the documentation is in Markdown format in the directory `./docs`.
+
+Many of the tutorials have blocks of code.
+
+In most of the cases, these blocks of code are actual complete applications that can be run as is.
+
+In fact, those blocks of code are not written inside the Markdown, they are Python files in the `./docs_src/` directory.
+
+And those Python files are included/injected in the documentation when generating the site.
+
+### Docs for tests
+
+Most of the tests actually run against the example source files in the documentation.
+
+This helps making sure that:
+
+* The documentation is up to date.
+* The documentation examples can be run as is.
+* Most of the features are covered by the documentation, ensured by test coverage.
+
+During local development, there is a script that builds the site and checks for any changes, live-reloading:
+
+<div class="termy">
+
+```console
+$ bash scripts/docs-live.sh
+
+<span style="color: green;">[INFO]</span> - Building documentation...
+<span style="color: green;">[INFO]</span> - Cleaning site directory
+<span style="color: green;">[INFO]</span> - Documentation built in 2.74 seconds
+<span style="color: green;">[INFO]</span> - Serving on http://127.0.0.1:8008
+```
+
+</div>
+
+It will serve the documentation on `http://127.0.0.1:8008`.
+
+That way, you can edit the documentation/source files and see the changes live.
+
+## Tests
+
+There is a script that you can run locally to test all the code and generate coverage reports in HTML:
+
+<div class="termy">
+
+```console
+$ bash scripts/test-cov-html.sh
+```
+
+</div>
+
+This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.
+
+## Thanks
+
+Thanks for contributing! β
--- /dev/null
+# Intro to Databases
+
+!!! info
+ Are you a seasoned developer and already know everything about databases? π€
+
+ Then you can skip to the [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank} right away.
+
+If you don't know everything about databases, here's a quick overview.
+
+You can always study much more on your own later.
+
+But this should help you start using databases and being productive with **SQLModel**. π
+
+## What is a Database
+
+So, what is a database?
+
+A **database** is a system to store and manage data in a structured and very efficient way.
+
+!!! tip
+ It's very common to abbreviate the word "database" as **"DB"**.
+
+As there's a lot of information about databases, and it can get very technical and academic, I'll give you a quick overview about some of the main concepts here.
+
+I'll even tell you a bit about different types of databases, including the ones not covered by SQLModel ("NoSQL" databases).
+
+## Why Use a Database
+
+When starting to program, it might **not be obvious** why having a database apart from the code for your program is a **good idea**. Let's start with that.
+
+!!! tip
+ If that's obvious to you, just continue in the next section below. π
+
+In your code you already have **variables**, **dictionaries**, **lists**, etc. They all store **data** in some way already. Why would you need to have a separate database?
+
+If you look closely, your code is **static**, it doesn't really change over time *once you run it*. Of course, you change the code frequently, adding features, etc, but once you start Python running your code, the program stays as it was when you started it. And if you change the code, the program will only change **once you run it again**.
+
+And even if you change things in variables, once the program terminates, all that data that was in **memory** is **gone**. π₯
+
+In most of the cases, the objective of your program is to do something with data *outside* of the program.
+
+* It could be just moving **files** from one place to the other.
+* Or it could be taking data from the user in the **terminal** and showing it differently.
+* Or a **web API** that takes some data and process it in some way, etc.
+
+In most cases, the data *comes from outside* the program or *ends outside the program* (for example, shown on the screen, in a file, etc).
+
+In many cases, you need your program to be able to **create** and store data, **read** it, **update** it, **delete** it, etc.
+
+You could do all that by reading and writing to files from your code. And that works in simple cases. But for most complex systems with data that is a bit more **complex** that strategy is not very efficient. And you would have to deal with a lot of **caveats**, keeping the data in sync, making sure it is safely stored, etc.
+
+Databases are designed to **solve these problems**, making the process of handling data much more efficient, and independent of your code. β¨
+
+## How to Interact with a Database
+
+There are many databases of many types.
+
+### A single file database
+
+A database could be a single file called `heroes.db`, managed with code in a very efficient way. An example would be SQLite, more about that on a bit.
+
+
+
+### A server database
+
+A database could also be a system running as an application on a server, handling multiple files internally in optimized formats.
+
+Like a web server, but communicating in a custom and very efficient way. That is the most common type of database interaction.
+
+In this case, your code would talk to this server application instead of reading or modifying files directly.
+
+The database could be located in a different server/machine:
+
+
+
+Or the database could be located in the same server/machine:
+
+
+
+The most important aspect of these types of databases is that **your code doesn't read or modify** the files containing the data directly.
+
+Instead, your code communicates with the database application and that database application is the one that actually reads and modifies its data files. This is because this database application is normally **much more efficient** than what your code could be.
+
+Some examples of databases that work like this could be **PostgreSQL**, **MySQL**, or **MongoDB**.
+
+### Distributed servers
+
+In some cases, the database could even be a group server applications running on different machines, working together and communicating between them to be more efficient and handle more data.
+
+In this case, your code would talk to one or more of these server applications running on different machines.
+
+
+
+Most of the databases that work as server applications also support multiple servers in one way or another.
+
+Having distributed systems also creates additional challenges, so there's a high chance that you would first interact with a single server application or one based on a single file.
+
+## SQL Databases
+
+We already talked about the different ways to interact with a database and how they handle files, etc. That applies to most or all of the databases.
+
+But there's another way to categorize databases that is very important. As you can imagine, there are many types of databases and many databases in each group. But in general, they can be separated in two big groups: "SQL Databases" and "NoSQL Databases".
+
+We will get to why the name "SQL" in a bit, but first, let's see what is it all about.
+
+### SQLModel for SQL Databases
+
+**SQLModel** is a tool to help you with **SQL Databases**.
+
+It cannot help you much with **NoSQL Databases**. Nevertheless, I'll explain a bit about them here.
+
+## Invent SQL Databases
+
+A long time ago, some smart people realized that a great way to store data was putting it in different tables.
+
+And by "table" I mean just data in a grid, with different columns and rows, pretty much like a single spreadsheet.
+
+Each row would represent a specific item or **record**. And each column would represent a specific attribute or field of that record.
+
+### An example of a big table
+
+Let's imagine that we need to store some data about heroes.
+
+If we worked with a single table to store our heroes, it could be like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Factor</td><td>Sister Margaretβs Bar</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+</table>
+
+That's probably what we would have to do with a single table, for example, with a single spreadsheet.
+
+But there are some problems with this. Let's check some.
+
+#### Single table problems
+
+Imagine that they decided to rename the "Sharp Tower" to "Preventers Tower".
+
+Now we would have to update that in two places.
+
+What happens if our code starts to update that name in one place and suddenly there's a power outage and the computer goes off?
+
+We could end up with inconsistent information, having one place saying "Preventers Tower" and the other one saying "Sharp Tower":
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Preventers Tower β
</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>Preventers</td><td>Sharp Tower π¨</td>
+</tr>
+</table>
+
+And now imagine that we need to add a new hero called "Mahjong" that is part of the team "Z-Force".
+
+We could forget the name of the team and end up adding "Mahjong" with an invalid team name, for example "Y-Force".
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Preventers Tower</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>4</td><td>Mahjong</td><td>Neena Thurgirl</td><td>31</td><td>Y-Force π¨</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And what if a single hero belongs to two teams? We wouldn't have an easy way to put this into a single big table.
+
+### Multiple tables
+
+But these and other problems could be solved better by having the data in multiple tables.
+
+So, instead of having a single table with all the data, we could have one table for the heroes and one for teams, and a way to connect one with the other.
+
+The table for the teams could look like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+Now, the table for the heroes would look almost the same. But remember that we mentioned that we need a way to connect the two tables?
+
+The table for the heroes would now have another column `team_id`. This column shows the relationship from each row (from each hero) to the team they belong to.
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id β¨</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2 β¨</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1 β¨</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1 β¨</td>
+</tr>
+</table>
+
+#### Identifications - Primary Key
+
+In the example above, each one of the rows has an <abbr title='abbreviated from the word "identification", in many cases written as "ID"'>`id`</abbr>. Each ID is unique per table and identifies that particular row.
+
+These SQL databases require having a unique way to identify each row in a table. It could be a combination of columns that is unique, but commonly it is just one single column. This is called the "**primary key**" of the table.
+
+The **primary key** is frequently a single column, commonly it's just an integer generated automatically by the database, and in many cases, the column is simply called `id`.
+
+This **primary key**, in this case the column `id`, has to be unique per table. But two different tables could have the same ID. For example, above, both tables have the ID `2` for two different rows, one for "**Z-Force**" in one table and one for "**Spider-Boy**" in the other table, but that's still okay as long as there's a single one per table.
+
+#### Relationships - Foreign Key
+
+Each row in a table has a single **primary key** (in our example a single column `id`).
+
+For example, the table for the teams has the ID `1` for the team `Preventers` and the ID `2` for the team `Z-Force`.
+
+As these **primary key** IDs can uniquely identify each row on the table for teams, we can now go to the table for heroes and refer to those IDs in the table for teams.
+
+<img alt="table relationships" src="/img/databases/relationships.svg">
+
+So, in the table for heroes, we use the `team_id` column to define a relationship to the *foreign* table for teams. Each value in the `team_id` column on the table with heroes will be the same value as the `id` column of one row in the table wiwth teams.
+
+In the table for heroes we have a **primary key** that is the `id`. But we also have another column `team_id` that refers to a **key** in a **foreign** table. There's a technical term for that too, the `team_id` is a "**foreign key**".
+
+### Relations and Relational Databases
+
+The technical and academic term for each one of these tables is a "**relation**".
+
+You might hear that term a lot when talking about these databases.
+
+It doesn't have the meaning that you would use in English of something being related to something else, even though each of these tables is actually "related" to the others.
+
+The technical term **relation** just refers to each one of these tables.
+
+And because of this technical term, these **SQL Databases** are also called **Relational Databases** (in fact, that is the technically correct term). But it still just refers to these databases made with multiple tables.
+
+### SQL - The Language
+
+After developing these ideas of how to store data in multiple tables they also created a **language** that could be used to interact with them.
+
+The language is called **SQL**, the name comes from for **Structured Query Language**.
+
+Nevertheless, the language is not only used to *query* for data. It is also used to create records/rows, to update them, to delete them. And to manipulate the database, create tables, etc.
+
+This language is supported by all these databases that handle multiple tables, that's why they are called **SQL Databases**. Although, each database has small variations in the SQL language they support.
+
+Let's imagine that the table holding the heroes is called the `hero` table. An example of a SQL query to get all the data from it could look like:
+
+```SQL
+SELECT *
+FROM hero;
+```
+
+And that SQL query would return the table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+</table>
+
+### SQLModel for SQL
+
+**SQLModel** is a library that helps you write Python code with regular Python objects, and then it transfers that to **SQL** statements that it sends to a **SQL Database**.
+
+Next, it receives the data and puts it in Python objects that you can continue to use in your code.
+
+I'll tell you more about SQL, SQLModel, how to use them, and how they are related in the next sections.
+
+!!! info "Technical Details"
+ SQLModel is built on top of SQLAlchemy. It is, in fact, just <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> mixed together with some sugar on top.
+
+## NoSQL Databases
+
+Although SQL Databases are the oldest and most commonly used type of database, there's another (very interesting) category, the one of **NoSQL Databases**.
+
+**NoSQL Databases** covers a wide range of different sub-types, including key-value stores, document stores, graph databases, and more.
+
+**SQLModel** can only help you with SQL Databases. So, that's what we'll talk about in the rest of the documentation.
--- /dev/null
+# Database to Code (ORMs)
+
+Here I'll tell you how **SQLModel** interacts with the database, why you would want to use it (or use a similar tool), and how it relates to SQL.
+
+## SQL Inline in Code
+
+Let's check this example of a simple SQL query to get all the data from the `hero` table:
+
+```SQL
+SELECT *
+FROM hero;
+```
+
+And that SQL query would return the table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+</table>
+
+This SQL language has a little **caveat**. It was not designed to be mixed with normal code in a programming language like Python. π¨
+
+So, if you are working with Python, the simplest option would be to put SQL code inside a string, and send that string directly to the database.
+
+```Python
+statement = "SELECT * FROM hero;"
+
+results = database.execute(statement)
+```
+
+But in that case, you wouldn't have editor support, inline errors, autocompletion, etc. Because for the editor, the SQL statement is just a string of text. If you have an error, the editor wouldn't be able to help. π
+
+And even more importantly, in most of the cases, you would send the SQL strings with modifications and parameters. For example, to get the data for a *specific item ID*, a *range of dates*, etc.
+
+And in most cases, the parameters your code uses to query or modify the data in the database come, in some way, from an external user.
+
+For example, check this SQL query:
+
+```SQL
+SELECT *
+FROM hero
+WHERE id = 2;
+```
+
+It is using the ID parameter `2`. That number `2` probably comes, in some way, from a user input.
+
+The user is probably, in some way, telling your application:
+
+> Hey, I want to get the hero with ID:
+
+```SQL
+2
+```
+
+And the would be this table (with a single row):
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+</table>
+
+### SQL Injection
+
+But let's say that your code takes whatever the external user provides and puts it inside the SQL string before sending it to the database. Something like this:
+
+```Python
+# Never do this! π¨ Continue reading.
+
+user_id = input("Type the user ID: ")
+
+statement = f"SELECT * FROM hero WHERE id = {user_id};"
+
+results = database.execute(statement)
+```
+
+If the external user is actually an attacker, they could send you a malicious SQL string that does something terrible like deleting all the records. That's called a "**SQL Injection**".
+
+For example, imagine that this new attacker user says:
+
+> Hey, I want to get the hero with ID:
+
+```SQL
+2; DROP TABLE hero
+```
+
+Then the code above that takes the user input and puts it in SQL would actually send this to the database:
+
+```SQL
+SELECT * FROM hero WHERE id = 2; DROP TABLE hero;
+```
+
+Check that section added at the end. That's another entire SQL statement:
+
+```SQL
+DROP TABLE hero;
+```
+
+That is how you tell the database in SQL to delete the entire table `hero`.
+
+<a href="http://www.nooooooooooooooo.com/" class="external-link" target="_blank">Nooooo!</a> We lost all the data in the `hero` table! π₯π±
+
+### SQL Sanitization
+
+The process of making sure that whatever the external user sends is safe to use in the SQL string is called **sanitization**.
+
+It comes by default in **SQLModel** (thanks to SQLAlchemy). And many other similar tools would also provide that functionality among many other features.
+
+Now you are ready for <a href="https://xkcd.com/327/" class="external-link" target="_blank">a joke from xkcd</a>:
+
+
+
+## SQL with SQLModel
+
+With **SQLModel**, instead of writing SQL statements directly, you use Python classes and objects to interact with the database.
+
+For example, you could ask the database for the same hero with ID `2` with this code:
+
+```Python
+user_id = input("Type the user ID: ")
+
+session.exec(
+ select(Hero).where(Hero.id == user_id)
+).all()
+```
+
+If the user provides this ID:
+
+```SQL
+2
+```
+
+...the would be this table (with a single row):
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+</table>
+
+### Preventing SQL Injections
+
+If the user is an attacker and tries to send this as the "ID":
+
+```SQL
+2; DROP TABLE hero
+```
+
+Then **SQLModel** will convert that to a literal string `"2; DROP TABLE hero"`.
+
+And then, it will tell the SQL Database to try to find a record with that exact ID instead of injecting the attack.
+
+The difference in the final SQL statement is subtle, but it changes the meaning completely:
+
+```SQL
+SELECT * FROM hero WHERE id = "2; DROP TABLE hero;";
+```
+
+!!! tip
+ Notice the double quotes (`"`) making it a string instead of more raw SQL.
+
+The database will not find any record with that ID:
+
+```SQL
+"2; DROP TABLE hero;"
+```
+
+Then the database will send an empty table as the result because it didn't find any record with that ID.
+
+Then your code will continue to execute and calmly tell the user that it couldn't find anything.
+
+But we never deleted the `hero` table. π
+
+!!! info
+ Of course, there are also other ways to do SQL data sanitization without using a tool like **SQLModel**, but it's still a nice feature you get by default.
+
+### Editor Support
+
+Check that Python snippet above again.
+
+Because we are using **standard Python classes and objects**, your editor will be able to provide you with autocompletion, inline errors, etc.
+
+For example, let's say you wanted to query the database to find a hero based on the secret identity.
+
+Maybe you don't remember how you named the column. Maybe it was:
+
+* `secret_identity`?
+
+...or was it:
+
+* `secretidentity`?
+
+...or:
+
+* `private_name`?
+* `secret_name`?
+* `secretname`?
+
+If you type that in SQL strings in your code, your editor **won't be able to help you**:
+
+```SQL
+statement = "SELECT * FROM hero WHERE secret_identity = 'Dive Wilson';"
+
+results = database.execute(statement)
+```
+
+...your editor will see that as a **long string** with some text inside, and it will **not be able to autocomplete** or detect the error in `secret_identity`.
+
+But if you use common Python classes and objects, your editor will be able to help you:
+
+```Python
+database.execute(
+ select(Hero).where(Hero.secret_name == "Dive Wilson")
+).all()
+```
+
+<img class="shadow" src="/img/db-to-code/autocompletion01.png">
+
+
+## ORMs and SQL
+
+These types of libraries like **SQLModel** (and of course, SQLAlchemy) that translate between SQL and code with classes and objects are called **ORMs**.
+
+**ORM** means **Object-Relational Mapper**.
+
+This is a very common term, but it also comes from quite technical and **academical** concepts π©βπ:
+
+* **Object**: refers to code with classes and instances, normally called "Object Oriented Programming", that's why the "**Object**" part.
+
+For example this class is part of that **Object** Oriented Programming:
+
+```Python
+class Hero(SQLModel):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str
+ secret_name: str
+ age: Optional[int] = None
+```
+
+* **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.
+
+For example this **Relation** or table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+</table>
+
+* **Mapper**: this comes from Math, when there's something that can convert from some set of things to another, that's called a "**mapping function**". That's where the **Mapper** comes from.
+
+
+
+We could also write a **mapping function** in Python that converts from the *set of lowercase letters* to the *set of uppercase letters*, like this:
+
+```Python
+def map_lower_to_upper(value: str):
+ return value.upper()
+```
+
+It's actually a simple idea with a very academic and mathematical name. π
+
+So, an **ORM** is a library that translates from SQL to code, and from code to SQL. All using classes and objects.
+
+There are many ORMs available apart from **SQLModel**, you can read more about some of them in [Alternatives, Inspiration and Comparisons](alternatives.md){.internal-link target=_blank}
+
+## SQL Table Names
+
+!!! info "Technical Background"
+ This is a bit of boring background for SQL purists. Feel free to skip this section. π
+
+When working with pure SQL, it's common to name the tables in plural. So, the table would be named `heroes` instead of `hero`, because it could contain multiple rows, each with one hero.
+
+Nevertheless, **SQLModel** and many other similar tools can generate a table name automatically from your code, as you will see later in the tutorial.
+
+But this name will be derived from a class name. And it's common practice to use **singular** names for classes (e.g. `class Hero`, instead of `class Heroes`). Using singular names for classes like `class Hero` also makes your code more intuitive.
+
+You will see **your own code** a lot more than the internal table names, so it's probably better to keep the code/class convention than the SQL convention.
+
+So, to keep things consistent, I'll keep using the same table names that **SQLModel** would have generated.
+
+!!! tip
+ You can also override the table name. You can read about it in the Advanced User Guide.
\ No newline at end of file
--- /dev/null
+# Features
+
+## Designed for **FastAPI**
+
+**SQLModel** was created by the same <a href="https://tiangolo.com/" class="external-link" target="_blank">author</a> of FastAPI.
+
+<a href="https://fastapi.tiangolo.com" target="_blank"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" style="width: 20%;"></a>
+
+It follows the same design and ideas, and it was created to be the most intuitive way to interact with SQL databases in FastAPI applications.
+
+Nevertheless, SQLModel is completely **independent** of FastAPI and can be used with any other type of application. You can still benefit from its features.
+
+## Just Modern Python
+
+It's all based on standard <abbr title="Python currently supported versions, 3.6 and above.">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
+
+If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">Python types intro</a>.
+
+You will also see a 20 seconds refresher on the section [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank}.
+
+## Editor support
+
+**SQLModel** was designed to be easy and intuitive to use to ensure the best development experience, with autocompletion everywhere.
+
+Here's how your editor might help you:
+
+* in <a href="https://code.visualstudio.com/" class="external-link" target="_blank">Visual Studio Code</a>:
+
+<img class="shadow" src="/img/index/autocompletion02.png">
+
+* in <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a>:
+
+<img class="shadow" src="/img/features/autocompletion01.png">
+
+You will get completion for everything while writing the **minimum** amount of code.
+
+You won't need to keep guessing the types of different attributes in your models, if they could be `None`, etc. Your editor will be able to help you with everything because **SQLModel** is based on **standard Python type annotations**.
+
+**SQLModel** even adopts currently <a href="https://github.com/microsoft/pyright/blob/main/specs/dataclass_transforms.md" class="external-link" target="_blank">in development standards</a> for Python type annotations to ensure the **best developer experience**, so you will get inline errors and autocompletion even while creating new model instances.
+
+<img class="shadow" src="/img/index/autocompletion01.png">
+
+!!! info
+ Don't worry, adopting this in-development standard only affects/improves editor support.
+
+ It doesn't affect performance or correctness. And if the in-progress standard was deprecated your code won't be affected.
+
+ Meanwhile, you will get inline errors (like type checks) and autocompletion on places you wouldn't get with any other library. π
+
+## Short
+
+**SQLModel** has **sensible defaults** for everything, with **optional configurations** everywhere.
+
+But by default, it all **"just works"**.
+
+You can start with the simplest (and most intuitive) type annotations for your data.
+
+And later, you can fine-tune everything with all the power of SQLAlchemy and Pydantic.
+
+## Based on Pydantic
+
+**SQLModel** is based on Pydantic and keeps the same design, syntax, and ideas.
+
+Underneath, β¨ a **SQLModel** model is also a **Pydantic** model. β¨
+
+There was a lot of research and effort dedicated to make it that way.
+
+That means you get all of **Pydantic's features**, including automatic data **validation**, **serialization**, and **documentation**. You can use SQLModel in the same way you can use Pydantic.
+
+You can even create SQLModel models that do *not* represent SQL tables. In that case, they would be **the same as Pydantic models**.
+
+This is useful, in particular, because now you can create a SQL database model that *inherits* from another non-SQL model. You can use that to **reduce code duplication** a lot. It will also make your code more consistent, improve editor support, etc.
+
+This makes it the perfect combination for working with SQL databases in **FastAPI** applications. π
+
+You will learn more about combining different models later in the tutorial.
+
+## Based on SQLAlchemy
+
+**SQLModel** is also based on SQLAlchemy and uses it for everything.
+
+Underneath, β¨ a **SQLModel** model is also a **SQLAlchemy** model. β¨
+
+There was **a lot** of research and effort dedicated to make it that way. In particular, there was a lot of effort and experimentation in making a single model be **both a SQLAlchemy model and a Pydantic** model at the same time.
+
+That means that you get all the power, robustness, and certainty of SQLAlchemy, the <a href="https://www.jetbrains.com/lp/python-developers-survey-2020/" class="external-link" target="_blank">most widely used database library in Python</a>.
+
+**SQLModel** provides its own utilities to <abbr title="with type completion, type checks, etc.">improve the developer experience</abbr>, but underneath, it uses all of SQLAlchemy.
+
+You can even **combine** SQLModel models with SQLAlchemy models.
+
+SQLModel is designed to satisfy the **most common use cases** and to be as simple and convenient as possible for those cases, providing the best developer experience.
+
+But when you have more exotic use cases that require more complex features, you can still plug SQLAlchemy directly into SQLModel and use all its features in your code.
+
+## Tested
+
+* 100% <abbr title="The amount of code that is automatically tested">test coverage</abbr> (currently 97%, reaching 100% in the coming days/weeks).
+* 100% <abbr title="Python type annotations, with this your editor and external tools can give you better support">type annotated</abbr> code base.
--- /dev/null
+# Help SQLModel - Get Help
+
+Do you like **SQLModel**?
+
+Would you like to help SQLModel, other users, and the author?
+
+Or would you like to get help with **SQLModel**?
+
+There are very simple ways to help (several involve just one or two clicks).
+
+And there are several ways to get help too.
+
+## Subscribe to the FastAPI and Friends newsletter
+
+You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](/newsletter/){.internal-link target=_blank} to stay updated about:
+
+* News about FastAPI and friends, including SQLModel π
+* Guides π
+* Features β¨
+* Breaking changes π¨
+* Tips and tricks β
+
+## Star **SQLModel** in GitHub
+
+You can "star" SQLModel in GitHub (clicking the star button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. βοΈ
+
+By adding a star, other users will be able to find it more easily and see that it has been already useful for others.
+
+## Watch the GitHub repository for releases
+
+You can "watch" SQLModel in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. π
+
+There you can select "Releases only".
+
+By doing it, you will receive notifications (in your email) whenever there's a new release (a new version) of **SQLModel** with bug fixes and new features.
+
+## Connect with the author
+
+You can connect with <a href="https://tiangolo.com" class="external-link" target="_blank">me (SebastiΓ‘n RamΓrez / `tiangolo`)</a>, the author.
+
+You can:
+
+* <a href="https://github.com/tiangolo" class="external-link" target="_blank">Follow me on **GitHub**</a>.
+ * See other Open Source projects I have created that could help you.
+ * Follow me to see when I create a new Open Source project.
+* <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Follow me on **Twitter**</a>.
+ * Tell me how you use SQLModel (I love to hear that).
+ * Hear when I make announcements or release new tools.
+* <a href="https://www.linkedin.com/in/tiangolo/" class="external-link" target="_blank">Connect with me on **Linkedin**</a>.
+ * Hear when I make announcements or release new tools (although I use Twitter more often π€·ββ).
+* Read what I write (or follow me) on <a href="https://dev.to/tiangolo" class="external-link" target="_blank">**Dev.to**</a> or <a href="https://medium.com/@tiangolo" class="external-link" target="_blank">**Medium**</a>.
+ * Read other ideas, articles, and read about tools I have created.
+ * Follow me to read when I publish something new.
+
+## Tweet about **SQLModel**
+
+<a href="https://twitter.com/compose/tweet?text=I'm loving SQLModel because... https://github.com/tiangolo/sqlmodel cc: @tiangolo" class="external-link" target="_blank">Tweet about **SQLModel**</a> and let me and others know why you like it. π
+
+I love to hear about how **SQLModel** is being used, what you have liked in it, in which project/company are you using it, etc.
+
+## Help others with issues in GitHub
+
+You can see <a href="https://github.com/tiangolo/sqlmodel/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. π€
+
+## Watch the GitHub repository
+
+You can "watch" SQLModel in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. π
+
+If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue.
+
+Then you can try and help them solve those issues.
+
+## Create issues
+
+You can <a href="https://github.com/tiangolo/sqlmodel/issues/new/choose" class="external-link" target="_blank">create a new issue</a> in the GitHub repository, for example to:
+
+* Ask a **question** or ask about a **problem**.
+* Suggest a new **feature**.
+
+**Note**: if you create an issue, then I'm going to ask you to also help others. π
+
+## Create a Pull Request
+
+You can [contribute](contributing.md){.internal-link target=_blank} to the source code with Pull Requests, for example:
+
+* To fix a typo you found on the documentation.
+* To propose new documentation sections.
+* To fix an existing issue/bug.
+* To add a new feature.
+
+## Sponsor the author
+
+You can also financially support the author (me) through <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub sponsors</a>.
+
+There you could buy me a coffee βοΈ to say thanks. π
+
+## Sponsor the tools that power SQLModel
+
+As you have seen in the documentation, SQLModel stands on the shoulders of giants, Pydantic and SQLAlchemy.
+
+You can also sponsor:
+
+* <a href="https://github.com/sponsors/samuelcolvin" class="external-link" target="_blank">Samuel Colvin (Pydantic)</a>
+* <a href="https://github.com/sponsors/sqlalchemy" class="external-link" target="_blank">SQLAlchemy</a>
+
+---
+
+Thanks! π
--- /dev/null
+<p align="center">
+ <a href="https://sqlmodel.tiangolo.com"><img src="https://sqlmodel.tiangolo.com/img/logo-margin/logo-margin-vector.svg" alt="SQLModel"></a>
+</p>
+<p align="center">
+ <em>SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness.</em>
+</p>
+<p align="center">
+<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3ATest" target="_blank">
+ <img src="https://github.com/tiangolo/sqlmodel/workflows/Test/badge.svg" alt="Test">
+</a>
+<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">
+ <img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">
+</a>
+<a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">
+ <img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">
+</a>
+<a href="https://pypi.org/project/sqlmodel" target="_blank">
+ <img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">
+</a>
+</p>
+
+---
+
+**Documentation**: <a href="https://sqlmodel.tiangolo.com" target="_blank">https://sqlmodel.tiangolo.com</a>
+
+**Source Code**: <a href="https://github.com/tiangolo/sqlmodel" target="_blank">https://github.com/tiangolo/sqlmodel</a>
+
+---
+
+SQLModel is a library for interacting with <abbr title='Also called "Relational databases"'>SQL databases</abbr> from Python code, with Python objects. It is designed to be intuitive, easy to use, highly compatible, and robust.
+
+**SQLModel** is based on Python type annotations, and powered by <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> and <a href="https://sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a>.
+
+The key features are:
+
+* **Intuitive to write**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging. Designed to be easy to use and learn. Less time reading docs.
+* **Easy to use**: It has sensible defaults and does a lot of work underneath to simplify the code you write.
+* **Compatible**: It is designed to be compatible with **FastAPI**, Pydantic, and SQLAlchemy.
+* **Extensible**: You have all the power of SQLAlchemy and Pydantic underneath.
+* **Short**: Minimize code duplication. A single type annotation does a lot of work. No need to duplicate models in SQLAlchemy and Pydantic.
+
+## SQL Databases in FastAPI
+
+<a href="https://fastapi.tiangolo.com" target="_blank"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" style="width: 20%;"></a>
+
+**SQLModel** is designed to simplify interacting with SQL databases in <a href="https://fastapi.tiangolo.com" class="external-link" target="_blank">FastAPI</a> applications, it was created by the same <a href="https://tiangolo.com/" class="external-link" target="_blank">author</a>. π
+
+It combines SQLAlchemy and Pydantic and tries to simplify the code you write as much as possible, allowing you to reduce the **code duplication to a minimum**, but while getting the **best developer experience** possible.
+
+**SQLModel** is, in fact, a thin layer on top of **Pydantic** and **SQLAlchemy**, carefully designed to be compatible with both.
+
+## Requirements
+
+A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
+
+As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
+
+## Installation
+
+<div class="termy">
+
+```console
+$ pip install sqlmodel
+---> 100%
+Successfully installed sqlmodel
+```
+
+</div>
+
+## Example
+
+For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com" target="_blank">SQLModel documentation</a>.
+
+Here's a quick example. β¨
+
+### A SQL Table
+
+Imagine you have a SQL table called `hero` with:
+
+* `id`
+* `name`
+* `secret_name`
+* `age`
+
+And you want it to have this data:
+
+| id | name | secret_name | age |
+-----|------|-------------|------|
+| 1 | Deadpond | Dive Wilson | null |
+| 2 | Spider-Boy | Pedro Parqueador | null |
+| 3 | Rusty-Man | Tommy Sharp | 48 |
+
+### Create a SQLModel Model
+
+Then you could create a **SQLModel** model like this:
+
+```Python
+from typing import Optional
+
+from sqlmodel import Field, SQLModel
+
+
+class Hero(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str
+ secret_name: str
+ age: Optional[int] = None
+```
+
+That class `Hero` is a **SQLModel** model, the equivalent of a SQL table in Python code.
+
+And each of those class attributes is equivalent to each **table column**.
+
+### Create Rows
+
+Then you could **create each row** of the table as an **instance** of the model:
+
+```Python
+hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
+hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
+```
+
+This way, you can use conventional Python code with **classes** and **instances** that represent **tables** and **rows**, and that way communicate with the **SQL database**.
+
+### Editor Support
+
+Everything is designed for you to get the best developer experience possible, with the best editor support.
+
+Including **autocompletion**:
+
+<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/autocompletion01.png">
+
+And **inline errors**:
+
+<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/inline-errors01.png">
+
+### Write to the Database
+
+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:
+
+```Python hl_lines="18 21 23-27"
+from typing import Optional
+
+from sqlmodel import Field, Session, SQLModel, create_engine
+
+
+class Hero(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str
+ secret_name: str
+ age: Optional[int] = None
+
+
+hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
+hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
+
+
+engine = create_engine("sqlite:///database.db")
+
+
+SQLModel.metadata.create_all(engine)
+
+with Session(engine) as session:
+ session.add(hero_1)
+ session.add(hero_2)
+ session.add(hero_3)
+ session.commit()
+```
+
+That will save a **SQLite** database with the 3 heroes.
+
+### Select from the Database
+
+Then you could write queries to select from that same database, for example with:
+
+```Python hl_lines="15-18"
+from typing import Optional
+
+from sqlmodel import Field, Session, SQLModel, create_engine, select
+
+
+class Hero(SQLModel, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ name: str
+ secret_name: str
+ age: Optional[int] = None
+
+
+engine = create_engine("sqlite:///database.db")
+
+with Session(engine) as session:
+ statement = select(Hero).where(Hero.name == "Spider-Boy")
+ hero = session.exec(statement).first()
+ print(hero)
+```
+
+### Editor Support Everywhere
+
+**SQLModel** was carefully designed to give you the best developer experience and editor support, **even after selecting data** from the database:
+
+<img class="shadow" src="https://sqlmodel.tiangolo.com/img/index/autocompletion02.png">
+
+## SQLAlchemy and Pydantic
+
+That class `Hero` is a **SQLModel** model.
+
+But at the same time, β¨ it is a **SQLAlchemy** model β¨. So, you can combine it and use it with other SQLAlchemy models, or you could easily migrate applications with SQLAlchemy to **SQLModel**.
+
+And at the same time, β¨ it is also a **Pydantic** model β¨. You can use inheritance with it to define all your **data models** while avoiding code duplication. That makes it very easy to use with **FastAPI**.
+
+## License
+
+This project is licensed under the terms of the MIT license.
--- /dev/null
+# Release Notes
+
+## Latest Changes
+
+
+## 0.0.1
+
+* First release. π
--- /dev/null
+# Automatic IDs, None Defaults, and Refreshing Data
+
+In the previous chapter we saw how to add rows to the database using **SQLModel**.
+
+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)`.
+
+But the same `id` field actually **can be `None`** in the Python code, so we declare the type with `Optional[int]`, and set the default value to `Field(default=None)`:
+
+```Python hl_lines="4"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:6-10]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+Next, I'll show you a bit more about the synchronization of data between the database and the Python code.
+
+When do we get an actual `int` from the database in that `id` field? Let's see all that. π
+
+## Create a New `Hero` Instance
+
+When we create a new `Hero` instance, we don't set the `id`:
+
+```Python hl_lines="3-6"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-26]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+### How `Optional` Helps
+
+Because we don't set the `id`, it takes the Python's default value of `None` that we set in `Field(default=None)`.
+
+This is the only reason why we define it with `Optional` and with a default value of `None`.
+
+Because at this point in the code, **before interacting with the database**, the Python value could actually be `None`.
+
+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:
+
+```Python
+next_hero_id = hero_1.id + 1
+```
+
+If we ran this code before saving the hero to the database and the `hero_1.id` was still `None`, we would get an error like:
+
+```
+TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
+```
+
+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`. π
+
+## Print the Default `id` Values
+
+We can confirm that by printing our heroes before adding them to the database:
+
+```Python hl_lines="9-11"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-31]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+That will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+Before interacting with the database
+Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
+Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
+Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
+```
+
+</div>
+
+Notice they all have `id=None`.
+
+That's the default value we defined in the `Hero` model class.
+
+What happens when we `add` these objects to the **session**?
+
+## Add the Objects to the Session
+
+After we add the `Hero` instance objects to the **session**, the IDs are *still* `None`.
+
+We can verify by creating a session using a `with` block, and adding the objects. And then printing them again:
+
+```Python hl_lines="19-21"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-41]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+This will, again, output the `id`s of the objects as `None`:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+After adding to the session
+Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
+Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
+Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
+```
+
+</div>
+
+As we saw before, the **session** is smart and doesn't talk to the database every time we prepare something to be changed, only after we are ready and tell it to `commit` the changes it goes and sends all the SQL to the database to store the data.
+
+## Commit the Changes to the Database
+
+Then we can `commit` the changes in the session, and print again:
+
+```Python hl_lines="13 16-18"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-48]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+And now, something unexpected happens, look at the output, it seems as if the `Hero` instance objects had no data at all:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+// Here the engine talks to the database, the SQL part
+INFO Engine BEGIN (implicit)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00018s] ('Deadpond', 'Dive Wilson', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.0008968s ago] ('Spider-Boy', 'Pedro Parqueador', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001143s ago] ('Rusty-Man', 'Tommy Sharp', 48)
+INFO Engine COMMIT
+
+// And now our prints
+After committing the session
+Hero 1:
+Hero 2:
+Hero 3:
+
+// What is happening here? π±
+```
+
+</div>
+
+What happens is that SQLModel (actually SQLAlchemy) is internally marking those objects as "expired", they **don't have the latest version of their data**. This is because we could have some fields updated in the database, for example, imagine a field `updated_at: datetime` that was automatically updated when we saved changes.
+
+The same way, other values could have changed, so the option the **session** has to be sure and safe is to just internally mark the objects as expired.
+
+And then, next time we access each attribute, for example with:
+
+```Python
+current_hero_name = hero_1.name
+```
+
+...SQLModel (actually SQLAlchemy) will make sure to contact the database and **get the most recent version of the data**, updating that field `name` in our object and then making it available for the rest of the Python expression. In the example above, at that point, Python would be able to continue executing and use that `hero_1.name` value (just updated) to put it in the variable `current_hero_name`.
+
+All this happens automatically and behind the scenes. β¨
+
+And here's the funny and strange thing with our example:
+
+```Python
+print("Hero 1:", hero_1)
+```
+
+We didn't access the object's attributes, like `hero.name`. We only accessed the entire object and printed it, so **SQLAlchemy has no way of knowing** that we want to access this object's data.
+
+## Print a Single Field
+
+To confirm and understand how this **automatic expiration and refresh** of data when accessing attributes work, we can print some individual fields (instance attributes):
+
+```Python hl_lines="21-23 26-28"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-58]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+Now we are actually accessing the attributes, because instead of printing the whole object `hero_1`:
+
+```Python
+print("Hero 1:", hero_1)
+```
+
+...we are now printing the `id` attribute in `hero.id`:
+
+```Python
+print("Hero 1 ID:", hero_1.id)
+```
+
+By accessing the attribute, that **triggers** a lot of work done by SQLModel (actually SQLAlchemy) underneath to **refresh the data from the database**, set it in the object's `id` attribute, and make it available for the Python expression (in this case just to print it).
+
+Let's see how it works:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+// After committing, the objects are expired and have no values
+After committing the session
+Hero 1:
+Hero 2:
+Hero 3:
+
+// Now we will access an attribute like the ID, this is the first print
+After committing the session, show IDs
+
+// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data π€
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00017s] (1,)
+
+// Here's our first print, now we have the database-generated ID
+Hero 1 ID: 1
+
+// Before the next print, refresh the data for the second object
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.001245s ago] (2,)
+
+// Here's our print for the second hero with its auto-generated ID
+Hero 2 ID: 2
+
+// Before the third print, refresh its data
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.002215s ago] (3,)
+
+// And here's our print for the third hero
+Hero 3 ID: 3
+
+// What if we print another attribute like the name?
+After committing the session, show names
+Hero 1 name: Deadpond
+Hero 2 name: Spider-Boy
+Hero 3 name: Rusty-Man
+
+// Because the Session already refreshed these objects with all their data and the session knows they are not expired, it doesn't have to go again to the database for the names π€
+
+```
+
+</div>
+
+## Refresh Objects Explicitly
+
+You just learnt how the **session** refreshes the data automatically behind the scenes, as a side effect, when you access an attribute.
+
+But what if you want to **explicitly refresh** the data?
+
+You can do that too with `session.refresh(object)`:
+
+```Python hl_lines="30-32 35-37"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-67]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+When Python executes this code:
+
+```Python
+session.refresh(hero_1)
+```
+
+...the **session** goes and makes the **engine** communicate with the database to get the recent data for this object `hero_1`, and then the **session** puts the data in the `hero_1` object and marks it as "fresh" or "not expired".
+
+Here's how the output would look like:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+// The first refresh
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00024s] (1,)
+
+// The second refresh
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.001487s ago] (2,)
+
+// The third refresh
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.002377s ago] (3,)
+
+// Now print the data, as it's already refreshed there's no need for the Session to go and refresh it again
+After refreshing the heroes
+Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
+Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
+Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+```
+
+</div>
+
+This could be useful, for example, if you are building a web API to create heroes. And once a hero is created with some data, you return it to the client.
+
+You wouldn't want to return an object that looks empty because the automatic magic to refresh the data was not triggered.
+
+In this case, after committing the object to the database with the **session**, you could refresh it, and then return it to the client. This would ensure that the object has its fresh data.
+
+## Print Data After Closing the Session
+
+Now, as a fnal experiment, we can also print data after the **session** is closed.
+
+There are no surprises here, it still works:
+
+```Python hl_lines="40-42"
+# Code above omitted π
+
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-72]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
+```
+
+</details>
+
+And the output shows again the same data:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Output above ommitted π
+
+// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
+INFO Engine ROLLBACK
+
+// Then we print the data, it works normally
+After the session closes
+Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
+Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
+Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+```
+
+</div>
+
+## Review All the Code
+
+Now let's review all this code once again.
+
+!!! tip
+ Each one of the numbered bubbles shows what each line will print in the output.
+
+ And as we created the **engine** with `echo=True`, we can see the SQL statements being executed at each step.
+
+```{ .python .annotate hl_lines="54" }
+{!./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">
+
+```console
+$ python app.py
+
+INFO Engine BEGIN (implicit)
+INFO Engine PRAGMA main.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine PRAGMA temp.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine
+CREATE TABLE hero (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ secret_name VARCHAR NOT NULL,
+ age INTEGER,
+ PRIMARY KEY (id)
+)
+
+
+INFO Engine [no key 0.00018s] ()
+INFO Engine COMMIT
+Before interacting with the database
+Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
+Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
+Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
+After adding to the session
+Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
+Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
+Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
+INFO Engine BEGIN (implicit)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00022s] ('Deadpond', 'Dive Wilson', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001127s ago] ('Spider-Boy', 'Pedro Parqueador', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
+INFO Engine COMMIT
+After committing the session
+Hero 1:
+Hero 2:
+Hero 3:
+After committing the session, show IDs
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00029s] (1,)
+Hero 1 ID: 1
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.002132s ago] (2,)
+Hero 2 ID: 2
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.003367s ago] (3,)
+Hero 3 ID: 3
+After committing the session, show names
+Hero 1 name: Deadpond
+Hero 2 name: Spider-Boy
+Hero 3 name: Rusty-Man
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00025s] (1,)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.001583s ago] (2,)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.002722s ago] (3,)
+After refreshing the heroes
+Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
+Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
+Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+INFO Engine ROLLBACK
+After the session closes
+Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
+Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
+Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+```
+
+</div>
+
+## Recap
+
+You read all that! That was a lot! Have some cake, you earned it. π°
+
+We discussed how the **session** uses the **engine** to send SQL to the database, to create data and to fetch data too. How it keeps track of "**expired**" and "**fresh**" data. At which moments it **fetches data automatically** (when accessing instance attributes) and how that data is synchronized between objects in memory and the database via the **session**.
+
+If you understood all that, now you know a lot about **SQLModel**, SQLAlchemy, and how the interactions from Python with databases work in general.
+
+If you didn't get all that, it's fine, you can always come back later to <abbr title="See what I did there? π">`refresh`</abbr> the concepts.
+
+I think this might be one of the main types of bugs that cause problems and makes you scratch your head. So, good job studying it! πͺ
--- /dev/null
+# Code Structure and Multiple Files
+
+Let's stop for a second to think about how to structure the code, particularly in **large projects** with multiple files.
+
+## Circular Imports
+
+The class `Hero` has a reference to the class `Team` internally.
+
+But the class `Team` also has a reference to the class `Hero`.
+
+So, if those two classes where in separate files and you tried to import the classes in each other's file directly, it would result in a **circular import**. π
+
+And Python will not be able to handle it and will throw an error. π¨
+
+But we actually want to *mean* that **circular reference**, because in our code, we would be able to do crazy things like:
+
+```Python
+hero.team.heroes[0].team.heroes[1].team.heroes[2].name
+```
+
+And that circular reference is what we are expressing with these **relationship attributes**, that:
+
+* A hero can have a team
+ * That team can have a list of heroes
+ * Each of those heroes can have a team
+ * ...and so on.
+
+Let's see different strategies to **structure the code** accounting for this.
+
+## Single Module for Models
+
+This is the simplest way. β¨
+
+In this solution we are still using **multiple files**, for the `models`, for the `database`, and for the `app`.
+
+And we could have any **other files** necessary.
+
+But in this first case, all the models would live in a **single file**.
+
+The file structure of the project could be:
+
+```
+.
+βββ project
+ βββ __init__.py
+ βββ app.py
+ βββ database.py
+ βββ models.py
+```
+
+We have 3 <abbr title="Python files that can be imported or run">**Python modules**</abbr> (or files):
+
+* `app`
+* `database`
+* `models`
+
+And we also have an empty `__init__.py` file to make this project a "**Python package**" (a collection of Python modules). This way we can use **relative imports** in the `app.py` file/module, like:
+
+```Python
+from .models import Hero, Team
+from .database import engine
+```
+
+We can use these relative imports because, for example, in the file `app.py` (the `app` module) Python knows that it is **part of our Python package** because it is in the same directory as the file `__init__.py`. And all the Python files on the same directory are part of the same Python package too.
+
+### Models File
+
+You could put all the database Models in a single Python module (a single Python file), for example `models.py`:
+
+```Python
+{!./docs_src/tutorial/code_structure/tutorial001/models.py!}
+```
+
+This way, you wouldn't have to deal with circular imports for other models.
+
+And then you could import the models from this file/module in any other file/module in your application.
+
+### Database File
+
+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`:
+
+```Python
+{!./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`:
+
+```Python hl_lines="3-4"
+{!./docs_src/tutorial/code_structure/tutorial001/app.py!}
+```
+
+Here we import the models, the engine, and the function to create all the tables and then we can use them all internally.
+
+### Order Matters
+
+Remember that [Order Matters](create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} when calling `SQLModel.metadata.create_all()`?
+
+The point of that section in the docs is that you have to import the module that has the models **before** calling `SQLModel.metadata.create_all()`.
+
+We are doing that here, we import the models in `app.py` and **after** that we create the database and tables, so we are fine and everything works correctly. π
+
+### Run It in the Command Line
+
+Because now this is a larger project with a **Python package** and not a single Python file, we **cannot** call it just passing a single file name as we did before with:
+
+```console
+$ python app.py
+```
+
+Now we have to tell Python that we want it to execute a *module* that is part of a package:
+
+```console
+$ python -m project.app
+```
+
+The `-m` is to tell Python to call a *module*. And the next thing we pass is a string with `project.app`, that is the same format we would use in an **import**:
+
+```Python
+import project.app
+```
+
+Then Python will execute that module *inside* of that package, and because Python is executing it directly, the same trick with the **main block** that we have in `app.py` will still work:
+
+```Python
+if __name__ == '__main__':
+ main()
+```
+
+So, the output would be:
+
+<div class="termy">
+
+```console
+$ python -m project.app
+
+Created hero: id=1 secret_name='Dive Wilson' team_id=1 name='Deadpond' age=None
+Hero's team: name='Z-Force' headquarters='Sister Margaretβs Bar' id=1
+```
+
+</div>
+
+## Make Circular Imports Work
+
+Let's say that for some reason you hate the idea of having all the database models together in a single file, and you really want to have **separate files** a `hero_model.py` file and a `team_model.py` file.
+
+You can also do it. π There's a couple of things to keep in mind. π€
+
+!!! warning
+ This is a bit more advanced.
+
+ If the solution above already worked for you, that might be enough for you, and you can continue in the next chapter. π€
+
+Let's assume that now the file structure is:
+
+```
+.
+βββ project
+ βββ __init__.py
+ βββ app.py
+ βββ database.py
+ βββ hero_model.py
+ βββ team_model.py
+```
+
+### Circular Imports and Type Annotations
+
+The problem with circular imports is that Python can't resolve them at <abbr title="While it is executing the program, as oposed to the code as just text in a file stored on disk.">*runtime*</abbr>.
+
+but when using Python **type annotations** it's very common to need to declare the type of some variables with classes imported from other files.
+
+And the files with those classes might **also need to import** more things from the first files.
+
+And this ends up *requiring* the same **circular imports** that are not supported in Python at *runtime*.
+
+### Type Annotations and Runtime
+
+But these **type annotations** we want to declare are not needed at *runtime*.
+
+In fact, remember that we used `List["Hero"]`, with a `"Hero"` in a string?
+
+For Python, at runtime, that is **just a string**.
+
+So, if we could add the type annotations we need using the **string versions**, Python wouldn't have a problem.
+
+But if we just put strings in the type annotations, without importing anything, the editor wouldn't know what we mean, and wouldn't be able to help us with **autocompletion** and **inline errors**.
+
+So, if there was a way to "import" some things that act as "imported" only while editing the code but not at <abbr title="When Python is executing the code.">*runtime*</abbr>, that would solve it... And it exists! Exactly that. π
+
+### Import Only While Editing with `TYPE_CHECKING`
+
+To solve it, there's a special trick with a special <abbr title="Technically it's a constant, it doesn't vary in the code π€·">variable</abbr> `TYPE_CHECKING` in the `typing` module.
+
+It has a value of `True` for editors and tools that analyze the code with the type annotations.
+
+But when Python is executing, its value is `False`.
+
+So, we can us it in an `if` block and import things inside the `if` block. And they will be "imported" only for editors, but not at runtime.
+
+### Hero Model File
+
+Using that trick of `TYPE_CHECKING` we can "import" the `Team` in `hero_model.py`:
+
+```Python hl_lines="1 5-6 16"
+{!./docs_src/tutorial/code_structure/tutorial002/hero_model.py!}
+```
+
+Have 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:
+
+```Python hl_lines="1 5-6 14"
+{!./docs_src/tutorial/code_structure/tutorial002/team_model.py!}
+```
+
+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:
+
+```Python hl_lines="4-5 10 12-14"
+{!./docs_src/tutorial/code_structure/tutorial002/app.py!}
+```
+
+And of course, all the tricks with `TYPE_CHECKING` and type annotations in strings are **only needed in the files with circular imports**.
+
+As there are no circular imports with `app.py`, we can just use normal imports and use the classes as normally here.
+
+And running that achieves the same result as before:
+
+<div class="termy">
+
+```console
+$ python -m project.app
+
+Created hero: id=1 age=None name='Deadpond' secret_name='Dive Wilson' team_id=1
+Hero's team: id=1 name='Z-Force' headquarters='Sister Margaretβs Bar'
+```
+
+</div>
+
+## Recap
+
+For the **simplest cases** (for most of the cases) you can just keep all the models in a single file, and structure the rest of the application (including setting up the **engine**) in as many files as you want.
+
+And for the **complex cases** that really need separating all the models in different files, you can use the `TYPE_CHECKING` to make it all work and still have the best developer experience with the best editor support. β¨
--- /dev/null
+# Create and Connect Rows
+
+We will now **create rows** for each table. β¨
+
+The `team` table will look like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And after we finish working with the data in this chapter, the `hero` table will look like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>null</td>
+</tr>
+</table>
+
+Each row in the table `hero` will point to a row in the table `team`:
+
+<img alt="table relationships" src="/img/tutorial/relationships/select/relationships2.svg">
+
+!!! info
+ We will later update **Spider-Boy** to add him to the **Preventers** team too, but not yet.
+
+We will continue with the code in the previous example and we will add more things to it.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
+```
+
+</details>
+
+Make sure you remove the `database.db` file before running the examples to get the same results.
+
+## Create Rows for Teams with **SQLModel**
+
+Let's do the same we did before and define a `create_heroes()` function where we create our heroes.
+
+And now we will also create the teams there. π
+
+Let's start by creating two teams:
+
+```Python hl_lines="3-9"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-37]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+This would hopefully look already familiar.
+
+We start a **session** in a `with` block using the same **engine** we created above.
+
+Then we create two instances of the model class (in this case `Team`).
+
+Next we add those objects to the **session**.
+
+And finally we **commit** the session to save the changes to the database.
+
+## Add It to Main
+
+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:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:63-65]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+## Run it
+
+If we run that code we have up to now, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically start a transaction
+INFO Engine BEGIN (implicit)
+// Add the teams to the database
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [generated in 0.00050s] ('Preventers', 'Sharp Tower')
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [cached since 0.002324s ago] ('Z-Force', 'Sister Margaretβs Bar')
+INFO Engine COMMIT
+```
+
+</div>
+
+You can see in the output that it uses common SQL `INSERT` statements to create the rows.
+
+## Create Rows for Heroes in Code
+
+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:
+
+```Python hl_lines="12"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+We haven't committed this hero to the database yet, but there are already a couple of things to pay **attention** to.
+
+If the database already had some teams, we wouldn't even know **what is the ID** that is going to be automatically assigned to each team by the database, for example, we couldn't just guess `1` or `2`.
+
+But once the team is created and committed to the database, we can access the object's `id` field to get that ID.
+
+Accessing an attribute in a model that was just committed, for example with `team_z_force.id`, automatically **triggers a refresh** of the data from the DB in the object, and then exposes the value for that field.
+
+So, even though we are not committing this hero yet, just because we are using `team_z_force.id`, that will trigger some SQL sent to the database to fetch the data for this team.
+
+That line alone would generate an output of:
+
+```
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [generated in 0.00025s] (2,)
+```
+
+Let's now create two more heroes:
+
+```Python hl_lines="14-20"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-52]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+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:
+
+```
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [cached since 0.001795s ago] (1,)
+```
+
+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).
+
+That means that a hero doesn't have to have a team. And in this case, **Spider-Boy** doesn't have one.
+
+Next we just commit the changes to save them to the database, and that will generate the output:
+
+```
+INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?)
+INFO Engine [generated in 0.00022s] ('Deadpond', 'Dive Wilson', None, 2)
+INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?)
+INFO Engine [cached since 0.0007987s ago] ('Rusty-Man', 'Tommy Sharp', 48, 1)
+INFO Engine INSERT INTO hero (name, secret_name, age, team_id) VALUES (?, ?, ?, ?)
+INFO Engine [cached since 0.001095s ago] ('Spider-Boy', 'Pedro Parqueador', None, None)
+INFO Engine COMMIT
+```
+
+## Refresh and Print Heroes
+
+Now let's refresh and print those new heroes to see their new ID pointing to their teams:
+
+```Python hl_lines="26-28 30-32"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-60]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+
+If we execute that in the command line, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically start a transaction
+INFO Engine BEGIN (implicit)
+
+// Refresh the first hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00021s] (1,)
+// Refresh the second hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.001575s ago] (2,)
+// Refresh the third hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.002518s ago] (3,)
+
+// Print the heroes
+Created hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None
+Created hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48
+Created hero: id=3 secret_name='Pedro Parqueador' team_id=None name='Spider-Boy' age=None
+```
+
+</div>
+
+They now have their `team_id`s, nice!
+
+## Relationships
+
+Relationships in SQL databases are just made by having **columns in one table** referencing the values in **columns on other tables**.
+
+And here we have treated them just like that, more **column fields**, which is what they actually are behind the scenes in the SQL database.
+
+But later in this tutorial, in the next group of chapters, you will learn about **Relationship Attributes** to make it all a lot easier to work with in code. β¨
--- /dev/null
+# Create Connected Tables
+
+Now we will deal with **connected** data put in different tables.
+
+So, the first step is to create more than one table and connect them, so that each row in one table can reference another row in the other table.
+
+We have been working with heroes in a single table `hero`. Let's now add a table `team`.
+
+The team table will look like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+To connect them, we will add another column to the hero table to point to each team by the ID with the `team_id`:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id β¨</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2 β¨</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1 β¨</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1 β¨</td>
+</tr>
+</table>
+
+This way each row in the table `hero` can point to a row in the table `team`:
+
+<img alt="table relationships" src="/img/databases/relationships.svg">
+
+## One-to-Many and Many-to-One
+
+Here we are creating connected data in a relationship where **one** team could have **many** heroes. So it is commonly called a **one-to-many** or **many-to-one** relationship.
+
+The **many-to-one** part can be seen if we start from the heroes, **many** heroes could be part of **one** team.
+
+This is probably the most popular type of relationship, so we'll start with that. But there's also **many-to-many** and **one-to-one** relationships.
+
+## Create Tables in Code
+
+### Create the `team` Table
+
+Let's start by creating the tables in code.
+
+Import the things we need from `sqlmodel` and create a new `Team` model:
+
+```Python hl_lines="6-9"
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:1-9]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
+```
+
+</details>
+
+This is very similar to what we have been doing with the `Hero` model.
+
+The `Team` model will be in a table automatically named `"team"`, and it will have the columns:
+
+* `id`, the primary key, automatically generated by the database
+* `name`, the name of the team
+* `headquarters`, the headquarters of the team
+
+And finally we mark it as a table in the config.
+
+### Create the New `hero` Table
+
+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`:
+
+```Python hl_lines="18"
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:1-18]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
+```
+
+</details>
+
+Most of that should look familiar:
+
+The column will be named `team_id`. It will be an integer, and it could be `NULL` in the database (or `None` in Python), becase there could be some heroes that don't belong to any team.
+
+As we don't have to explicitly pass `team_id=None` when creating a hero, we add a default of `None` to the `Field()`.
+
+Now, here's the new part:
+
+In `Field()` we pass the argument `foreign_key="team.id"`. This tells the database that this column `team_id` is a foreign key to the table `team`. A "**foreign key**" just means that this column will have the **key** to identify a row in a **foreign** table.
+
+The value in this column `team_id` will be the same integer that is in some row in the `id` column on the `team` table. That is what connects the two tables.
+
+#### The Value of `foreign_key`
+
+Notice that the `foreign_key` is a string.
+
+Inside it has the name of the **table**, then a dot, and then the name of the **column**.
+
+This is the name of the **table** in the database, so it is `"team"`, not the name of the **model** class `Team` (with a capital `T`).
+
+If you had a custom table name, you would use that custom table name.
+
+!!! info
+ You can learn about setting a custom table name for a model in the Advanced User Guide.
+
+### Create the Tables
+
+Now we can add the same code as before to create the engine and the function to create the tables:
+
+```Python hl_lines="3-4 6 9-10"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:21-28]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
+```
+
+</details>
+
+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:
+
+```Python hl_lines="3-4 7-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:31-36]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
+```
+
+</details>
+
+## Run the Code
+
+!!! tip
+ Before running the code, make sure you delete the file `database.db` to make sure you start from scratch.
+
+If we run the code we have up to now, it will go and create the database file `database.db` and the tables in it we just defined, `team` and `hero`:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Check if the tables exist already
+INFO Engine PRAGMA main.table_info("team")
+INFO Engine [raw sql] ()
+INFO Engine PRAGMA temp.table_info("team")
+INFO Engine [raw sql] ()
+INFO Engine PRAGMA main.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine PRAGMA temp.table_info("hero")
+INFO Engine [raw sql] ()
+
+// Create the tables
+INFO Engine
+CREATE TABLE team (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ headquarters VARCHAR NOT NULL,
+ PRIMARY KEY (id)
+)
+
+
+INFO Engine [no key 0.00010s] ()
+INFO Engine
+CREATE TABLE hero (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ secret_name VARCHAR NOT NULL,
+ age INTEGER,
+ team_id INTEGER,
+ PRIMARY KEY (id),
+ FOREIGN KEY(team_id) REFERENCES team (id)
+)
+
+
+INFO Engine [no key 0.00026s] ()
+INFO Engine COMMIT
+```
+
+</div>
+
+## Create Tables in SQL
+
+Let's see that same generated SQL code.
+
+As we saw before, those `VARCHAR` columns are converted to `TEXT` in SQLite, which is the database we are using for these experiments.
+
+So, the first SQL could also be written as:
+
+```SQL
+CREATE TABLE team (
+ id INTEGER,
+ name TEXT NOT NULL,
+ headquarters TEXT NOT NULL,
+ PRIMARY KEY (id)
+)
+```
+
+And the second table could be written as:
+
+```SQL hl_lines="8"
+CREATE TABLE hero (
+ id INTEGER,
+ name TEXT NOT NULL,
+ secret_name TEXT NOT NULL,
+ age INTEGER,
+ team_id INTEGER,
+ PRIMARY KEY (id),
+ FOREIGN KEY(team_id) REFERENCES team (id)
+)
+```
+
+The only new is the `FOREIGN KEY` line, and as you can see, it tells the database what column in this table is a foreign key (`team_id`), which other (foreign) table it references (`team`) and which column in that table is the key to define which row to connect (`id`).
+
+Feel free to experiment with it in **DB Browser for SQLite**.
+
+## Recap
+
+Using **SQLModel**, in most of the cases you only need a field (column) with a `foreign_key` in the `Field()` with a string pointing to another table and column to connect two tables.
+
+Now that we have the tables created and connected, let's create some rows in the next chapter. π
--- /dev/null
+# Connect Tables - JOIN - Intro
+
+By this point, you already know how to perform the main <abbr title="Create, read, update, delete.">CRUD</abbr> operations with **SQLModel** using a single table. π
+
+But the main advantage and feature of SQL databases is being able to handle related data, to **connect** or **"join"** different tables together. Connecting rows in one table to rows in another.
+
+Let's see how to use **SQLModel** to manage connected data in the next chapters. π€
+
+!!! tip
+ We will extend this further in the next group of chapters making it even more convenient to work with in Python code, using **relationship attributes**.
+
+ But you should start in this group of chapters first. π€
--- /dev/null
+# Read Connected Data
+
+Now that we have some data in both tables, let's select the data that is connected together.
+
+The `team` table has this data:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And the `hero` table has this data:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>null</td>
+</tr>
+</table>
+
+We will continue with the code in the previous example and we will add more things to it.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+## `SELECT` Connected Data with SQL
+
+Let's start seeing how SQL works when selecting connected data. This is where SQL databases actually shine.
+
+If you don't have a `database.db` file, run that previous program we had written (or copy it from the preview above) to create it.
+
+Now open **DB Browser for SQLite** and open the `database.db` file.
+
+To `SELECT` connected data we use the same keywords we have used before, but now we combine the two tables.
+
+Let's get each hero with the `id`, `name`, and the team `name`:
+
+```SQL
+SELECT hero.id, hero.name, team.name
+FROM hero, team
+WHERE hero.team_id = team.id
+```
+
+!!! info
+ Because we have two columns called `name`, one for `hero` and one for `team`, we can specify them with the prefix of the table name and the dot to make it explicit what we refer to.
+
+Notice that now in the `WHERE` part we are not comparing one column with a literal value (like `hero.name = "Deadpond"`), but we are comparing two columns.
+
+It means, more or less:
+
+> Hey SQL database π, please go and `SELECT` some data for me.
+>
+> I'll first tell you the columns I want:
+>
+> * `id` of the `hero` table
+> * `name` of the `hero` table
+> * `name` of the `team` table
+>
+> I want you to get that data `FROM` the tables `hero` and `team`.
+>
+> And I don't want you to combine each hero with each possible team. Instead, for each hero, go and check each possible team, but give me only the ones `WHERE` the `hero.team_id` is the same as the `team.id`.
+
+If we execute that SQL, it will return the table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Z-Force</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Preventers</td>
+</tr>
+</table>
+
+You can go ahead and try it in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/relationships/select/image01.png">
+
+!!! note
+ Wait, what about Spider-Boy? π±
+
+ He doesn't have a team, so his `team_id` is `NULL` in the database. And this SQL is comparing that `NULL` from the `team_id` with all the `id` fields in the rows in the `team` table.
+
+ As there's no team with an ID of `NULL`, it doesn't find a match.
+
+ But we'll see how to fix that later with a `LEFT JOIN`.
+
+## Select Related Data with **SQLModel**
+
+Now let's use SQLModel to do the same select.
+
+We'll create a function `select_heroes()` just as we did before, but now we'll work with two tables.
+
+Remember SQLModel's `select()` function? It can take more than one argument.
+
+So, we can pass the `Hero` and `Team` model classes. And we can also use both their columns in the `.where()` part:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial001.py[ln:63-65]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial001.py!}
+```
+
+</details>
+
+Notice that in the comparison with `==` we are using the class attributes for both `Hero.team_id` and `Team.id`.
+
+That will generate the appropriate **expression** object that will be converted to the right SQL, equivalent to the SQL example we saw above.
+
+Now we can execute it and get the `results` object.
+
+And as we used `select` with two models, we will receive tuples of instances of those two models, so we can iterate over them naturally in a `for` loop:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial001.py[ln:63-68]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial001.py!}
+```
+
+</details>
+
+For each iteration in the `for` loop we get a a tuple with an instance of the class `Hero` and an instance of the class `Team`.
+
+And in this `for` loop we assign them to the variable `hero` and the variable `team`.
+
+!!! info
+ There was a lot of research, design, and work behind **SQLModel** to make this provide the best possible developer experience.
+
+ And you should get autocompletion and inline errors in your editor for both `hero` and `team`. π
+
+## Add It to Main
+
+As always, we must remember to add this new `select_heroes()` function to the `main()` function to make sure it is executed when we call this program from the command line.
+
+```Python hl_lines="6"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial001.py[ln:71-74]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial001.py!}
+```
+
+</details>
+
+
+## Run the Program
+
+Now we can run the program and see how it shows us each hero with their corresponding team:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Get the heroes with their teams
+2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
+FROM hero, team
+WHERE hero.team_id = team.id
+2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine [no key 0.00015s] ()
+
+// Print the first hero and team
+Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaretβs Bar' id=2 name='Z-Force'
+
+// Print the second hero and team
+Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
+2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine ROLLBACK
+```
+
+</div>
+
+## `JOIN` Tables with SQL
+
+There's an alternative syntax for that SQL query from above using the keyword `JOIN` instead of `WHERE`.
+
+This is the same version from above, using `WHERE`:
+
+```SQL
+SELECT hero.id, hero.name, team.name
+FROM hero, team
+WHERE hero.team_id = team.id
+```
+
+And this is the alternative version using `JOIN`:
+
+```SQL
+SELECT hero.id, hero.name, team.name
+FROM hero
+JOIN team
+ON hero.team_id = team.id
+```
+
+Both are equivalent. The differences in the SQL code are that instead of passing the `team` to the `FROM` part (also called `FROM` clause) we add a `JOIN` and put the `team` table there.
+
+And then, instead of putting a `WHERE` with a condition, we put an `ON` keyword with the condition, because `ON` is the one that comes with `JOIN`. π€·
+
+So, this second version means, more or less:
+
+> Hey SQL database π, please go and `SELECT` some data for me.
+>
+> I'll first tell you the columns I want:
+>
+> * `id` of the `hero` table
+> * `name` of the `hero` table
+> * `name` of the `team` table
+>
+> ...up to here it's the same as before, LOL.
+>
+> Now, I want you to get that data starting `FROM` the table `hero`.
+>
+> And to get the rest of the data, I want you to `JOIN` it with the table `team`.
+>
+> And I want you to join those two tables `ON` the combinations of rows that have the `hero.team_id` with the same value as the `team.id`.
+>
+> Did I say all this before already? I feel like I'm just repeating myself. π€
+
+That will return the same table as before:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Z-Force</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Preventers</td>
+</tr>
+</table>
+
+Also in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/relationships/select/image02.png">
+
+!!! tip
+ Why bother with all this if the result is the same?
+
+ This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if he doesn't have a team.
+
+## Join Tables in **SQLModel**
+
+The same way there's a `.where()` available when using `select()`, there's also a `.join()`.
+
+And in SQLModel (actually SQLAlchemy), when using the `.join()`, because we already declared what is the `foreign_key` when creating the models, we don't have to pass an `ON` part, it is inferred automatically:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial002.py[ln:63-68]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial002.py!}
+```
+
+</details>
+
+Also notice that we are still including `Team` in the `select(Hero, Team)`, because we still want to access that data.
+
+This is equivalent to the previous example.
+
+And if we run it in the command line, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Select using a JOIN with automatic ON
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
+FROM hero JOIN team ON team.id = hero.team_id
+INFO Engine [no key 0.00032s] ()
+
+// Print the first hero and team
+Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaretβs Bar' id=2 name='Z-Force'
+
+// Print the second hero and team
+Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
+
+```
+
+</div>
+
+## `JOIN` Tables with SQL and `LEFT OUTER` (Maybe `JOIN`)
+
+When working with a `JOIN`, you can imagine that you start with a table on the `FROM` part and put that table in an imaginary space on the **left** side.
+
+And then you want another table to `JOIN` the result.
+
+And you put that second table in the **right** side on that imaginary space.
+
+And then you tell the database `ON` which condition it should join those two tables and give you the results back.
+
+But by default, only the rows from both left and right that match the condition will be returned.
+
+<img alt="table relationships" src="/img/databases/relationships.svg">
+
+In this example of tables above π, it would return all the heroes, because every hero has a `team_id`, so every hero can be joined with the `team` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Z-Force</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Preventers</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Preventers</td>
+</tr>
+</table>
+
+### Foreign Keys with `NULL`
+
+But in the database that we are working with in the code above, **Spider-Boy** doesn't have any team, the value of `team_id` is `NULL` in the database.
+
+So there's no way to join the **Spider-Boy** row with some row in the `team` table:
+
+<img alt="table relationships" src="/img/tutorial/relationships/select/relationships2.svg">
+
+Running the same SQL we used above, the resulting table would not include **Spider-Boy** π±:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Z-Force</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Preventers</td>
+</tr>
+</table>
+
+### Include Everything on the `LEFT OUTER`
+
+In this case, that we want to include all heroes in the result even if they don't have a team, we can extend that same SQL using a `JOIN` from above and add a `LEFT OUTER` right before `JOIN`:
+
+```SQL hl_lines="3"
+SELECT hero.id, hero.name, team.name
+FROM hero
+LEFT OUTER JOIN team
+ON hero.team_id = team.id
+```
+
+This `LEFT OUTER` part tells the database that we want to keep everything on the first table, the one on the `LEFT` in the imaginary space, even if those rows would be left **out**, so we want it to include the `OUTER` rows too. In this case, every hero with or without a team.
+
+And that would return the following result, including **Spider-Boy** π:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Z-Force</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Preventers</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>null</td>
+</tr>
+</table>
+
+!!! tip
+ The only difference between this query and the previous is that extra `LEFT OUTER`.
+
+And here's another of the SQL variations, you could write `LEFT OUTER JOIN` or just `LEFT JOIN`, it means the same.
+
+## Join Tables in **SQLModel** with `LEFT OUTER`
+
+Now let's replicate the same query in **SQLModel**.
+
+`.join()` has a parameter we can use `isouter=True` to make the `JOIN` be a `LEFT OUTER JOIN`:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial003.py[ln:63-68]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial003.py!}
+```
+
+</details>
+
+And if we run it, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// SELECT using LEFT OUTER JOIN
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
+FROM hero LEFT OUTER JOIN team ON team.id = hero.team_id
+
+INFO Engine [no key 0.00051s] ()
+
+// Print the first hero and team
+Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaretβs Bar' id=2 name='Z-Force'
+// Print the second hero and team
+Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
+// Print the third hero and team, we included Spider-Boy π
+Hero: id=3 secret_name='Pedro Parqueador' team_id=None name='Spider-Boy' age=None Team: None
+```
+
+</div>
+
+## What Goes in `select()`
+
+You might be wondering why we put the `Team` in the `select()` and not just in the `.join()`.
+
+And then why we didn't include `Hero` in the `.join()`. π€
+
+In SQLModel (actually in SQLAlchemy), all these functions and tools try to **replicate** how it would be to work with the **SQL** language.
+
+Remember that [`SELECT` defines the columns to get and `WHERE` how to filter them?](../where.md#select-and-where){.internal-link target=_blank}.
+
+This also applies here, but with `JOIN` and `ON`.
+
+### Select Only Heroes But Join with Teams
+
+If we only put the `Team` in the `.join()` and not in the `select()` function, we would not get the `team` data.
+
+But we would still be able to **filter** the rows with it. π€
+
+We could even add some additional `.where()` after `.join()` to filter the data more, for example to return only the heroes from one team:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial004.py[ln:63-68]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial004.py!}
+```
+
+</details>
+
+Here we are **filtering** with `.where()` to get only the heroes that belong to the **Preventers** team.
+
+But we are still only requesting the data from the heroes, not their teams.
+
+If we run that, it would output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Select only the hero data
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+// But still join with the team table
+FROM hero JOIN team ON team.id = hero.team_id
+// And filter with WHERE to get only the Preventers
+WHERE team.name = ?
+INFO Engine [no key 0.00066s] ('Preventers',)
+
+// We filter with the team, but only get the hero
+Preventer Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48
+```
+
+</div>
+
+### Include the `Team`
+
+By putting the `Team` in `select()` we tell **SQLModel** and the database that we want the team data too.
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/select/tutorial005.py[ln:63-68]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/select/tutorial005.py!}
+```
+
+</details>
+
+And if we run that, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Select the hero and the team data
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
+// Join the hero with the team table
+FROM hero JOIN team ON team.id = hero.team_id
+// Filter with WHERE to get only Preventers
+WHERE team.name = ?
+INFO Engine [no key 0.00018s] ('Preventers',)
+
+// Print the hero and the team
+Preventer Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
+```
+
+</div>
+
+We still have to `.join()` because otherwise it would just compute all the possible combinations of heroes and teams, for example including **Rusty-Man** with **Preventers** and also **Rusty-Man** with **Z-Force**, which would be a mistake.
+
+## Relationship Attributes
+
+Here we have been using the pure class models directly, but in a future chapter we will also see how to use **Relationship Attributes** that let us interact with the database in a way much more close to the code with Python objects.
+
+And we will also see how to load their data in a different, simpler way, achieving the same we achieved here. β¨
--- /dev/null
+# Remove Data Connections
+
+We currently have a `team` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And a `hero` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+</table>
+
+Let's see how to **remove** connections between rows in tables.
+
+We will continue with the code from the previous chapter.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/update/tutorial001.py!}
+```
+
+</details>
+
+## Break a Connection
+
+We don't really have to delete anyting to break a connection. We can just assign `None` to the foreign key, in this case, to the `team_id`.
+
+Let's say **Spider-Boy** is tired of the lack of friendly neighbors and wants to get out of the **Preventers**.
+
+We can simply set the `team_id` to `None`, and now it doesn't have a connection with the team:
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:31-32]!}
+
+ # Previous code here omitted π
+
+{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:68-72]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/delete/tutorial001.py!}
+```
+
+</details>
+
+Again, we just **assign** a value to that field attribute `team_id`, now the value is `None`, which means `NULL` in the database. Then we `add()` the hero to the session, and then `commit()`.
+
+Next we `refresh()` it to get the recent data, and we print it.
+
+Running that in the command line will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Update the hero
+INFO Engine UPDATE hero SET team_id=? WHERE hero.id = ?
+INFO Engine [cached since 0.07753s ago] (None, 3)
+// Commit the session
+INFO Engine COMMIT
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+// Refresh the hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.1661s ago] (3,)
+
+// Print the hero without a team
+No longer Preventer: id=3 secret_name='Pedro Parqueador' team_id=None name='Spider-Boy' age=None
+```
+
+</div>
+
+That's it, we now removed a connection between rows in different tables by unsetting the foreign key column. π₯
--- /dev/null
+# Update Data Connections
+
+At this point we have a `team` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And a `hero` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>null</td>
+</tr>
+</table>
+
+Some of these heroes are part of a team.
+
+Now we'll see how to **update** those connections between rows tables.
+
+We will continue with the code we used to create some heroes, and we'll update them.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+## Assign a Team to a Hero
+
+Let's say that **Tommy Sharp** uses his "rich uncle" charms to recruit **Spider-Boy** to join the team of the **Preventers**, now we need to update our Spider-Boy hero object to connect it to the Preventers team.
+
+Doing it is just like updating any other field:
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/update/tutorial001.py[ln:31-32]!}
+
+ # Previous code here omitted π
+
+{!./docs_src/tutorial/connect/update/tutorial001.py[ln:62-66]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/update/tutorial001.py!}
+```
+
+</details>
+
+We can simply **assign** a value to that field attribute `team_id`, then `add()` the hero to the session, and then `commit()`.
+
+Next we `refresh()` it to get the recent data, and we print it.
+
+Running that in the command line will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Update the hero
+INFO Engine UPDATE hero SET team_id=? WHERE hero.id = ?
+INFO Engine [generated in 0.00014s] (1, 3)
+// Commit the session saving the changes
+INFO Engine COMMIT
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+// Refresh the hero data
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.08837s ago] (3,)
+
+// Print the updated hero
+Updated hero: id=3 secret_name='Pedro Parqueador' team_id=1 name='Spider-Boy' age=None
+```
+
+</div>
+
+And now **Spider-Boy** has the `team_id=1`, which is the ID of the Preventers. π
+
+Let's now see how to remove connections in the next chapter. π₯
--- /dev/null
+# Create a Table with SQL
+
+Let's get started!
+
+We will:
+
+* Create a SQLite database with **DB Browser for SQLite**
+* Create a table in the database with **DB Browser for SQLite**
+
+We'll add data later. For now, we'll create the database and the first table structure.
+
+We will create a table to hold this data:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+## Create a Database
+
+**SQLModel** and SQLAlchemy are based on SQL.
+
+They are designed to help you with using SQL through Python classes and objects. But it's still always very useful to understand SQL.
+
+So let's start with a simple, pure SQL example.
+
+Open **DB Browser for SQLite**.
+
+Click the button <kbd>New Database</kbd>.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image001.png">
+
+A dialog should show up. Go to the [project directory you created](./index.md#create-a-project){.internal-link target=_blank} and save the file with a name of `database.db`.
+
+!!! tip
+ It's common to save SQLite database files with an extension of `.db`. Sometimes also `.sqlite`.
+
+## Create a Table
+
+After doing that, it might prompt you to create a new table right away.
+
+If it doesn't, click the button <kbd>Create Table</kbd>.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image002.png">
+
+Then you will see the dialog to create a new table.
+
+So, let's create a new table called `hero` with the following columns:
+
+* `id`: an `INTEGER` that will be the **primary key** (check `PK` β
).
+* `name`: a `TEXT`, it should be `NOT NULL` (check `NN` β
), so, it should always have a value.
+* `secret_name`: a `TEXT`, it should be `NOT NULL` too (check `NN` β
).
+* `age`: an `INTEGER`, this one can be `NULL`, so you don't have to check anything else.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image003.png">
+
+Click <kbd>OK</kbd> to create the table.
+
+While you click on the <kbd>Add</kbd> button and add the information, it will create and update the SQL statement that is executed to create the table:
+
+```{ .sql .annotate }
+CREATE TABLE "hero" ( --(1)
+ "id" INTEGER, --(2)
+ "name" TEXT NOT NULL, --(3)
+ "secret_name" TEXT NOT NULL, --(4)
+ "age" INTEGER, --(5)
+ PRIMARY KEY("id") --(6)
+); --(7)
+```
+
+1. Create a table with the name `hero`. Also notice that the columns for this table are declared inside the parenthesis " `(`" that starts here.
+2. The `id` column, an `INTEGER`. This is declared as the primary key at the end.
+3. The `name` column, a `TEXT`, and it should always have a value `NOT NULL`.
+4. The `secret_name` column, another `TEXT`, also `NOT NULL`.
+5. The `age` column, an `INTEGER`. This one doesn't have `NOT NULL`, so it *can* be `NULL`.
+6. The `PRIMARY KEY` of all this is the `id` column.
+7. This is the end of the SQL table, with the final parenthesis "`)`". It also has the semicolon "`;`" that marks the end of the SQL statement. There could be more SQL statements in the same SQL string.
+
+Now you will see that it shows up in the list of Tables with the columns we specified. π
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image004.png">
+
+The only step left is to click <kbd>Write Changes</kbd> to save the changes to the file.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image005.png">
+
+After that, the new table is saved in this database on the file `./database.db`.
+
+## Confirm the Table
+
+Let's confirm that it's all saved.
+
+First click the button <kbd>Close Database</kbd> to close the database.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image006.png">
+
+Now click on <kbd>Open Database</kbd> to open the database again, and select the same file `./database.db`.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image007.png">
+
+You will see again the same table we created.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image008.png">
+
+## Create the Table again, with SQL
+
+Now, to see how is it that SQL works, let's create the table again, but with SQL.
+
+Click the <kbd>Close Database</kbd> button again.
+
+And delete that `./database.db` file in your project directory.
+
+And click again on <kbd>New Database</kbd>.
+
+This time, if you see the dialog to create a new table, just close it by clicking the <kbd>Cancel</kbd> button.
+
+And now, go to the tab <kbd>Execute SQL</kbd>.
+
+Write the same SQL that was generated in the previous step:
+
+```SQL
+CREATE TABLE "hero" (
+ "id" INTEGER,
+ "name" TEXT NOT NULL,
+ "secret_name" TEXT NOT NULL,
+ "age" INTEGER,
+ PRIMARY KEY("id")
+);
+```
+
+Then click the "Execute all" <kbd>βΆ</kbd> button.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image009.png">
+
+You will see the "execution finished successfully" message.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image010.png">
+
+And if you go back to the <kbd>Database Structure</kbd> tab, you will see that you effectively created again the same table.
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image008.png">
+
+## Learn More SQL
+
+I will keep showing you small bits of SQL through this tutorial. And you don't have to be a SQL expert to use **SQLModel**.
+
+But if you are curious and want to get a quick overview of SQL, I recommend the visual documentation from SQLite, on <a href="https://www.sqlite.org/lang.html" class="external-link" target="_blank">SQL As Understood By SQLite</a>.
+
+You can start with <a href="https://www.sqlite.org/lang_createtable.html" class="external-link" target="_blank">`CREATE TABLE`</a>.
+
+Of course, you can also go and take a full SQL course or read a book about SQL, but you don't need more than what I'll explain here on the tutorial to start being productive with **SQLModel**. π€
+
+## Recap
+
+We saw how to interact with SQLite databases in files using **DB Browser for SQLite** in a visual user interface.
+
+We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correclty, to debug, etc.
+
+In the next chapters we will start using **SQLModel** to interact with the database, and we will continue to use **DB Browser for SQLite** at the same time to look at the database underneath. π
--- /dev/null
+# Create a Table with SQLModel - Use the Engine
+
+Now let's get to the code. π©βπ»
+
+Make sure you are inside of your project directory and with your virtual environment activated as [explained in the previous chapter](index.md){.internal-link target=_blank}.
+
+We will:
+
+* Define a table with **SQLModel**
+* Create the same SQLite database and table with **SQLModel**
+* Use **DB Browser for SQLite** to confirm the operations
+
+Here's a reminder of the table structure we want:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+## Create the Table Model Class
+
+The first thing we need to do is create a class to represent the data in the table.
+
+A class like this that represents some data is commonly called a **model**.
+
+!!! tip
+ That's why this package is called `SQLModel`. Because it's mainly used to create **SQL Models**.
+
+For that, we will import `SQLModel` (plus other things we will also use) and create a class `Hero` that inherits from `SQLModel` and represents the **table model** for our heroes:
+
+```Python hl_lines="3 6"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+This class `Hero` **represents the table** for our heroes. And each instance we create later will **represent a row** in the table.
+
+We use the config `table=True` to tell **SQLModel** that this is a **table model**, it represents a table.
+
+!!! info
+ It's also possible to have models without `table=True`, those would be only **data models**, without a table in the database, they would not be **table models**.
+
+ Those **data models** will be **very useful later**, but for now, we'll just keep adding the `table=True` configuration.
+
+## Define the Fields, Columns
+
+The next step is to define the fields or columns of the class by using standard Python type annotations.
+
+The name of each of these variables will be the name of the column in the table.
+
+And the type of each of them will also be the type of table column:
+
+```Python hl_lines="1 3 7-10"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+Let's now see with more detail these field/column declarations.
+
+### Optional Fields, Nullable Columns
+
+Let's start with `age`, notice that it has a type of `Optional[int]`.
+
+And we import that `Optional` from the `typing` standard module.
+
+That is the standard way to declare that something "could be an `int` or `None`" in Python.
+
+And we also set the default value of `age` to `None`.
+
+```Python hl_lines="1 10"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+!!! tip
+ We also define `id` with `Optional`. But we will talk about `id` below.
+
+This way, we tell **SQLModel** that `age` is not required when validating data and that it has a default value of `None`.
+
+And we also tell it that, in the SQL database, the default value of `age` is `NULL` (the SQL equivalent to Python's `None`).
+
+So, this column is "nullable" (can be set to `NULL`).
+
+!!! info
+ In terms of **Pydantic**, `age` is an **optional field**.
+
+ In terms of **SQLAlchemy**, `age` is a **nullable column**.
+
+### Primary Key `id`
+
+Now let's review the `id` field. This is the <abbr title="That unique identifier of each row in a specific table.">**primary key**</abbr> of the table.
+
+So, we need to mark `id` as the **primary key**.
+
+To do that, we use the special `Field` function from `sqlmodel` and set the argument `primary_key=True`:
+
+```Python hl_lines="3 7"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
+
+But inside the SQL database, it is **always required** and can't be `NULL`. Why should we declare it with `Optional`?
+
+The `id` will be required in the database, but it will be *generated by the database*, not by our code.
+
+So, whenever we create an instance of this class (in the next chapters), we *will not set the `id`*. And the value of `id` will be `None` **until we save it in the database**, and then it will finally have a value.
+
+```Python
+my_hero = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
+
+do_something(my_hero.id) # Oh no! my_hero.id is None! π±π¨
+
+# Imagine this saves it to the database
+somehow_save_in_db(my_hero)
+
+do_something(my_hero.id) # Now my_hero.id has a value generated in DB π
+```
+
+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`.
+
+<img class="shadow" src="/img/create-db-and-table/inline-errors01.png">
+
+Now, because we are taking the place of the default value with our `Field()` function, we set **the actual default value** of `id` to `None` with the argument `default=None` in `Field()`:
+
+```Python
+Field(default=None)
+```
+
+If we didn't set the `default` value, whenever we use this model later to do data validation (powered by Pydantic) it would *accept* a value of `None` apart from an `int`, but it would still **require** passing that `None` value. And it would be confusing for whoever is using this model later (probably us), so **better set the default value here**.
+
+## Create the Engine
+
+Now we need to create the SQLAlchemy **Engine**.
+
+It is an object that handles the communication with the database.
+
+If you have a server database (for example PostgreSQL or MySQL), the **engine** will hold the **network connections** to that database.
+
+Creating the **engine** is very simple, just call `create_engine()` with a URL for the database to use:
+
+```Python hl_lines="3 16"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-16]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+You should normally have a single **engine** object for your whole application and re-use it everywhere.
+
+!!! tip
+ There's another related thing called a **Session** that normally should *not* be a single object per application.
+
+ But we will talk about it later.
+
+### Engine Database URL
+
+Each supported database has it's own URL type. For example, for **SQLite** it is `sqlite:///` followed by the file path. For example:
+
+* `sqlite:///database.db`
+* `sqlite:///databases/local/application.db`
+* `sqlite:///db.sqlite`
+
+For SQLAlchemy, there's also a special one, which is a database all *in memory*, this means that it is deleted after the program terminates, and it's also very fast:
+
+* `sqlite://`
+
+```Python hl_lines="13-14 16"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-19]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the <a href="https://docs.sqlalchemy.org/en/14/core/engines.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
+
+### Engine Echo
+
+In this example, we are also using the argument `echo=True`.
+
+It will make the engine print all the SQL statements it executes, which can help you understand what's happening.
+
+It is particularly useful for **learning** and **debugging**:
+
+```Python hl_lines="16"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-16]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+But in production, you would probably want to remove `echo=True`:
+
+```Python
+engine = create_engine(sqlite_url)
+```
+
+### Engine Technical Details
+
+!!! tip
+ If you didn't know about SQLAlchemy before and are just learning **SQLModel**, you can probably skip this section, scroll below.
+
+You can read a lot more about the engine in the <a href="https://docs.sqlalchemy.org/en/14/tutorial/engine.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
+
+**SQLModel** defines it's own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0).
+
+And SQLModel's version of `create_engine()` is type annotated internally, so your editor will be able to help you with autocompletion and inline errors.
+
+## Create the Database and Table
+
+Now everything is in place to finally create the database and table:
+
+```Python hl_lines="18"
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+!!! tip
+ Creating the engine doesn't create the `database.db` file.
+
+ But once we run `SQLModel.metadata.create_all(engine)`, it creates the `database.db` file **and** creates the `hero` table in that database.
+
+ Both things are done in this single step.
+
+Let's unwrap that:
+
+```Python
+SQLModel.metadata.create_all(engine)
+```
+
+### SQLModel MetaData
+
+The `SQLModel` class has a `metadata` attribute. It is an instance of a class `MetaData`.
+
+Whenever you create a class that inherits from `SQLModel` **and is configured with `table = True`**, it is registered in this `metadata` attribute.
+
+So, by the last line, `SQLModel.metadata` already has the `Hero` registered.
+
+### Calling `create_all()`
+
+This `MetaData` object at `SQLModel.metadata` has a `create_all()` method.
+
+It takes an **engine** and uses it to create the database and all the tables registered in this `MetaData` object.
+
+### SQLModel MetaData Order Matters
+
+This also means that you have to call `SQLModel.metadata.create_all()` *after* the code that creates new model classes inheriting from `SQLModel`.
+
+For example, let's imagine you do this:
+
+* Create the models in one Python file `models.py`.
+* Create the engine object in a file `db.py`.
+* Create your main app and call `SQLModel.metadata.create_all()` in `app.py`.
+
+If you only imported `SQLModel` and tried to call `SQLModel.metadata.create_all()` in `app.py`, it would not create your tables:
+
+```Python
+# This wouldn't work! π¨
+from sqlmodel import SQLModel
+
+from .db import engine
+
+SQLModel.metadata.create_all(engine)
+```
+
+It wouldn't work because when you import `SQLModel` alone, Python doesn't execute all the code creating the classes inheriting from it (in our example, the class `Hero`), so `SQLModel.metadata` is still empty.
+
+But if you import the models *before* calling `SQLModel.metadata.create_all()`, it will work:
+
+```Python
+from sqlmodel import SQLModel
+
+from . import models
+from .db import engine
+
+SQLModel.metadata.create_all(engine)
+```
+
+This would work because by importing the models, Python executes all the code creating the classes inheriting from `SQLModel` and registering them in the `SQLModel.metadata`.
+
+As an alternative, you could import `SQLModel` and your models inside of `db.py`:
+
+```Python
+# db.py
+from sqlmodel import SQLModel, create_engine
+from . import models
+
+
+sqlite_file_name = "database.db"
+sqlite_url = f"sqlite:///{sqlite_file_name}"
+
+engine = create_engine(sqlite_url)
+```
+
+And then import `SQLModel` *from* `db.py` in `app.py`, and there call `SQLModel.metadata.create_all()`:
+
+```Python
+# app.py
+from .db import engine, SQLModel
+
+SQLModel.metadata.create_all(engine)
+```
+
+The import of `SQLModel` from `db.py` would work because `SQLModel` is also imported in `db.py`.
+
+And this trick would work correctly and create the tables in the database because by importing `SQLModel` from `db.py`, Python executes all the code creating the classes that inherit from `SQLModel` in that `db.py` file, for example, the class `Hero`.
+
+## Migrations
+
+For this simple example, and for most of the **Tutorial - User Guide**, using `SQLModel.metadata.create_all()` is enough.
+
+But for a production system you would probably want to use a system to migrate the database.
+
+This would be useful and important, for example, whenever you add or remove a column, add a new table, change a type, etc.
+
+But you will learn about migrations later in the Advanced User Guide.
+
+## Run The Program
+
+Let's run the program to see it all working.
+
+Put the code it in a file `app.py` if you haven't already.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
+```
+
+</details>
+
+!!! tip
+ Remember to [activate the virtual environment](./index.md#create-a-python-virtual-environment){.internal-link target=_blank} before running it.
+
+Now run the program with Python:
+
+<div class="termy">
+
+```console
+// We set echo=True, so this will show the SQL code
+$ python app.py
+
+// First, some boilerplate SQL that we are not that intereted in
+
+INFO Engine BEGIN (implicit)
+INFO Engine PRAGMA main.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine PRAGMA temp.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine
+
+// Finally, the glorious SQL to create the table β¨
+
+CREATE TABLE hero (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ secret_name VARCHAR NOT NULL,
+ age INTEGER,
+ PRIMARY KEY (id)
+)
+
+// More SQL boilerplate
+
+INFO Engine [no key 0.00020s] ()
+INFO Engine COMMIT
+```
+
+</div>
+
+!!! info
+ I simplified the output above a bit to make it easier to read.
+
+ But in reality, instead of showing:
+
+ ```
+ INFO Engine BEGIN (implicit)
+ ```
+
+ it would show something like:
+
+ ```
+ 2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
+ ```
+
+### `TEXT` or `VARCHAR`
+
+In the example in the previous chapter we created the table using `TEXT` for some columns.
+
+But in this output SQLAlchemy is using `VARCHAR` instead. Let's see what's going on.
+
+Remember that [each SQL Database has some different variations in what they support?](../databases/#sql-the-language){.internal-link target=_blank}
+
+This is one of the differences. Each database supports some particular **data types**, like `INTEGER` and `TEXT`.
+
+Some databases have some particular types that are special for certain things. For example, PostgreSQL and MySQL support `BOOLEAN` for values of `True` and `False`. SQLite accepts SQL with booleans, even when defining table columns, but what it actually uses internally are `INTEGER`s, with `1` to represent `True` and `0` to represent `False`.
+
+The same way, there are several possible types for storing strings. SQLite uses the `TEXT` type. But other databases like PostgreSQL and MySQL use the `VARCHAR` type by default, and `VARCHAR` is one of the most common data types.
+
+**`VARCHAR`** comes from **variable** length **character**.
+
+SQLAlchemy generates the SQL statements to create tables using `VARCHAR`, and then SQLite receives them, and internally converts them to `TEXT`s.
+
+Additional to the difference between those two data types, some databases like MySQL require setting a maximum length for the `VARCHAR` types, for example `VARCHAR(255)` sets the maximum number of characters to 255.
+
+To make it easier to start using **SQLModel** right away independent of the database you use (even with MySQL), and without any extra configurations, by default, `str` fields are interpreted as `VARCHAR` in most databases and `VARCHAR(255)` in MySQL, this way you know the same class will be compatible with the most popular databases without extra effort.
+
+!!! tip
+ You will learn how to change the maximum length of string columns later in the Advanced Tutorial - User Guide.
+
+### Verify the Database
+
+Now, open the database with **DB Browser for SQLite**, you will see that the program created the table `hero` just as before. π
+
+<img class="shadow" src="/img/create-db-and-table-with-db-browser/image008.png">
+
+## Refactor Data Creation
+
+Now let's restructure the code a bit to make it easier to **reuse**, **share**, and **test** later.
+
+Let's move the code that has the main **side effects**, that changes data (creates a file with a database and a table) to a function.
+
+In this example it's just the `SQLModel.metadata.create_all(engine)`.
+
+Let's put it in a function `create_db_and_tables()`:
+
+```Python hl_lines="22-23"
+{!./docs_src/tutorial/create_db_and_table/tutorial002.py[ln:1-20]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
+```
+
+</details>
+
+If `SQLModel.metadata.create_all(engine)` was not in a function and we tried to import something from this module (from this file) in another, it would try to create the database and table **every time**.
+
+We don't want that to happen like that, only when we **intend** it to happen, that's why we put it in a function.
+
+Now we would be able to, for example, import the `Hero` class in some other file without having those **side effects**.
+
+!!! tip
+ π
**Spoiler alert**: The function is called `create_db_and_tables()` because we will have more **tables** in the future with other classes apart from `Hero`. π
+
+### Create Data as a Script
+
+We prevented the side effects when importing something from your `app.py` file.
+
+But we still want it to **create the database and table** when we call it with Python directly as an independent script from the terminal, just as as above.
+
+!!! tip
+ Think of the word **script** and **program** as interchangeable.
+
+ The word **script** often implies that the code could be run independently and easily. Or in some cases it refers to a relatively simple program.
+
+For that we can use the special variable `__name__` in an `if` block:
+
+```Python hl_lines="23-24"
+{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
+```
+
+### About `__name__ == "__main__"`
+
+The main purpose of the `__name__ == "__main__"` is to have some code that is executed when your file is called with:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Something happens here β¨
+```
+
+</div>
+
+...but is not called when another file imports it, like in:
+
+```Python
+from app import Hero
+```
+
+!!! tip
+ That `if` block using `if __name__ == "__main__":` is sometimes called the "**main block**".
+
+ The official name (in the <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python docs</a>) is "**Top-level script environment**".
+
+#### More details
+
+Let's say your file is named `myapp.py`.
+
+If you run it with:
+
+<div class="termy">
+
+```console
+$ python myapp.py
+
+// This will call create_db_and_tables()
+```
+
+</div>
+
+...then the internal variable `__name__` in your file, created automatically by Python, will have as value the string `"__main__"`.
+
+So, the function in:
+
+```Python hl_lines="2"
+if __name__ == "__main__":
+ create_db_and_tables()
+```
+
+...will run.
+
+---
+
+This won't happen if you import that module (file).
+
+So, if you have another file `importer.py` with:
+
+```Python
+from myapp import Hero
+
+# Some more code
+```
+
+...in that case, the automatic variable inside of `myapp.py` will not have the variable `__name__` with a value of `"__main__"`.
+
+So, the line:
+
+```Python hl_lines="2"
+if __name__ == "__main__":
+ create_db_and_tables()
+```
+
+...will **not** be executed.
+
+!!! info
+ For more information, check <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">the official Python docs</a>.
+
+## Last Review
+
+After those changes, you could run it again, and it would generate the same output as before.
+
+But now we can import things from this module in other files.
+
+Now, let's give the code a final look:
+
+```{.python .annotate}
+{!./docs_src/tutorial/create_db_and_table/tutorial003.py!}
+```
+
+{!./docs_src/tutorial/create_db_and_table/annotations/en/tutorial003.md!}
+
+!!! tip
+ Review what each line does by clicking each number bubble in the code. π
+
+## Recap
+
+We learnt how to use **SQLModel** to define how a table in the database should look like, and we created a database and a table using **SQLModel**.
+
+We also refactored the code to make it easier to reuse, share, and test later.
+
+In the next chapters we will see how **SQLModel** will help us interact with SQL databases from code. π€
--- /dev/null
+# Delete Data - DELETE
+
+Now let's delete some data using **SQLModel**.
+
+## Continue From Previous Code
+
+As before, we'll continue from where we left off with the previous code.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial003.py!}
+```
+
+</details>
+
+Remember to remove the `database.db` file before running the examples to get the same results.
+
+## Delete with SQL
+
+This `Spider-Youngster` is getting too weird, so let's just delete it.
+
+But don't worry, we'll reboot it later with a new story. π
+
+Let's see how to delete it with **SQL**:
+
+```SQL hl_lines="1"
+DELETE
+FROM hero
+WHERE name = "Spider-Youngster"
+```
+
+This means, more or less:
+
+> Hey SQL database π, I want to `DELETE` rows `FROM` the table called `hero`.
+>
+> Please delete all the rows `WHERE` the value of the column `name` is equal to `"Spider-Youngster"`.
+
+Remember that when using a `SELECT` statement it has the form:
+
+```SQL
+SELECT [some stuff here]
+FROM [name of a table here]
+WHERE [some condition here]
+```
+
+`DELETE` is very similar, and again we use `FROM` to tell the table to work on, and we use `WHERE` to tell the condition to use to match the rows that we want to delete.
+
+You can try that in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/delete/image01.png">
+
+Have in mind that `DELETE` is to delete entire **rows**, not single values in a row.
+
+If you want to "delete" a single value in a column while **keeping the row**, you would instead **update** the row as explained in the previous chapter, setting the specific value of the column in that row to `NULL` (to `None` in Python).
+
+Now let's delete with **SQLModel**.
+
+To get the same results, delete the `database.db` file before running the examples.
+
+## Read From the Database
+
+We'll start by selecting the hero `"Spider-Youngster"` that we updated in the previous chapter, this is the one we will delete:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-77]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+As this is a new function `delete_heroes()`, we'll also add it to the `main()` function so that we call it when executing the program from the command line:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:92-100]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+That will print the same existing hero **Spider-Youngster**:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate and previous output omitted π
+
+// The SELECT with WHERE
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00011s] ('Spider-Youngster',)
+
+// Print the hero as obtained from the database
+Hero: name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
+```
+
+</div>
+
+## Delete the Hero from the Session
+
+Now, very similar to how we used `session.add()` to add or update new heroes, we can use `session.delete()` to delete the hero from the session:
+
+```Python hl_lines="10"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-79]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+## Commit the Session
+
+To save the current changes in the session, **commit** it.
+
+This will save all the changes stored in the **session**, like the deleted hero:
+
+```Python hl_lines="11"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-80]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+The same as we have seen before, `.commit()` will also save anything else that was added to the session. Including updates, or created heroes.
+
+This commit after deleting the hero will generate this output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// The SQL to update the hero in the database
+INFO Engine DELETE FROM hero WHERE hero.id = ?
+INFO Engine [generated in 0.00020s] (2,)
+INFO Engine COMMIT
+```
+
+</div>
+
+## Print the Deleted Object
+
+Now the hero is deleted from the database.
+
+If we tried to use `session.refresh()` with it, it would raise an exception, because there's no data in the database for this hero.
+
+Nevertheless, the object is still available with its data, but now it's not connected to the session and it no longer exists in the database.
+
+As the object is not connected to the session, it is not marked as "expired", the session doesn't even care much about this object anymore.
+
+Because of that, the object still contains its attributes with the data in it, so we can print it:
+
+```Python hl_lines="13"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-82]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+This will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// Print the deleted hero
+Deleted hero: name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
+```
+
+</div>
+
+## Query the Database for the Same Row
+
+To confirm if it was deleted, now let's query the database again, with the same `"Spider-Youngster"` name:
+
+```Python hl_lines="15-17"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-86]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+Here we are using `results.first()` to get the first object found (in case it found multiple) or `None`, if it didn't find anything.
+
+If we used `results.one()` instead, it would raise an exception, because it expects exactly one result.
+
+And because we just deleted that hero, this should not find anything and we should get `None`.
+
+This will execute some SQL in the database and output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// SQL to search for the hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00013s] ('Spider-Youngster',)
+```
+
+</div>
+
+## Confirm the Deletion
+
+Now let's just confirm that, indeed, no hero was found in the database with that name.
+
+We'll do it by checking that the "first" item in the `results` is `None`:
+
+```Python hl_lines="19-20"
+# Code above omitted π
+
+{!./docs_src/tutorial/delete/tutorial001.py[ln:72-89]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/delete/tutorial001.py!}
+```
+
+</details>
+
+This will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// Indeed, the hero was deleted π₯
+There's no hero named Spider-Youngster
+
+// Cleanup after the with block
+INFO Engine ROLLBACK
+```
+
+</div>
+
+## Review the Code
+
+Now let's review all that code:
+
+```{ .python .annotate hl_lines="72-90" }
+{!./docs_src/tutorial/delete/tutorial002.py!}
+```
+
+{!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+## Recap
+
+To delete rows with **SQLModel** you just have to `.delete()` them with the **session**, and then, as always, `.commit()` the session to save the changes to the database. π₯
--- /dev/null
+# Delete Data with FastAPI
+
+Let's now add a *path operation* to delete a hero.
+
+This is quite straightforward. π
+
+## Delete Path Operation
+
+Because we want to **delete** data, we use an HTTP `DELETE` operation.
+
+We get a `hero_id` from the path parameter and verify if it exists, just as we did when reading a single hero or when updating it, **possibly raising an error** with a `404` response.
+
+And if we actually find a hero, we just delete it with the **session**.
+
+```Python hl_lines="3-11"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/delete/tutorial001.py[ln:91-99]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/delete/tutorial001.py!}
+```
+
+</details>
+
+After deleting it successfully, we just return a response of:
+
+```JSON
+{
+ "ok": true
+}
+```
+
+## Recap
+
+That's it, feel free to try it out in the interactve docs UI to delete some heroes. π₯
+
+Using **FastAPI** to read data and combining it with **SQLModel** makes it quite straightforward to delete data from the database.
--- /dev/null
+# FastAPI and Pydantic - Intro
+
+One of the use cases where **SQLModel** shines the most, and the main one why it was built, was to be combined with **FastAPI**. β¨
+
+<a href="https://fastapi.tiangolo.com/" class="external-link" target="_blank">FastAPI</a> is a Python web framework for building web APIs created by the same <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">author</a> of SQLModel. FastAPI is also built on top of **Pydantic**.
+
+In this group of chapters we will see how to combine SQLModel **table models** representing tables in the SQL database as all the ones we have seen up to now, with **data models** that only represent data (which are actually just Pydantic models behind the scenes).
+
+Being able to combine SQLModel **table** models with pure **data** models would be useful on its own, but to make all the examples more concrete, we will use them with **FastAPI**.
+
+By the end we will have a **simple** but **complete** web **API** to interact with the data in the database. π
+
+## Learning FastAPI
+
+If you have never used FastAPI, maybe a good idea would be to go and study it a bit before continuing.
+
+Just reading and trying the examples on the <a href="https://fastapi.tiangolo.com/" class="external-link" target="_blank">FastAPI main page</a> should be enough, and it shouldn't take you more than **10 minutes**.
--- /dev/null
+# Read Heroes with Limit and Offset wtih FastAPI
+
+When a client sends a request to get all the heroes, we have been returning them all.
+
+But if we had **thousands** of heroes that could consume a lot of **computational resources**, network bandwith, etc.
+
+So we probably want to limit it.
+
+Let's use the same **offset** and **limit** we learned about in the previous tutorial chapters for the API.
+
+!!! info
+ In many cases this is also called **pagination**.
+
+## Add a Limit and Offset to the Query Parameters
+
+Let's add `limit` and `offset` to the query parameters.
+
+By default, we will return the first results from the database, so `offset` will have a default value of `0`.
+
+And by default, we will return a maximum of `100` heroes, so `limit` will have a default value of `100`.
+
+```Python hl_lines="3 9 11"
+{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py[ln:54-58]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py!}
+```
+
+</details>
+
+We want to allow clients to set a different `offset` and `limit` values.
+
+But we don't want them to be able to set a `limit` of something like `9999`, that's over `9000`! π±
+
+So, to prevent it, we add additional validation to the `limit` query parameter, declaring that it has to be **l**ess **t**han or **e**qual to `100` with `lte=100`.
+
+This way, a client can decide to take less heroes if they want, but not more.
+
+!!! info
+ If you need to refresh how query parameters and their validation work, check out the docs in FastAPI:
+
+ * <a href="https://fastapi.tiangolo.com/tutorial/query-params/" class="external-link" target="_blank">Query Parameters</a>
+ * <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/" class="external-link" target="_blank">Query Parameters and String Validations</a>
+ * <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/" class="external-link" target="_blank">Path Parameters and Numeric Validations</a>
+
+## Check the Docs UI
+
+Now we can see that the docs UI shows the new parameters to control **limit** and **offset** of our data.
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/limit-and-offset/image01.png">
+
+## Recap
+
+You can use **FastAPI**'s automatic data validation to get the parameters for `limit` and `offset`, and then use them with the **session** to control ranges of data to be sent in responses.
--- /dev/null
+# Multiple Models with FastAPI
+
+We have been using the same `Hero` model to declare the schema of the data we receive in the API, the table model in the database, and the schema of the data we send back in responses.
+
+But in most of the cases there are slight differences, let's use multiple models to solve it.
+
+Here you will see the main and biggest feature of **SQLModel**. π
+
+## Review Creation Schema
+
+Let's start by reviewing the automatically generated schemas from the docs UI.
+
+For input we have:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/simple-hero-api/image01.png">
+
+If we pay attention, it shows that the client *could* send an `id` in the JSON body of the request.
+
+This means that the client could try to use the same ID that already exists in the database for another hero.
+
+That's not what we want.
+
+We want the client to only send the data that is needed to create a new hero:
+
+* `name`
+* `secret_name`
+* Optional `age`
+
+And we want the `id` to be generated automatically by the database, so we don't want the client to send it.
+
+We'll see how to fix it in a bit.
+
+## Review Response Schema
+
+Now let's review the schema of the response we send back to the client in the docs UI.
+
+If you click the small tab <kbd>Schema</kbd> instead of the <kbd>Example Value</kbd>, you will see something like this:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/multiple-models/image01.png">
+
+Let's see the details.
+
+The fields with a red asterisk (<span style="color: #ff0000;">*</span>) are "required".
+
+This means that our API application is required to return those fields in the response:
+
+* `name`
+* `secret_name`
+
+The `age` is optional, we don't have to return it, or it could be `None` (or `null` in JSON), but the `name` and the `secret_name` are required.
+
+Here's the weird thing, the `id` currently seems also "optional". π€
+
+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.
+
+But in the responses, we would always send a model from the database, and it would **always have an ID**. So the `id` in the responses could be declared as required too.
+
+This would mean that our application is making the compromise with the clients that if it sends a hero, it would for sure have an `id` with a value, it would not be `None`.
+
+### Why Is it Important to Compromise with the Responses
+
+The ultimate goal of an API is for some **clients to use it**.
+
+The clients could be a frontend application, a command line program, a graphical user interface, a mobile application, another backend application, etc.
+
+And the code those clients write depend on what our API tells them they **need to send**, and what they can **expect to receive**.
+
+Making both sides very clear will make it much easier to interact with the API.
+
+And in most of the cases, the developer of the client for that API **will also be yourself**, so you are **doing your future self a favor** by declaring those schemas for requests and responses. π
+
+### So Why is it Important to Have Required IDs
+
+Now, what's the matter with having one **`id` field marked as "optional"** in a response when in reality it is always required?
+
+For example, **automatically generated clients** in other languages (or also in Python) would have some declaration that this field `id` is optional.
+
+And then the developers using those clients in their languages would have to be checking all the time in all their code if the `id` is not `None` before using it anywhere.
+
+That's a lot of unnecessary checks and **unnecessary code** that could have been saved by declaring the schema properly. π
+
+It would be a lot simpler for that code to know that the `id` from a response is required and **will always have a value**.
+
+Let's fix that too. π€
+
+## Multiple Hero Schemas
+
+So, we want to have our `Hero` model that declares the **data in the database**:
+
+* `id`, optional on creation, required on database
+* `name`, required
+* `secret_name`, required
+* `age`, optional
+
+But we also want to have a `HeroCreate` for the data we want to receive when **creating** a new hero, which is almost all the same data as `Hero`, except for the `id`, because that is created automatically by the database:
+
+* `name`, required
+* `secret_name`, required
+* `age`, optional
+
+And we want to have a `HeroRead` 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:
+
+* `id`, required
+* `name`, required
+* `secret_name`, required
+* `age`, optional
+
+## Multiple Models with Duplicated Fields
+
+The simplest way to solve it could be to create **multiple models**, each one with all the corresponding fields:
+
+```Python hl_lines="5-9 12-15 18-22"
+# This would work, but there's a better option below π¨
+
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py[ln:7-24]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py!}
+```
+
+</details>
+
+Here's the important detail, and probably the most important feature of **SQLModel**: only `Hero` is declared with `table = True`.
+
+This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.
+
+But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
+
+This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. π
+
+!!! tip
+ We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
+
+## Use Multiple Models to Create a Hero
+
+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:
+
+```Python hl_lines="3-4 6"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py[ln:46-53]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py!}
+```
+
+</details>
+
+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**.
+
+```Python hl_lines="3"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py[ln:47]!}
+
+# Code below omitted π
+```
+
+Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.from_orm()`.
+
+The method `.from_orm()` reads data from another object with attributes and creates a new instance of this class, in this case `Hero`.
+
+The alternative is `Hero.parse_obj()` that reads data from a dictionary.
+
+But as in this case we have a `HeroCreate` instance in the `hero` variable, this is an object with attributes, so we use `.from_orm()` to read those attributes.
+
+With this we 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.
+
+```Python hl_lines="3"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py[ln:49]!}
+
+# Code below omitted π
+```
+
+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 `HeroRead`:
+
+```Python hl_lines="3"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py[ln:46]!}
+
+# Code below omitted π
+```
+
+This will validate that all the data that we promised is there, and will remove any data we didn't declare.
+
+!!! tip
+ This filtering could be very important, and could be a very good security feature, for example to make sure you filter private data, hashed passwords, etc.
+
+ You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about Response Model</a>.
+
+In particular, it will make sure that the `id` is there, and that it is indeed an integer (and not `None`).
+
+## Shared Fields
+
+But looking closely, we could see that these models have a lot of **duplicated information**.
+
+All **the 3 models** declare that thay share some **common fields** that look exactly the same:
+
+* `name`, required
+* `secret_name`, required
+* `age`, optional
+
+And then they declare other fields with some differences (in this case only about the `id`).
+
+We want to **avoid duplicated information** if possible.
+
+This is important if, for example, in the future we decide to **refactor the code** and rename one field (column). For example, from `secret_name` to `secret_identity`.
+
+If we have that duplicated in multiple models, we could easily forget to update one of them. But if we **avoid duplication**, there's only one place that would need updating. β¨
+
+Let's now improve that. π€
+
+## Multiple Models with Inheritance
+
+And here it is, you found the biggest feature of **SQLModel**. π
+
+Each of these models is only a **data model** or both a data model and a **table model**.
+
+So, it's possible to create models with **SQLModel** that don't represent tables in the database.
+
+On top of that, we can use inheritance to avoid duplicated information in these models.
+
+We can see from above that they all share some **base** fields:
+
+* `name`, required
+* `secret_name`, required
+* `age`, optional
+
+So let's create a **base** model `HeroBase` that the others can inherit from:
+
+```Python hl_lines="3-6"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py[ln:7-10]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
+```
+
+</details>
+
+As you can see, this is *not* a **table model**, it doesn't have the `table = True` config.
+
+But now we can create the **other models inheriting from it**, they will all share these fields, just as if they had them declared.
+
+### The `Hero` **Table Model**
+
+Let's start with the only **table model**, the `Hero`:
+
+```Python hl_lines="9-10"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py[ln:7-14]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
+```
+
+</details>
+
+Notice that `Hero` now doesn't inherit from `SQLModel`, but from `HeroBase`.
+
+And now we only declare one single field directly, the `id`, that here is `Optional[int]`, and is a `primary_key`.
+
+And even though we don't declare the other fields **explicitly**, because they are inherited, they are also part of this `Hero` model.
+
+And of course, all these fields will be in the columns for the resulting `hero` table in the database.
+
+And those inherited fields will also be in the **autocompletion** and **inline errors** in editors, etc.
+
+### The `HeroCreate` **Data Model**
+
+Now let's see the `HeroCreate` model that will be used to define the data that we want to receive in the API when creating a new hero.
+
+This is a fun one:
+
+```Python hl_lines="13-14"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py[ln:7-18]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
+```
+
+</details>
+
+What's happening here?
+
+The fields we need to create are **exactly the same** as the ones in the `HeroBase` model. So we don't have to add anything.
+
+And because we can't leave the empty space when creating a new class, but we don't want to add any field, we just use `pass`.
+
+This means that there's nothing else special in this class apart from the fact that it is named `HeroCreate` and that it inherits from `HeroBase`.
+
+As an alternative, we could use `HeroBase` directly in the API code instead of `HeroCreate`, but it would show up in the auomatic docs UI with that name "`HeroBase`" which could be **confusing** for clients. Instead, "`HeroCreate`" is a bit more explicit about what it is for.
+
+On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example a password), and now we already have the class to put those extra fields.
+
+### The `HeroRead` **Data Model**
+
+Now let's check the `HeroRead` 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.
+
+```Python hl_lines="17-18"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py[ln:7-22]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
+```
+
+</details>
+
+## Review the Updated Docs UI
+
+The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now we define them in a smarter way with inheritance.
+
+So, we can jump to the docs UI right away and see how they look with the updated data.
+
+### Docs UI to Create a Hero
+
+Let's see the new UI for creating a hero:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/multiple-models/image02.png">
+
+Nice! It now shows that to create a hero, we just pass the `name`, `secret_name`, and optinally `age`.
+
+We no longer pass an `id`.
+
+### Docs UI with Hero Responses
+
+Now we can scroll down a bit to see the response schema:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/multiple-models/image03.png">
+
+We can now see that `id` is a required field, it has a red asterisk (<span style="color: #f00;">*</span>).
+
+And if we check the schema for the **Read Heroes** *path operation* it will also show the updated schema.
+
+## Inheritance and Table Models
+
+We just saw how powerful inheritance of these models can be.
+
+This is a very simple example, and it might look a bit... meh. π
+
+But now imagine that your table has **10 or 20 columns**. And that you have to duplicate all that information for all your **data models**... then it becomes more obvious why it's quite useful to be able to avoid all that information duplication with inheritance.
+
+Now, this probably looks so flexible that it's not obvious **when to use inheritance** and for what.
+
+Here are a couple of rules of thumb that can help you.
+
+### Only Inherit from Data Models
+
+Only inherit from **data models**, don't inherit from **table models**.
+
+It will help you avoid confusion, and there won't be any reason for you to need to inherit from a **table model**.
+
+If you feel like you need to inherit from a **table model**, then instead create a **base** class that is only a **data model** and has all those fields, like `HeroBase`.
+
+And then inherit from that **base** class that is only a **data model** for any other **data model** and for the **table model**.
+
+### Avoid Duplication - Keep it Simple
+
+It could feel like you need to have a profound reason why to inherit from one model or another, because "in some mystical way" they separate different concepts... or something like that.
+
+In some cases, there are **simple separations** that you can use, like the models to create data, read, update, etc. If that's quick and obvious, nice, use it. π―
+
+Otherwise, don't worry too much about profound conceptual reasons to separate models, just try to **avoid duplication** and **keep the code simple** enough to reason about it.
+
+If you see you have a lot of **overlap** between two models, then you can probably **avoid some of that duplication** with a base model.
+
+But if to avoid some duplication you end up with a crazy tree of models with inheritance, then it might be **simpler** to just duplicate some of those fields, and that might be easier to reason about and to maintain.
+
+Do whatever is easier to **reason** about, to **program** with, to **maintain**, and to **refactor** in the future. π€
+
+Remember that inheritance, the same as **SQLModel**, and anything else, are just tools to **help you be more productive**, that's one of their main objectives. If something is not helping with that (e.g. too much duplication, too much complexity), then change it. π
+
+## Recap
+
+You can use **SQLModel** to declare multiple models:
+
+* Some models can be only **data models**. They will also be **Pydantic** models.
+* And some can *also* be **table models** (apart from already being **data models**) by having the config `table = True`. They will also be **Pydantic** models and **SQLAlchemy** models.
+
+Only the **table models** will create tables in the database.
+
+So, you can use all the other **data models** to validate, convert, filter, and document the schema of the data for your application. β¨
+
+You can use inheritance to **avoid information and code duplication**. π
+
+And you can use all these models directly with **FastAPI**. π
--- /dev/null
+# Read One Model with FastAPI
+
+Let's now add a *path operation* to read a single model to our **FastAPI** application.
+
+## Path Operation for One Hero
+
+Let's add a new *path operation* to read one single hero.
+
+We want to get the hero based on the `id`, so we will use a **path parameter** `hero_id`.
+
+!!! info
+ If you need to refresh how *path parameters* work, including their data validation, check the <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">FastAPI docs about Path Parameters</a>.
+
+```Python hl_lines="8"
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
+```
+
+</details>
+
+For example, to get the hero with ID `2` we would send a `GET` request to:
+
+```
+/heroes/2
+```
+
+## Handling Errors
+
+Then, because FastAPI already takes care of making sure that the `hero_id` is an actual integer, we can use it directly with `Hero.get()` to try and get one hero by that ID.
+
+But if the integer is not the ID of any hero in the database, it will not find anything, and the variable `hero` will be `None`.
+
+So, we check it in an `if` block, if it's `None`, we raise an `HTTPException` with a `404` status code.
+
+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.
+
+```Python hl_lines="3 11-13"
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
+```
+
+</details>
+
+## Return the Hero
+
+Then, if the hero exists, we return it.
+
+And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
+
+```Python hl_lines="8 14"
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
+```
+
+</details>
+
+## Check the Docs UI
+
+We can then go to the docs UI and see the new *path operation*.
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/read-one/image01.png">
+
+## Recap
+
+You can combine **FastAPI** features like automatic path parameter validation to get models by ID.
--- /dev/null
+# Models with Relationships in FastAPI
+
+If we go right now and read a single **hero** by ID, we get the hero data with the team ID.
+
+But we don't get any data about the particular team:
+
+<img class="shadow" alt="Interactive API docs UI getting a single hero" src="/img/tutorial/fastapi/relationships/image01.png">
+
+We get a response of:
+
+```JSON hl_lines="5"
+{
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": null,
+ "team_id": 1,
+ "id": 1,
+}
+```
+
+And the same way, if we get a **team** by ID, we get the team data, but we don't get any information about this team's heroes:
+
+<img class="shadow" alt="Interactive API docs UI getting a single team" src="/img/tutorial/fastapi/relationships/image02.png">
+
+Here we get a response of:
+
+```JSON
+{
+ "name": "Preventers",
+ "headquarters": "Sharp Tower",
+ "id": 2
+}
+```
+
+...but no information about the heroes.
+
+Let's update that. π€
+
+## Why Aren't We Getting More Data
+
+First, why is it that we are not getting the related data for each hero and for each team?
+
+It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
+
+And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
+
+```Python hl_lines="3-5 9-10 14-19 23-24"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:7-9]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:22-23]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:32-37]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:46-47]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+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=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
+
+```Python hl_lines="3 8 12 17"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:105-110]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:160-165]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+## Don't Include All the Data
+
+Now let's stop for a second and think about it.
+
+We cannot simply include *all* the data including all the internal relationships, because each **hero** has an attribute `team` with their team, and then that **team** also has an attribute `heroes` with all the **heroes** in the team, including this one.
+
+If we tried to include everything, we could make the server application **crash** trying to extract **infinite data**, going through the same hero and team over and over again internally, something like this:
+
+```JSON hl_lines="2 13 24 34"
+{
+ "name": "Rusty-Man",
+ "secret_name": "Tommy Sharp",
+ "age": 48,
+ "team_id": 1,
+ "id": 1,
+ "team": {
+ "name": "Preventers",
+ "headquarters": "Sharp Tower",
+ "id": 2,
+ "heroes": [
+ {
+ "name": "Rusty-Man",
+ "secret_name": "Tommy Sharp",
+ "age": 48,
+ "team_id": 1,
+ "id": 1,
+ "team": {
+ "name": "Preventers",
+ "headquarters": "Sharp Tower",
+ "id": 2,
+ "heroes": [
+ {
+ "name": "Rusty-Man",
+ "secret_name": "Tommy Sharp",
+ "age": 48,
+ "team_id": 1,
+ "id": 1,
+ "team": {
+ "name": "Preventers",
+ "headquarters": "Sharp Tower",
+ "id": 2,
+ "heroes": [
+ ...with infinite data here... π±
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+As you can see, in this example we would get the hero **Rusty-Man**, and from this hero we would get the team **Preventers**, and then from this team we would get its heroes, of course, including **Rusty-Man**... π±
+
+So we start again, and in the end, the server would just crash trying to get all the data with a `"Maximum recursion error"`, we would not even get a response like the one above.
+
+So, we need to carefully choose in which cases we want to include data and in which not.
+
+## What Data to Include
+
+This is a decision that will depend on **each application**.
+
+In our case, let's say that if we get a **list of heroes**, we don't want to also include each of their teams in each one.
+
+And if we get a **list of teams**, we don't want to get a a list of the heroes for each one.
+
+But if we get a **single hero**, we want to include the team data (without the team's heroes).
+
+And if we get a **single team**, we want to include the list of heroes (without each hero's team).
+
+Let's add a couple more **data models** that declare that data so we can use them in those two specific *path operations*.
+
+## Models with Relationships
+
+Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
+
+We'll add them **after** the other models so that we can easily reference the previous models.
+
+```Python hl_lines="3-4 7-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:61-66]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/relationships/tutorial001.py!}
+```
+
+</details>
+
+These two models are very **simple in code**, but there's a lot happening here, let's check it out.
+
+### Inheritance and Type Annotations
+
+The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
+
+And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
+
+Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declare the **new field** `heroes` which is a list of `HeroRead`.
+
+### Data Models Without Relationship Attributes
+
+Now, notice that these new fields `team` and `heroes` are not declared with `Relationship()`, because these are not **table models**, they cannot have **relationship attributes** with the magic access to get that data from the database.
+
+Instead, here these are only **data models** that will tell FastAPI **which attributes** to get data from and **which data** to get from them.
+
+### Reference to Other Models
+
+Also notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
+
+And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
+
+This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
+
+## Update the Path Operations
+
+Now we can update the *path operations* to use the new models.
+
+This will tell **FastAPI** to take the object that we return from the *path operation function* (a **table model**) and **access the additional attributes** from them to extract their data.
+
+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.
+
+```Python hl_lines="3 8 12 17"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:113-118]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:168-173]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/relationships/tutorial001.py!}
+```
+
+</details>
+
+## Check It Out in the Docs UI
+
+Now let's try it out again in the **docs UI**.
+
+Let's try again with the same **hero** with ID `1`:
+
+<img class="shadow" alt="Interactive API docs UI getting a single hero with team" src="/img/tutorial/fastapi/relationships/image03.png">
+
+Now we get the **team** data included:
+
+```JSON hl_lines="7-11"
+{
+ "name": "Deadpond",
+ "secret_name": "Dive Wilson",
+ "age": null,
+ "team_id": 1,
+ "id": 1,
+ "team": {
+ "name": "Z-Force",
+ "headquarters": "Sister Margaretβs Bar",
+ "id": 1
+ }
+}
+```
+
+And if we get now the **team** with ID `2`:
+
+<img class="shadow" alt="Interactive API docs UI getting a single team with the list of heroes" src="/img/tutorial/fastapi/relationships/image04.png">
+
+Now we get the list of **heroes** included:
+
+```JSON hl_lines="5-41"
+{
+ "name": "Preventers",
+ "headquarters": "Sharp Tower",
+ "id": 2,
+ "heroes": [
+ {
+ "name": "Rusty-Man",
+ "secret_name": "Tommy Sharp",
+ "age": 48,
+ "team_id": 2,
+ "id": 2
+ },
+ {
+ "name": "Spider-Boy",
+ "secret_name": "Pedro Parqueador",
+ "age": null,
+ "team_id": 2,
+ "id": 3
+ },
+ {
+ "name": "Tarantula",
+ "secret_name": "Natalia Roman-on",
+ "age": 32,
+ "team_id": 2,
+ "id": 6
+ },
+ {
+ "name": "Dr. Weird",
+ "secret_name": "Steve Weird",
+ "age": 36,
+ "team_id": 2,
+ "id": 7
+ },
+ {
+ "name": "Captain North America",
+ "secret_name": "Esteban Rogelios",
+ "age": 93,
+ "team_id": 2,
+ "id": 8
+ }
+ ]
+}
+```
+
+## Recap
+
+Using the same techniques to declare additonal **data models** we can tell FastAPI what data to return in the responses, even when we return **table models**.
+
+Here we almost **didn't have to change the FastAPI app** code, but of course, there will be cases where you need to get the data and process it in different ways in the *path operation function* before returning it.
+
+But even in those cases, you will be able to define the **data models** to use in `response_model` to tell FastAPI how to validate and filter the data.
+
+By this point, you already have a very robust API to handle data in a SQL database combining **SQLModel** with **FastAPI**, and implementing **best practices**, like data validation, conversion, filtering, and documentation. β¨
+
+In the next chapter I'll tell you how to implement automated **testing** for your application using FastAPI and SQLModel. β
--- /dev/null
+# FastAPI Response Model with SQLModel
+
+Now I'll show you how to use FastAPI's `response_model` with **SQLModel**.
+
+## Interactive API Docs
+
+Up to now, with the code we have used, the API docs know the data the clients have to send:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/simple-hero-api/image01.png">
+
+This interactive docs UI is powered by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>, and what Swagger UI does is to read a big JSON content that defines the API with all the data schemas (data shapes) using the standard <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md" class="external-link" target="_blank">OpenAPI</a>, and showing it in that nice <abbr title="User Interface">UI</abbr>.
+
+FastAPI automatically **generates that OpenAPI** for Swagger UI to read it.
+
+And it generates it **based on the code you write**, using the Pydantic models (in this case **SQLModel** models) and type annotations to know the schemas of the data that the API handles.
+
+## Response Data
+
+But up to now, the API docs UI doesn't know the schema of the *responses* our app sends back.
+
+You can see that there's a possible "Successful Response" with a code `200`, but we have no idea how the response data would look like.
+
+<img class="shadow" alt="API docs UI without response data schemas" src="/img/tutorial/fastapi/response-model/image01.png">
+
+Right now we only tell FastAPI the data we want to receive, but we don't tell it yet the data we want to send back.
+
+Let's do that now. π€
+
+## Use `response_model`
+
+We can use `response_model` to tell FastAPI the schema of the data we want to send back.
+
+For example, we can pass the same `Hero` **SQLModel** class (because it is also a Pydantic model):
+
+```Python hl_lines="3"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/response_model/tutorial001.py[ln:33-39]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/response_model/tutorial001.py!}
+```
+
+</details>
+
+## 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.
+
+First, we import `List` from `typing` and then we declare the `response_model` with `List[Hero]`:
+
+```Python hl_lines="1 5"
+{!./docs_src/tutorial/fastapi/response_model/tutorial001.py[ln:1]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/response_model/tutorial001.py[ln:42-46]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/response_model/tutorial001.py!}
+```
+
+</details>
+
+## FastAPI and Response Model
+
+FastAPI will do data validation and filtering of the response with this `response_model`.
+
+So this works like a contract between our application and the client.
+
+You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about `response_model`</a>.
+
+## New API Docs UI
+
+Now we can go back to the docs UI and see that they now show the schema of the response we will receive.
+
+<img class="shadow" alt="API docs UI without response data schemas" src="/img/tutorial/fastapi/response-model/image02.png">
+
+The clients will know what data they should expect.
+
+## Automatic Clients
+
+The most visible advantage of using the `response_model` is that it shows up in the API docs UI.
+
+But there are other advantages, like that FastAPI will do automatic <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">data validation and filtering</a> of the response data using this model.
+
+Additionally, because the schemas are defined in using a standard, there are many tools that can take advantage of this.
+
+For example, client generators, that can automatically create the code necessary to talk to your API in many languages.
+
+!!! info
+ If you are curious about the standards, FastAPI generates OpenAPI, that internally uses JSON Schema.
+
+ You can read about all that in the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/#openapi" class="external-link" target="_blank">FastAPI docs - First Steps</a>.
+
+## Recap
+
+Use the `response_model` to tell FastAPI the schema of the data you want to send back and have awesome data APIs. π
--- /dev/null
+# Session with FastAPI Dependency
+
+Before we keep adding things, let's change a bit how we get the session for each request to simplify our life later.
+
+## Current Sessions
+
+Up to now, we have been creating a session in each *path operation*, in a `with` block.
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/delete/tutorial001.py[ln:50-57]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/delete/tutorial001.py!}
+```
+
+</details>
+
+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*.
+
+These dependencies are also very useful during **testing**, because we can **easily replace them**, and then, for example, use a new database for our tests, or put some data before the tests, etc.
+
+So, let's refactor these sessions to use **FastAPI Dependencies**.
+
+## Create a **FastAPI** Dependency
+
+A **FastAPI** dependency is very simple, it's just a function that returns a value.
+
+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.
+
+```Python hl_lines="3-5"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:42-44]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py!}
+```
+
+</details>
+
+## 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.
+
+```Python hl_lines="3 15"
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:42-44]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-61]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py!}
+```
+
+</details>
+
+!!! tip
+ Here's a tip about that `*,` thing in the parameters.
+
+ Here we are passing the parameter `session` that has a "default value" of `Depends(get_session)` before the parameter `hero`, that doesn't have any default value.
+
+ Python would normally complain about that, but we can use the initial "parameter" `*,` to mark all the rest of the parameters as "keyword only", which solves the problem.
+
+ You can read more about it in the FastAPI documentation <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#order-the-parameters-as-you-need-tricks" class="external-link" target="_blank">Path Parameters and Numeric Validations - Order the parameters as you need, tricks</a>
+
+The value of a dependency will **only be used for one request**, FastAPI will call it right before calling your code, and will give you the value from that dependency.
+
+If it had `yield`, then it will continue the rest of the execution once you are done sending the response. In the case of the **session**, it will finish the cleanup code from the `with` block, closing the session, etc.
+
+Then FastAPI will call it again for the **next request**.
+
+Because it is called **once per request**, we will still get a **single session per request** as we should, so we are still fine with that. β
+
+And because dependencies can use `yield`, FastAPI will make sure to run the code **after** the `yield` once it is done, including all the **cleanup code** at the end of the `with` block. So we are also fine with that. β
+
+## The `with` Block
+
+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.
+
+```Python hl_lines="16-20"
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:42-44]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-61]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py!}
+```
+
+</details>
+
+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:
+
+```Python hl_lines="9-10"
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:42-44]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-61]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py!}
+```
+
+</details>
+
+We will see how this is very useful when testing the code later. β
+
+## Update the Path Operations to Use the Dependency
+
+Now we can update the rest of the *path operations* to use the new dependency.
+
+We just declare the dependency in the parameters of the function, with:
+
+```Python
+session: Session = Depends(get_session)
+```
+
+And then we remove the previous `with` block with the old **session**.
+
+```Python hl_lines="15 26 35 44 59"
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:1-4]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:42-44]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-107]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py!}
+```
+
+</details>
+
+## Recap
+
+You just learned how to use **FastAPI dependencies** to handle the database session. This will come in handy later when testing the code.
+
+And you will see how much these dependencies can help the more you work with FastAPI, to handle **permissions**, **authentication**, resources like database **sessions**, etc. π
+
+If you want to learn more about dependencies, checkout the <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI docs about Dependencies</a>.
--- /dev/null
+# Simple Hero API with FastAPI
+
+Let's start by building a simple hero web API with **FastAPI**. β¨
+
+## Install **FastAPI**
+
+The first step is to install FastAPI.
+
+FastAPI is the framework to create the **web API**.
+
+But we also need another type of program to run it, it is called a "**server**". We will use **Uvicorn** for that. And we will install Uvicorn with its *standard* dependencies.
+
+Make sure you [have a virtual environment activated](../index.md#create-a-python-virtual-environment){.internal-link target=_blank}.
+
+Then install FastAPI and Uvicorn:
+
+<div class="termy">
+
+```console
+$ python -m pip install fastapi "uvicorn[standard]"
+
+---> 100%
+```
+
+</div>
+s
+## **SQLModel** Code - Models, Engine
+
+Now let's start with the SQLModel code.
+
+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:
+
+```Python hl_lines="20-21"
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:1]!}
+
+# One line of FastAPI imports here later π
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:4]!}
+
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:7-22]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py!}
+```
+
+</details>
+
+There's only one change here from the code we have used before, the `check_same_thread` in the `connect_args`.
+
+That is a configuration that SQLAlchemy passes to the low-level library in charge of communicating with the database.
+
+`check_same_thread` is by default set to `True`, to prevent misuses in some simple cases.
+
+But here we will make sure we don't share the same **session** in more than one request, and that's the actual **safest way** to prevent any of the problems that configuration is there for.
+
+And we also need to disable it because in **FastAPI** each request could be handled by multiple interacting threads.
+
+!!! info
+ That's enough information for now, you can read more about it in the <a href="https://fastapi.tiangolo.com/async/" class="external-link" target="_blank">FastAPI docs for `async` and `await`</a>.
+
+ The main point is, by ensuring you **don't share** the same **session** with more than one request, the code is already safe.
+
+## **FastAPI** App
+
+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:
+
+```Python hl_lines="3 8"
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:1-4]!}
+
+# SQLModel code here omitted π
+
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:25]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py!}
+```
+
+</details>
+
+## Create Database and Tables on `startup`
+
+We want to make sure that once the app starts running, the function `create_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:
+
+```Python hl_lines="6-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:25-30]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py!}
+```
+
+</details>
+
+## Create Heroes *Path Operation*
+
+!!! info
+ If you need a refresher on what a **Path Operation** is (an endpoint with a specific HTTP Operation) and how to work with it in FastAPI, check out the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">FastAPI First Steps docs</a>.
+
+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**:
+
+```Python hl_lines="11-12"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:25-39]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py!}
+```
+
+</details>
+
+!!! info
+ If you need a refresher on some of those concepts, checkout the FastAPI documentation:
+
+ * <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">First Steps</a>
+ * <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">Path Parameters - Data Validation and Data Conversion</a>
+ * <a href="https://fastapi.tiangolo.com/tutorial/body/" class="external-link" target="_blank">Request Body</a>
+
+## The **SQLModel** Advantage
+
+Here's where having our **SQLModel** class models be both **SQLAlchemy** models and **Pydantic** models at the same tieme shine. β¨
+
+Here we use the **same** class model to define the **request body** that will be received by our API.
+
+Because **FastAPI** is based on Pydantic, it will use the same model (the Pydantic part) to do automatic data validation and <abbr title="also called serialization, marshalling">conversion</abbr> from the JSON request to an object that is an actual instance of the `Hero` class.
+
+And then because this same **SQLModel** object is not only a **Pydantic** model instance but also a **SQLAlchemy** model instance, we can use it directly in a **session** to create the row in the database.
+
+So we can use intuitive standard Python **type annotations**, and we don't have to duplicate a lot of the code for the database models and the API data models. π
+
+!!! tip
+ We will improve this further later, but for now, it already shows the power of having **SQLModel** classes be both **SQLAlchemy** models and **Pydantic** models at the same time.
+
+## Read Heroes *Path Operation*
+
+Now let's add another **path operation** to read all the heroes:
+
+```Python hl_lines="20-24"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py[ln:25-46]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py!}
+```
+
+</details>
+
+This is pretty straightforward.
+
+When a client sends a request to the **path** `/heroes/` with a `GET` HTTP **operation**, we run this function that gets the heroes from the database and returns them.
+
+## One Session per Request
+
+Remember that we shoud use a SQLModel **session** per each group of operations and if we need other unrelated operations we should use a different session?
+
+Here it is much more obvious.
+
+We should normally have **one session per request** in most of the cases.
+
+In some isolated cases we would want to have new sessions inside, so, **more than one session** per request.
+
+But we would **never want to *share* the same session** among different requests.
+
+In this simple example, we just create the new sessions manually in the **path operation functions**.
+
+In future examples later we will use a <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI Dependency</a> to get the **session**, being able to share it with other dependencies and being able to replace it during testing. π€
+
+## Run the **FastAPI** Application
+
+Now we are ready to run the FastAPI application.
+
+Put all that code in a file called `main.py`.
+
+Then run it with **Uvicorn**:
+
+<div class="termy">
+
+```console
+$ uvicorn main:app
+
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+<span style="color: green;">INFO</span>: Started reloader process [28720]
+<span style="color: green;">INFO</span>: Started server process [28722]
+<span style="color: green;">INFO</span>: Waiting for application startup.
+<span style="color: green;">INFO</span>: Application startup complete.
+```
+
+</div>
+
+!!! info
+ The command `uvicorn main:app` refers to:
+
+ * `main`: the file `main.py` (the Python "module").
+ * `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
+
+### Uvicorn `--reload`
+
+During development (and only during development), you can also add the option `--reload` to Uvicorn.
+
+It will restart the server every time you make a change to the code, this way you will be able to develop faster. π€
+
+<div class="termy">
+
+```console
+$ uvicorn main:app --reload
+
+<span style="color: green;">INFO</span>: Will watch for changes in these directories: ['/home/user/code/sqlmodel-tutorial']
+<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
+<span style="color: green;">INFO</span>: Started reloader process [28720]
+<span style="color: green;">INFO</span>: Started server process [28722]
+<span style="color: green;">INFO</span>: Waiting for application startup.
+<span style="color: green;">INFO</span>: Application startup complete.
+```
+
+</div>
+
+Just remember to never use `--reload` in production, as it consumes much more resources than necessary, would be more error prone, etc.
+
+## Check the API docs UI
+
+Now you can go to that URL in your browser `http://127.0.0.1:8000`. We didn't create a *path operation* for the root path `/`, so that URL alone will only show a "Not Found" error... that "Not Found" error is produced by your FastAPI application.
+
+But you can go to the **automatically generated interactive API documentation** at the path `/docs`: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. β¨
+
+You will see that this **automatic API docs <abbr title="user interface">UI</abbr>** has the *paths* that we defined above with their *operations*, and that it already knows the shape of the data that the **path operations** will receive:
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/simple-hero-api/image01.png">
+
+## Play with the API
+
+You can actually click the button <kbd>Try it out</kbd> and send some requests to create some heroes with the **Create Hero** *path operation*.
+
+And then you can get them back with the **Read Heroes** *path operation*:
+
+<img class="shadow" alt="Interactive API docs UI reading heroes" src="/img/tutorial/fastapi/simple-hero-api/image02.png">
+
+## Check the Database
+
+Now you can terminate that Uvicorn server by going back to the terminal and pressing <kbd>Ctrl+C</kbd>.
+
+And then you can open **DB Browser for SQLite** and check the database, to explore the data and confirm that it indeed saved the heroes. π
+
+<img class="shadow" alt="DB Browser for SQLite showing the heroes" src="/img/tutorial/fastapi/simple-hero-api/db-browser-01.png">
+
+## Recap
+
+Good job! This is already a FastAPI **web API** application to interact with the heroes database. π
+
+There are several things we can improve and extend. For example, we want the database to decide the ID of each new hero, we don't want to allow a user to send it.
+
+We will do all those improvements in the next chapters. π
--- /dev/null
+# FastAPI Path Opeartions for Teams - Other Models
+
+Let's now update the **FastAPI** application to handle data for teams.
+
+This is very similar to the things we have done for heroes, so we will go over it quickly here.
+
+We will use the same models we used in previous examples, with the **relationship attributes**, etc.
+
+## Add Teams Models
+
+Let's add the models for the teams.
+
+It's the same process we did for heroes, with a base model, a **table model**, and some other **data models**.
+
+We have a `TeamBase` **data model**, and from it we inherit with a `Team` **table model**.
+
+Then we also inherit from the `TeamBase` for the `TeamCreate` and `TeamRead` **data models**.
+
+And we also create a `TeamUpdate` **data model**.
+
+```Python hl_lines="7-9 12-15 18-19 22-23 26-29"
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:1-29]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+We now also have **relationship attributes**. π
+
+Let's now update the `Hero` models too.
+
+## Update Hero Models
+
+```Python hl_lines="3-8 11-15 17-18 21-22 25-29"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:32-58]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+We now have a `team_id` in the hero models.
+
+Notice that we can declare the `team_id` in the `HeroBase` because it can be reused by all the models, in all the cases it's an optional integer.
+
+And even though the `HeroBase` is *not* a **table model**, we can declare `team_id` in it with the `foreign key` parameter. It won't do anything in most of the models that inherit from `HeroBase`, but in the **table model** `Hero` it will be used to tell **SQLModel** that this is a **foreign key** to that table.
+
+## Relationship Attributes
+
+Notice that the **relationship attributes**, the ones with `Relationship()`, are **only** in the **table models**, as those are the ones that are handled by **SQLModel** with SQLAlchemy and that can have the automatic fetching of data from the database when we access them.
+
+```Python hl_lines="11 39"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:7-58]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+## Path Operations for Teams
+
+Let's now add the **path operations** for teams.
+
+These are equivalent and very similar to the **path operations** for the **heroes** we had before, so we don't have to go over the details for each one, let's check the code.
+
+```Python hl_lines="3-9 12-20 23-28 31-47 50-57"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:140-194]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
+```
+
+</details>
+
+## Using Relationships Attributes
+
+Up to this point we are actually not using the **relationship attributes**, but we could access them in our code.
+
+In the next chapter we will play more with them.
+
+## Check the Docs UI
+
+Now we can check the automatic docs UI to see all the **path operations** for heroes and teams.
+
+<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/teams/image01.png">
+
+## Recap
+
+We can use the same patterns to add more models and API **path operations** to our **FastAPI** application. π
--- /dev/null
+# Test Applications with FastAPI and SQLModel
+
+To finish this group of chapters about **FastAPI** with **SQLModel**, let's now learn how to implement automated tests for an application using FastAPI with SQLModel. β
+
+Including the tips and tricks. π
+
+## FastAPI Application
+
+Let's work with one of the **simpler** FastAPI applications we built in the previous chapters.
+
+All the same **concepts**, **tips** and **tricks** will apply to more complex applications as well.
+
+We will use the application with the hero models, but without team models, and we will use the dependency to get a **session**.
+
+Now we will see how useful it is to have this session dependency. β¨
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/main.py!}
+```
+
+</details>
+
+## File Structure
+
+Now we will have a Python project with multiple files, one file `main.py` with all the application, and one file `test_main.py` with the tests, with the same ideas from [Code Structure and Multiple Files](../code-structure.md){.internal-link target=_blank}.
+
+The file structure is:
+
+```
+.
+βββ project
+ βββ __init__.py
+ βββ main.py
+ βββ test_main.py
+```
+
+## Testing FastAPI Applications
+
+If you haven't done testing in FastAPI applications, first check the <a href="https://fastapi.tiangolo.com/tutorial/testing/" class="external-link" target="_blank">FastAPI docs about Testing</a>.
+
+Then, we can continue here, the first step is to install the dependencies, `requests` and `pytest`.
+
+Make sure you do it in the same [Python environment](../index.md#create-a-python-virtual-environment){.internal-link target=_blank}.
+
+<div class="termy">
+
+```console
+$ python -m pip install requests pytest
+
+---> 100%
+```
+
+</div>
+
+## Basic Tests Code
+
+Let's start with a simple test, with just the basic test code we need the check that the **FastAPI** application is creating a new hero correctly.
+
+```{ .python .annotate }
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_001.py[ln:1-7]!}
+ # Some code here omitted, we will see it later π
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_001.py[ln:20-24]!}
+ # Some code here omitted, we will see it later π
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_001.py[ln:26-32]!}
+
+# Code below omitted π
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_001.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+That's the **core** of the code we need for all the tests later.
+
+But now we need to deal with a bit of logistics and details we are not paying attention to just yet. π€
+
+## Testing Database
+
+This test looks fine, but there's a problem.
+
+If we run it, it will use the same **production database** that we are using to store our very important **heroes**, and we will end up adding adding unnecesary data to it, or even worse, in future tests we could end up removing production data.
+
+So, we should use an independent **testing database**, just for the tests.
+
+To do this, we need to change the URL used for the database.
+
+But when the code for the API is executed, it gets a **session** that is already connected to an **engine**, and the **engine** is already using a specific database URL.
+
+Even if we import the variable from the `main` module and change its value just for the tests, by that point the **engine** is already created with the original value.
+
+But all our API *path operations* get the *session* using a FastAPI **dependency**, and we can override dependencies in tests.
+
+Here's where dependencies start to help a lot.
+
+## Override a Dependency
+
+Let's override the `get_session()` dependency for the tests.
+
+This dependency is used by all the *path operations* to get the **SQLModel** session object.
+
+We will override it to use a different **session** object just for the tests.
+
+That way we protect the production database and we have better control of the data we are testing.
+
+```{ .python .annotate hl_lines="4 9-10 12 19" }
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_002.py[ln:1-7]!}
+ # Some code here omitted, we will see it later π
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_002.py[ln:15-32]!}
+
+# Code below omitted π
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_002.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+## Create the Engine and Session for Testing
+
+Now let's create that **session** object that will be used during testing.
+
+It will use its own **engine**, and this new engine will use a new URL for the testing database:
+
+```
+sqlite:///testing.db
+```
+
+So, the testing database will be in the file `testing.db`.
+
+``` { .python .annotate hl_lines="4 8-11 13 16 33"}
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_003.py!}
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_003.md!}
+
+### Import Table Models
+
+Here we create all the tables in the testing database with:
+
+```Python
+SQLModel.metadata.create_all(engine)
+```
+
+But remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`.
+
+In this case, it all works for a little subtlety that deserves some attention.
+
+Because we import something, *anything*, from `.main`, the code in `.main` will be executed, including the definition of the **table models**, and that will automatically register them in `SQLModel.metadata`.
+
+That way, when we call `.create_all()` all the **table models** are correctly registered in `SQLModel.metadata` and it will all work. π
+
+## Memory Database
+
+Now we are not using the production database, instead we use a **new testing database** with the `testing.db` file, which is great.
+
+But SQLite also supports having an **in memory** database. This means that all the database is only in memory, and it is never saved in a file on disk.
+
+After the program terminates, **the in-memory database is deleted**, so it wouldn't help much for a production database.
+
+But **it works great for testing**, because it can be quickly created before each test, and quickly removed after each test. β
+
+And also, because it never has to write anything to a file and it's all just in memory, it will be even faster than normally. π
+
+<details>
+<summary>
+Other alternatives and ideas π
+</summary>
+Before arriving at the idea of using an **in-memory database** we could have explored other alternatives and ideas.
+
+The first, is that we are not deleting the file after we finish the test, so, the next test could have **leftover data**. So, the right thing would be to delete the file right after finishing the test. π₯
+
+But if each test has to create a new file and then delete it afterwards, running all the tests could be **a bit slow**.
+
+Right now, we have a file `testing.db` that is used by all the tests (we only have one test now, but we will have more).
+
+So, if we tried to run the tests at the same time **in parallel** to try to speed things up a bit, they would clash trying to use the *same* `testing.db` file.
+
+Of couse, we could also fix that, using some **random name** for each testing database file... but in the case of SQLite, we have an even better alternative with just using an **in-memory database**. β¨
+
+</details>
+
+## Configure the In-Memory Database
+
+Let's update our code to use the in-memory database.
+
+We just have to change a couple of parameters in the **engine**.
+
+```{ .python .annotate hl_lines="3 9-13"}
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_004.py[ln:1-13]!}
+
+# Code below omitted π
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_004.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+That's it, now the test will run using the **in-memory database**, which will be faster and probably safer.
+
+And all the other tests can do the same.
+
+## Boilerplate Code
+
+Great, that works, and you could replicate all that process in each of the test functions.
+
+But we had to add a lot of **boilerplate code** to handle the custom database, creating it in memory, the custom session, the dependency override.
+
+Do we really have to duplicate all that for **each test**? No, we can do better! π
+
+We are using **pytest** to run the tests. And pytest also has a very similar concept to the **dependencies in FastAPI**.
+
+!!! info
+ In fact, pytest was one of the things that inspired the design of the dependencies in FastAPI.
+
+It's a way for us to declare some **code that should be run before** each test and **provide a value** for the test function (that's pretty much the same as FastAPI dependencies).
+
+In fact, it also has the same trick of allowing to use `yield` instead of `return` to provide the value, and then **pytest** makes sure that the code after `yield` is executed *after* the function with the test is done.
+
+In pytest, these things are called **fixtures** instead of *dependencies*.
+
+Let's use these **fixtures** to improve our code and reduce de duplicated boilerplate for the next tests.
+
+## Pytest Fixtures
+
+You can read more about them in the <a href="https://docs.pytest.org/en/6.2.x/fixture.html" class="external-link" target="_blank">pytest docs for fixtures</a>, but I'll give you a short example for what we need here.
+
+Let's see the first code example with a fixture:
+
+``` { .python .annotate }
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_005.py!}
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_005.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+**pytest** fixtures work in a very similar way to FastAPI dependencies, but have some minor differences:
+
+* In pytest fixtures we need to add a decorator of `@pytest.fixture()` on top.
+* To use a pytest fixture in a function, we have to declare the parameter with the **exact same name**. In FastAPI we have to **explicitly use `Depends()`** with the actual function inside it.
+
+But apart from the way we declare them and how we tell the framework that we want to have them in the function, they **work in a very similar way**.
+
+Now we create lot's of tests, and re-use that same fixture in all of them, saving us that **boilerplate code**.
+
+**pytest** will make sure to run them right before (and finish them right after) each test function. So, each test function will actually have its own database, engine, and session.
+
+## Client Fixture
+
+Awesome, that fixture helps us prevent a lot of duplicated code.
+
+But currently we still have to write some code in the test function that will be repetitive for other tests, right now we:
+
+* create the **dependency override**
+* put it in the `app.dependency_overrides`
+* create the `TestClient`
+* Clear the dependency override(s) after making the request
+
+That's still gonna be repetitive in the other future tests. Can we improve it? Yes! π
+
+Each **pytest** fixture (the same way as **FastAPI** dependencies), can require other fixtures.
+
+So, we can create a **client fixture** that will be used in all the tests, and it will itself require the **session fixture**.
+
+``` { .python .annotate hl_lines="19-28 31" }
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main_006.py!}
+```
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_006.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+Now we have a **client fixture** that in turns uses the **session fixture**.
+
+And in the actual test function, we just have to declare that we require this **client fixture**.
+
+## Add More Tests
+
+At this point, it all might seem like we just did a lot of changes for nothing, to get **the same result**. π€
+
+But normally we will create **lots of other test functions**. And now all the boilerplate and complexity is **writen only once**, in those two fixtures.
+
+Let's add some more tests:
+
+```Python hl_lines="3 22"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py[ln:30-58]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py!}
+```
+
+</details>
+
+!!! tip
+ It's always **good idea** to not only test the normal case, but also that **invalid data**, **errors**, and **corner cases** are handled correctly.
+
+ That's why we add these two extra tests here.
+
+Now, any additional test functions can be as **simple** as the first one, they just have to **declate the `client` parameter** to get the `TestClient` **fixture** with all the database stuff setup. Nice! π
+
+## Why Two Fixtures
+
+Now, seeing the code we could think, why do we put **two fixtures** instead of **just one** with all the code? And that makes total sense!
+
+For these examples, **that would have been simpler**, there's no need to separate that code in two fixtures for them...
+
+But for the next test function, we will require **both fixtures**, the **client** and the **session**.
+
+```Python hl_lines="6 10"
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py[ln:1-6]!}
+
+# Code here omitted π
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py[ln:61-81]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py!}
+```
+
+</details>
+
+In this test function we want to check that the *path operation* to **read a list of heroes** actually sends us heroes.
+
+But if the **database is empty**, we would get an **empty list**, and we wouldn't know if the hero data is being sent correctly or not.
+
+But we can **create some heroes** in the testing database right before sending the API request. β¨
+
+And because we are using the **testing database**, we don't affect anything by creating heroes for the test.
+
+To do it, we have to:
+
+* import the `Hero` model
+* require both fixtures, the **client** and the **session**
+* create some heroes and save them in the database using the **session**
+
+After that, we can send the request and check that we actually got the data back correctly from the database. π―
+
+Here's the important detail to notice: we can require fixtures in other fixtures **and also** in the test functions.
+
+The function for the **client fixture** and the actual testing function will **both** receive the same **session**.
+
+## Add the Rest of the Tests
+
+Using the same ideas, requiring the fixtures, creating data that we need for the tests, etc. we can now add the rest of the tests, they look quite similar to what we have done up to now.
+
+```Python hl_lines="3 18 33"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py[ln:84-125]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/app_testing/tutorial001/test_main.py!}
+```
+
+</details>
+
+## Run the Tests
+
+Now we can run the tests with `pytest` and see the results:
+
+<div class="termy">
+
+```console
+$ pytest
+
+============= test session starts ==============
+platform linux -- Python 3.7.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
+rootdir: /home/user/code/sqlmodel-tutorial
+<b>collected 7 items </b>
+
+---> 100%
+
+project/test_main.py <font color="#A6E22E">....... [100%]</font>
+
+<font color="#A6E22E">============== </font><font color="#A6E22E"><b>7 passed</b></font><font color="#A6E22E"> in 0.83s ===============</font>
+```
+
+</div>
+
+## Recap
+
+Did you read all that? Wow, I'm impressed! π
+
+Adding tests to your application will give you a lot of **certainty** that everything is **working correctly**, as you indended.
+
+And tests will be notoriously useful when **refactoring** your code, **changing things**, **adding features**. Because tests they can help catch a lot of errors that can be easily introduced by refactoring.
+
+And they will give you the confidence to work faster and **more efficiently**, because you know that you are checking if you are **not breaking anything**. π
+
+I think tests are one of those things that bring your code and you as a developer to the next professional level. π
+
+And if you read and studied all this, you already know a lot of the advanced ideas and tricks that took me years to learn. π
--- /dev/null
+# Update Data with FastAPI
+
+Now let's see how to update data in the database with a **FastAPI** *path operation*.
+
+## `HeroUpdate` Model
+
+We want clients to be able to udpate the `name`, the `secret_name`, and the `age` of a hero.
+
+But we don't want them to have to include all the data again just to **update a single field**.
+
+So, we need to have all those fields **marked as optional**.
+
+And because the `HeroBase` has some of them as *required* and not optional, we will need to **create a new model**.
+
+!!! tip
+ 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.
+
+ 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.
+
+So, let's create this new `HeroUpdate` model:
+
+```Python hl_lines="21-24"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/update/tutorial001.py[ln:7-28]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/update/tutorial001.py!}
+```
+
+</details>
+
+This is almost the same as `HeroBase`, but all the fields are optional, so we can't simply inherit from `HeroBase`.
+
+## Create the Update Path Operation
+
+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.
+
+```Python hl_lines="3-4"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/update/tutorial001.py[ln:76-91]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/update/tutorial001.py!}
+```
+
+</details>
+
+We also read the `hero_id` from the *path parameter* an the request body, a `HeroUpdate`.
+
+### Read the Existing Hero
+
+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.
+
+```Python hl_lines="6-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/update/tutorial001.py[ln:76-91]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/update/tutorial001.py!}
+```
+
+</details>
+
+### Get the New Data
+
+The `HeroUpdate` model has all the fields with **default values**, because they all have defaults, they are all optional, which is what we want.
+
+But that also means that if we just call `hero.dict()` we will get a dictionary that could potentially have several or all of those values with their defaults, for example:
+
+```Python
+{
+ "name": None,
+ "secret_name": None,
+ "age": None,
+}
+```
+
+And then if we update the hero in the database with this data, we would be removing any existing values, and that's probably **not what the client intended**.
+
+But fortunately Pydantic models (and so SQLModel models) have a parameter we can pass to the `.dict()` method for that: `exclude_unset=True`.
+
+This tells Pydantic to **not include** the values that were **not sent** by the client. Saying it another way, it would **only** include the values that were **sent by the client**.
+
+So, if the client sent a JSON with no values:
+
+```JSON
+{}
+```
+
+Then the dictionary we would get in Python using `hero.dict(exclude_unset=True)` would be:
+
+```Python
+{}
+```
+
+But if the client sent a JSON with:
+
+```JSON
+{
+ "name": "Deadpuddle"
+}
+```
+
+Then the dictionary we would get in Python using `hero.dict(exclude_unset=True)` would be:
+
+```Python
+{
+ "name": "Deadpuddle"
+}
+```
+
+Then we use that to get the data that was actually sent by the client:
+
+```Python hl_lines="9"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/update/tutorial001.py[ln:76-91]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/update/tutorial001.py!}
+```
+
+</details>
+
+## Update the Hero in the Database
+
+Now that we have a **dictionary with the data sent by the client**, we can iterate for each one of the keys and the values, and then we set them in the database hero model `db_hero` using `setattr()`.
+
+```Python hl_lines="10-11"
+# Code above omitted π
+
+{!./docs_src/tutorial/fastapi/update/tutorial001.py[ln:76-91]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/fastapi/update/tutorial001.py!}
+```
+
+</details>
+
+If you are not familiar with that `setattr()`, it takes an object, like the `db_hero`, then an attribute name (`key`), that in our case could be `"name"`, and a value (`value`). And then it **sets the attribute with that name to the value**.
+
+So, if `key` was `"name"` and `value` was `"Deadpuddle"`, then this code:
+
+```Python
+setattr(db_hero, key, value)
+```
+
+...would be more or less equivalent to:
+
+```Python
+db_hero.name = "Deadpuddle"
+```
+
+## Remove Fields
+
+Here's a bonus. π
+
+When getting the dictionary of data sent by the client, we only include **what the client actually sent**.
+
+This sounds simple, but it has some additional nuances that become **nice features**. β¨
+
+We are **not simply omitting** the data that has the **default values**.
+
+And we are **not simply omitting** anything that is `None`.
+
+This means that, if a model in the database **has a value different than the default**, the client could **reset it to the same value as the default**, or even `None`, and we would **still notice it** and **update it accordingly**. π€―π
+
+So, if the client wanted to intentionally remove the `age` of a hero, they could just send a JSON with:
+
+```JSON
+{
+ "age": null
+}
+```
+
+And when getting the data with `hero.dict(exclude_unset=True)`, we would get:
+
+```Python
+{
+ "age": None
+}
+```
+
+So, we would use that value and upate the `age` to `None` in the database, **just as the client intended**.
+
+Notice that `age` here is `None`, and **we still detected it**.
+
+Also that `name` was not even sent, and we don't *accidentaly* set it to `None` or something, we just didn't touch it, because the client didn't sent it, so we are **pefectly fine**, even in these corner cases. β¨
+
+These are some of the advantages of Pydantic, that we can use with SQLModel. π
+
+## Recap
+
+Using `.dict(exclude_unset=True)` in SQLModel models (and Pydantic models) we can easily update data **correctly**, even in the **edge cases**. π
--- /dev/null
+# Intro, Installation, and First Steps
+
+## Type hints
+
+If you need a refreshed about how to use Python type hints (type annotations), check <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">FastAPI's Python types intro</a>.
+
+You can also check the <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">mypy cheat sheet</a>.
+
+**SQLModel** uses type annotations for everything, this way you can use a familiar Python syntax and get all the editor support posible, with autocompletion and in-editor error checking.
+
+## Intro
+
+This tutorial shows you how to use **SQLModel** with all its features, step by step.
+
+Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can go directly to any specific one to solve your specific needs.
+
+It is also built to work as a future reference.
+
+So you can come back and see exactly what you need.
+
+## Run the code
+
+All the code blocks can be copied and used directly (they are tested Python files).
+
+It is **HIGHLY encouraged** that you write or copy the code, edit it, and run it locally.
+
+Using it in your editor is what really shows you the benefits of **SQLModel**, seeing how much code it saves you, and all the editor support you get, with autocompletion and in-editor error checks, preventing lots of bugs.
+
+Running the examples is what will really help you understand what is going on.
+
+You can learn a lot more by running some examples and playing around with them than by reading all the docs here.
+
+## Create a Project
+
+Please go ahead and create a directory for the project we will work on on this tutorial.
+
+What I normally do is that I create a directory named `code` inside my home/user directory.
+
+And inside of that I create one directory per project.
+
+So, for example:
+
+<div class="termy">
+
+```console
+// Go to the home directory
+$ cd
+// Create a directory for all your code projects
+$ mkdir code
+// Enter into that code directory
+$ cd code
+// Create a directory for this project
+$ mkdir sqlmodel-tutorial
+// Enter into that directory
+$ cd sqlmodel-tutorial
+```
+
+</div>
+
+!!! tip
+ Make sure you don't name it also `sqlmodel`, so that you don't end up overriding the name of the package.
+
+### Make sure you have Python
+
+Make sure you have an officially supported version of Python.
+
+Currently it is **Python 3.6** and above (Python 3.5 was already deprecated).
+
+You can check which version you have with:
+
+<div class="termy">
+
+```console
+$ python3 --version
+Python 3.6.9
+```
+
+</div>
+
+There's a chance that you have multiple Python versions installed.
+
+You might want to try with the specific versions, for example with:
+
+* `python3.10`
+* `python3.9`
+* `python3.8`
+* `python3.7`
+* `python3.6`
+
+The code would look like this:
+
+<div class="termy">
+
+```console
+// Let's check with just python3
+$ python3 --version
+// This is too old! π±
+Python 3.5.6
+// Let's see if python3.10 is available
+$ python3.10 --verson
+// Oh, no, this one is not available π
+command not found: python3.10
+$ python3.9 --version
+// Nice! This works π
+Python 3.9.0
+// In this case, we would continue using python3.9 instead of python3
+```
+
+</div>
+
+If you have different versions and `python3` is not the latest, make sure you use the latest version you have available. For example `python3.9`.
+
+If you don't have a valid Python version installed, go and install that first.
+
+### Create a Python virtual environment
+
+When writing Python code, you should **always** use virtual environments in one way or another.
+
+If you don't know what that is, you can read the <a href="https://docs.python.org/3/tutorial/venv.html" class="external-link" target="_blank">official tutorial for virtual environments</a>, it's quite simple.
+
+In very short, a virtual environment is a small directory that contains a copy of Python and all the libraries you need to run your code.
+
+And when you "activate" it, any package that you install, for example with `pip`, will be installed in that virtual environment.
+
+!!! tip
+ There are other tools to manage virtual environments, like <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>.
+
+ And there are alternatives that are particularly useful for deployment like <a href="https://docs.docker.com/get-started/" class="external-link" target="_blank">Docker</a> and other types of containers. In this case, the "virtual environment" is not just the Python standard files and the installed packages, but the whole system.
+
+Go ahead and create a Python virtual environment for this project. And make sure to also upgrade `pip`.
+
+Here are the commands you could use:
+
+=== "Linux, macOS, Linux in Windows"
+
+ <div class="termy">
+
+ ```console
+ // Remember that you might need to use python3.9 or similar π‘
+ // Create the virtual environment using the module "venv"
+ $ python3 -m venv env
+ // ...here it creates the virtual enviroment in the directory "env"
+ // Activate the virtual environment
+ $ source ./env/bin/activate
+ // Verify that the virtual environment is active
+ # (env) $$ which python
+ // The important part is that it is inside the project directory, at "code/sqlmodel-tutorial/env/bin/python"
+ /home/leela/code/sqlmodel-tutorial/env/bin/python
+ // Use the module "pip" to install and upgrade the package "pip" π€―
+ # (env) $$ python -m pip install --upgrade pip
+ ---> 100%
+ Successfully installed pip
+ ```
+
+ </div>
+
+=== "Windows PowerShell"
+
+ <div class="termy">
+
+ ```console
+ // Create the virtual environment using the module "venv"
+ # >$ python3 -m venv env
+ // ...here it creates the virtual enviroment in the directory "env"
+ // Activate the virtual environment
+ # >$ .\env\Scripts\Activate.ps1
+ // Verify that the virtual environment is active
+ # (env) >$ Get-Command python
+ // The important part is that it is inside the project directory, at "code\sqlmodel-tutorial\env\python.exe"
+ CommandType Name Version Source
+ ----------- ---- ------- ------
+ Application python 0.0.0.0 C:\Users\leela\code\sqlmodel-tutorial\env\python.exe
+ // Use the module "pip" to install and upgrade the package "pip" π€―
+ # (env) >$ python3 -m pip install --upgrade pip
+ ---> 100%
+ Successfully installed pip
+ ```
+
+ </div>
+
+## Install **SQLModel**
+
+Now, after making sure we are inside of a virtual environment in some way, we can install **SQLModel**:
+
+<div class="termy">
+
+```console
+# (env) $$ python -m pip install sqlmodel
+---> 100%
+Successfully installed sqlmodel pydantic sqlalchemy
+```
+
+</div>
+
+As **SQLModel** is built on top of <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a>, when you install `sqlmodel` they will also be automatically installed.
+
+## Install DB Browser for SQLite
+
+Remember that [SQLite is a simple database in a single file](../databases.md#a-single-file-database){.internal-link target=_blank}?
+
+For most of the tutorial I'll use SQLite for the examples.
+
+Python has integrated support for SQLite, it is a single file read and processed from Python. And it doesn't need an [External Database Server](../databases.md#a-server-database){.internal-link target=_blank}, so it will be perfect for learning.
+
+In fact, SQLite is perfectly capable of handling quite big applications. At some point you might want to migrate to a server-based database like <a href="https://www.postgresql.org/" class="external-link" target="_blank">PostgreSQL</a> (which is also free). But for now we'll stick to SQLite.
+
+Through the tutorial I will show you SQL fragments, and Python examples. And I hope (and expect π§) you to actually run them, and verify that the database is working as expected and showing you the same data.
+
+To be able to explore the SQLite file yourself, independent of Python code (and probably at the same time), I recommend you use <a href="https://sqlitebrowser.org/" class="external-link" target="_blank">DB Browser for SQLite</a>.
+
+It's a great and simple program to interact with SQLite databases (SQLite files) in a nice user interface.
+
+<img src="https://sqlitebrowser.org/images/screenshot.png">
+
+Go ahead and <a href="https://sqlitebrowser.org/" class="external-link" target="_blank">Install DB Browser for SQLite</a>, it's free.
+
+## Next Steps
+
+Okay, let's get going! On the [next section](create-db-and-table-with-db-browser.md) we'll start creating a database. π
--- /dev/null
+# Create Rows - Use the Session - INSERT
+
+Now that we have a database and a table, we can start adding data.
+
+Here's a reminder of how the table would look like, this is the data we want to add:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+## Create Table and Database
+
+We will continue from where we left of in the last chapter.
+
+This is the code we had to create the database and table, nothing new here:
+
+```{.python .annotate hl_lines="22" }
+{!./docs_src/tutorial/create_db_and_table/tutorial003.py[ln:1-20]!}
+
+# More code here later π
+
+{!./docs_src/tutorial/create_db_and_table/tutorial003.py[ln:23-24]!}
+```
+
+{!./docs_src/tutorial/create_db_and_table/annotations/en/tutorial003.md!}
+
+Now that we can create the database and the table, we will continue from this point and add more code on the same file to create the data.
+
+## Create Data with SQL
+
+Before working with Python code, let's see how we can create data with SQL.
+
+Let's say we want to insert the record/row for `Deadpond` into our database.
+
+We can do this with the following SQL code:
+
+```SQL
+INSERT INTO "hero" ("name", "secret_name")
+VALUES ("Deadpond", "Dive Wilson");
+```
+
+It means, more or less:
+
+> Hey SQL database π, please `INSERT` something (create a record/row) `INTO` the table `"hero"`.
+>
+> I want you to insert a row with some values in these specific columns:
+>
+> * `"name"`
+> * `"secret_name"`
+>
+> And the values I want you to put in these columns are:
+>
+> * `"Deadpond"`
+> * `"Dive Wilson"`
+
+### Try it in DB Explorer for SQLite
+
+You can try that SQL statement in **DB Explorer for SQLite**.
+
+Make sure to open the same database we already created by clicking <kbd>Open Database</kbd> and selecting the same `database.db` file.
+
+!!! tip
+ If you don't have that `database.db` file with the table `hero`, you can re-create it by running the Python program at the top. π
+
+Then go to the <kbd>Execute SQL</kbd> tab and copy the SQL from above.
+
+It would look like this:
+
+<img class="shadow" src="/img/tutorial/insert/image01.png">
+
+Click the "Execute all" <kbd>βΆ</kbd> button.
+
+Then you can go to the <kbd>Browse Data</kbd> tab, and you will see your newly created record/row:
+
+<img class="shadow" src="/img/tutorial/insert/image02.png">
+
+## Data in a Database and Data in Code
+
+When working with a database (SQL or any other type) in a programming language, we will always have some data **in memory**, in objects and variables we create in our code, and there will be some data **in the database**.
+
+We are constantly **getting** *some* of the data from the database and putting it in memory, in variables.
+
+The same way, we are constantly **creating** variables and objects with data in our code, that we then want to save in the database, so we **send** it somehow.
+
+In some cases, we can even create some data in memory and then change it and update it before saving it in the database.
+
+We might even decide with some logic in the code that we no longer want to save the data in the database, and then just remove it. π₯ And we only handled that data in memory, without sending it back and forth to the database.
+
+**SQLModel** does all it can (actually via SQLAlchemy) to make this interaction as simple, intuitive, and familiar or "close to programming" as possible. β¨
+
+But that division of the two places where some data might be at each moment in time (in memory or in the database) is always there. And it's important for you to have it in mind. π€
+
+## Create Data with Python and **SQLModel**
+
+Now let's create that same row in Python.
+
+First, remove that file `database.db` so we can start from a clean slate.
+
+Because we have Python code executing with data in memory, and the database is an independent system (an external SQLite file, or an external database server), we need to perform two steps:
+
+* create the data in Python, in memory (in a variable)
+* save/send the data to the database
+
+## Create a Model Instance
+
+Let's start with the first step, create the data in memory.
+
+We already created a class `Hero` that represents the `hero` table in the database.
+
+Each instance we create will represent the data in a row in the database.
+
+So, the first step is to simply create an instance of `Hero`.
+
+We'll create 3 right away, for the 3 heroes:
+
+```Python
+# Code above omitted π
+
+{!./docs_src/tutorial/insert/tutorial002.py[ln:23-26]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial002.py!}
+```
+
+</details>
+
+!!! tip
+ The code above in this file (the omitted code) is just the same code that you see at the top of this chapter.
+
+ The same code we used before to create the `Hero` model.
+
+We are putting that in a function `create_heroes()`, to call it later once we finish it.
+
+If you are trying the code interactively, you could also write that directly.
+
+## Create a **Session**
+
+Up to now, we have only used the **engine** to interact with the database.
+
+The **engine** is that single object that we share with all the code, and that is in charge of communicating with the database, handling the connections (when using a server database like PostgreSQL or MySQL), etc.
+
+But when working with **SQLModel** you will mostly use another tool that sits on top, the **Session**.
+
+In contrast to the **engine** that is one for the whole application, we create a new **session** for each group of operations with the database that belong together.
+
+In fact, the **session** needs and uses an **engine**.
+
+For example, if we have a web application, we would normally have a single **session** per request.
+
+We would re-use the same **engine** in all the code, everywhere in the application (shared by all the requests). But for each request, we would create and use a new **session**. And once the request is done, we would close the session.
+
+The first step is to import the `Session` class:
+
+```Python hl_lines="3"
+{!./docs_src/tutorial/insert/tutorial001.py[ln:1-3]!}
+
+# Code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial001.py!}
+```
+
+</details>
+
+Then we can create a new session:
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/insert/tutorial001.py[ln:23-28]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial001.py!}
+```
+
+</details>
+
+The new `Session` takes an `engine` as a parameter. And it will use the **engine** underneath.
+
+!!! tip
+ We will see a better way to create a **session** using a `with` block later.
+
+## Add Model Instances to the Session
+
+Now that we have some hero model instances (some objects in memory) and a **session**, the next step is to add them to the session:
+
+```Python hl_lines="9-11"
+# Code above omitted π
+{!./docs_src/tutorial/insert/tutorial001.py[ln:23-32]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial001.py!}
+```
+
+</details>
+
+By this point, our heroes are *not* stored in the database yet.
+
+And this is one of the cases where having a **session** independent of an **engine** makes sense.
+
+The session is holding in memory all the objects that should be saved in the database later.
+
+And once we are ready, we can **commit** those changes, and then the **session** will use the **engine** underneath to save all the data by sending the appropriate SQL to the database, and that way it will create all the rows. All in a single batch.
+
+This makes the interactions with the database more efficient (plus some extra benefits).
+
+!!! info "Technical Details"
+ The session will create a new transaction and execute all the SQL code in that transaction.
+
+ This ensures that the data is saved in a single batch, and that it will all succeed or all fail, but it won't leave the database in a broken state.
+
+## Commit the Session Changes
+
+Now that we have the heroes in the **session** and that we are ready to save all that to the database, we can **commit** the changes:
+
+```Python hl_lines="13"
+# Code above omitted π
+{!./docs_src/tutorial/insert/tutorial001.py[ln:23-34]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial001.py!}
+```
+
+</details>
+
+Once this line is executed, the **session** will use the **engine** to save all the data in the database by sending the corresponding SQL.
+
+## Create Heroes as a Script
+
+The function to create the heroes is now ready.
+
+Now we just need to make sure to call it when we run this program with Python directly.
+
+We already had a main block like:
+
+```Python
+if __name__ == "__main__":
+ create_db_and_tables()
+```
+
+We could add the new function there, as:
+
+```Python
+if __name__ == "__main__":
+ create_db_and_tables()
+ create_heroes()
+```
+
+But to keep things a bit more organized, let's instead create a new function `main()` that will contain all the code that should be executed when called as an independent script, and we can put there the previous function `create_db_and_tables()`, and add the new function `create_heroes()`:
+
+```Python hl_lines="2 4"
+# Code above omitted π
+{!./docs_src/tutorial/insert/tutorial002.py[ln:36-38]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial002.py!}
+```
+
+</details>
+
+And then we can call that single `main()` function from that main block:
+
+```Python hl_lines="8"
+# Code above omitted π
+{!./docs_src/tutorial/insert/tutorial002.py[ln:36-42]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial002.py!}
+```
+
+</details>
+
+By having everything that should happen when called as a script in a single function, we can easily add more code later on.
+
+And some other code could also import and use this same `main()` function if it was necessary.
+
+## Run the Script
+
+Now we can run our program as a script from the console.
+
+Because we created the **engine** with `echo=True`, it will print out all the SQL code that it is executing:
+
+<div class="termy">
+
+```console
+$ python app.py
+// Some boilerplate, checking that the hero table already exists
+INFO Engine BEGIN (implicit)
+INFO Engine PRAGMA main.table_info("hero")
+INFO Engine [raw sql] ()
+INFO Engine COMMIT
+// BEGIN a transaction automatically β¨
+INFO Engine BEGIN (implicit)
+// Our INSERT statement, it uses VALUES (?, ?, ?) as parameters
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+// ...and these are the parameter values π
+INFO Engine [generated in 0.00013s] ('Deadpond', 'Dive Wilson', None)
+// Again, for Spider-Boy
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.000755s ago] ('Spider-Boy', 'Pedro Parqueador', None)
+// And now for Rusty-Man
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001014s ago] ('Rusty-Man', 'Tommy Sharp', 48)
+// All good? Yes, commit this transaction! π
+INFO Engine COMMIT
+```
+
+</div>
+
+If you have ever used Git, this works very similarly.
+
+We use `session.add()` to add new objects (model instances) to the session (similar to `git add`).
+
+And that ends up in a group of data ready to be saved, but not saved yet.
+
+We can make more modifications, add more objects, etc.
+
+And once we are ready, we can **commit** all the changes in a single step (similar to `git commit`).
+
+## Close the Session
+
+The **session** holds some resources, like connections from the engine.
+
+So once we are done with the session, we should **close** it to make it release those resources and finish its cleanup:
+
+```Python hl_lines="16"
+# Code above omitted π
+
+{!./docs_src/tutorial/insert/tutorial001.py[ln:23-36]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial001.py!}
+```
+
+</details>
+
+But what happens if we forget to close the session?
+
+Or if there's an exception in the code and it never reaches the `session.close()`?
+
+For that, there's a better way to create and close the session, using a `with` block. π
+
+## A Session in a `with` Block
+
+It's good to know how the `Session` works and how to create and close it manually. It might be useful if, for example, you want to explore the code in an interactive session (for example with Jupyter).
+
+But there's a better way to handle the session, using a `with` block:
+
+```Python hl_lines="7-12"
+# Code above omitted π
+{!./docs_src/tutorial/insert/tutorial002.py[ln:23-33]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial002.py!}
+```
+
+</details>
+
+This is the same as creating the session manually and then manually closing it. But here, using a `with` block, it will be automatically created when **starting** the `with` block and assigned to the variable `session`, and it will be automatically closed after the `with` block is **finished**.
+
+And it will work even if there's an exception in the code. π
+
+## Review All the Code
+
+Let's give this whole file a final look. π
+
+You already know all the first part creating the `Hero` model class, the **engine**, and creating the database and table.
+
+Let's focus on the new code:
+
+```{.python .annotate }
+{!./docs_src/tutorial/insert/tutorial003.py!}
+```
+
+{!./docs_src/tutorial/insert/annotations/en/tutorial003.md!}
+
+!!! tip
+ Review what each line does by clicking each number bubble in the code. π
+
+You can now put it in a `app.py` file and run it with Python. And you will see an output like the one shown above.
+
+After that, if you open the database with **DB Browser for SQLite**, you will see the data you just created in the <kbd>Browse Data</kbd> tab:
+
+<img class="shadow" src="/img/tutorial/insert/image03.png">
+
+## What's Next
+
+Now you know how to add rows to the database. π
+
+Now is a good time to understand better why the `id` field **can't be `NULL`** on the database because it's a **primary key**, but actually **can be `None`** in the Python code.
+
+I'll tell you about that in the next chapter. π
--- /dev/null
+# Read a Range of Data - LIMIT and OFFSET
+
+Now you know how to get a single row with `.one()`, `.first()`, and `session.get()`.
+
+And you also know how to get multiple rows while filtering them using `.where()`.
+
+Now let's see how to get only a **range of results**.
+
+<img class="shadow" alt="table with first 3 rows selected" src="/img/tutorial/offset-and-limit/limit.svg">
+
+## Create Data
+
+We will continue with the same code as before, but we'll modify it a little the `select_heroes()` function to simplify the example and focus on what we want to achieve here.
+
+Again, we will create several heroes to have some data to select from:
+
+```Python hl_lines="4-10"
+# Code above omitted π
+
+{!./docs_src/tutorial/offset_and_limit/tutorial001.py[ln:23-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/offset_and_limit/tutorial001.py!}
+```
+
+</details>
+
+## Review Select All
+
+This is the code we had to select all the heroes in the `select()` examples:
+
+```Python hl_lines="3-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial003.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial003.py!}
+```
+
+</details>
+
+But this would get us **all** the heroes at the same time, in a database that could have thousands, that could be problematic.
+
+## Select with Limit
+
+We currently have 7 heroes in the database. But we could as well have thousands, so let's limit the results to get only the first 3:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/offset_and_limit/tutorial001.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/offset_and_limit/tutorial001.py!}
+```
+
+</details>
+
+The special **select** object we get from `select()` also has a method `.limit()` that we can use to limit the results to a certain number.
+
+In this case, instead of getting all the 7 rows, we are limiting them to only get the first 3.
+
+<img class="shadow" alt="table with first 3 rows selected" src="/img/tutorial/offset-and-limit/limit.svg">
+
+## Run the Program on the Comamnd Line
+
+If we run it on the command line, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Select with LIMIT
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+ LIMIT ? OFFSET ?
+INFO Engine [no key 0.00014s] (3, 0)
+
+// Print the heroes received, only 3
+[
+ Hero(age=None, secret_name='Dive Wilson', id=1, name='Deadpond'),
+ Hero(age=None, secret_name='Pedro Parqueador', id=2, name='Spider-Boy'),
+ Hero(age=48, secret_name='Tommy Sharp', id=3, name='Rusty-Man')
+]
+```
+
+</div>
+
+Great! We got only 3 heroes as we wanted.
+
+!!! tip
+ We will check out that SQL code more in a bit.
+
+## Select with Offset and Limit
+
+Now we can limit the results to get only the first 3.
+
+But imagine we are in a user interface showing the results in batches of 3 heroes at a time.
+
+!!! tip
+ This is commonly called "pagination". Because the user interface would normally show a "page" of a predefined number of heroes at a time.
+
+ And then you can interact with the user interface to get the next page, and so on.
+
+How do we get the next 3?
+
+<img class="shadow" alt="table with next rows selected, from 4 to 6" src="/img/tutorial/offset-and-limit/limit2.svg">
+
+We can use `.offset()`:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/offset_and_limit/tutorial002.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/offset_and_limit/tutorial002.py!}
+```
+
+</details>
+
+The way this works is that the special **select** object we get from `select()` has methods like `.where()`, `.offset()` and `.limit()`.
+
+Each of those methods applies the change in the internal special select statement object, and also **return the same object**, this way, we can continue using more methods on it, like in the example above that we use both `.offset()` and `.limit()`.
+
+**Offset** means "skip this many rows", and as we want to skip the ones we already saw, the first three, we use `.offset(3)`.
+
+## Run the Program with Offset on the Comamnd Line
+
+Now we can run the program on the command line, and it will output:
+
+<div class="termy">
+
+```console
+$python app.py
+
+// Previous output omitted π
+
+// Select with LIMIT and OFFSET
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+ LIMIT ? OFFSET ?
+INFO Engine [no key 0.00020s] (3, 3)
+
+// Print the 3 heroes received, the second batch
+[
+ Hero(age=32, secret_name='Natalia Roman-on', id=4, name='Tarantula'),
+ Hero(age=35, secret_name='Trevor Challa', id=5, name='Black Lion'),
+ Hero(age=36, secret_name='Steve Weird', id=6, name='Dr. Weird')
+]
+```
+
+</div>
+
+## Select Next Batch
+
+Then to get the next batch of 3 rows we would offset all the ones we already saw, the first 6:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/offset_and_limit/tutorial003.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/offset_and_limit/tutorial003.py!}
+```
+
+</details>
+
+The database right now has **only 7 rows**, so this query can only get 1 row.
+
+<img class="shadow" alt="table with the last row (7th) selected" src="/img/tutorial/offset-and-limit/limit3.svg">
+
+But don't worry, the database won't throw an error trying to get 3 rows when there's only one (as would happen with a Python list).
+
+The database knows that we want to **limit** the number of results, but it doesn't necessarily has to find those many results.
+
+## Run the Program with the Last Batch on the Comamnd Line
+
+And if we run it in the command line, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Select last batch with LIMIT and OFFSET
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+ LIMIT ? OFFSET ?
+INFO Engine [no key 0.00038s] (3, 6)
+
+// Print last batch of heroes, only one
+[
+ Hero(age=93, secret_name='Esteban Rogelios', id=7, name='Captain North America')
+]
+```
+
+</div>
+
+## SQL with LIMIT and OFFSET
+
+You probably noticed the new SQL keywords `LIMIT` and `OFFSET`.
+
+You can use them in SQL, at the end of the other parts:
+
+```SQL
+SELECT id, name, secret_name, age
+FROM hero
+LIMIT 3 OFFSET 6
+```
+
+If you try that in **DB Browser for SQLite**, you will get the same result:
+
+<img class="shadow" alt="DB Browser for SQLite showing the result of the SQL query" src="/img/tutorial/offset-and-limit/db-browser.png">
+
+## Combine Limit and Offset with Where
+
+Of course, you can also combine `.limit()` and `.offset()` with `.where()` and other methods you will learn about later:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/offset_and_limit/tutorial004.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/offset_and_limit/tutorial004.py!}
+```
+
+</details>
+
+## Run the Program with Limit and Where on the Comamnd Line
+
+If we run it on the command line, it will find all the heroes in the database with an age above 32. That would normally be 4 heroes.
+
+But we are limiting the results to only get the first 3:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Select with WHERE and LIMIT
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age > ?
+ LIMIT ? OFFSET ?
+INFO Engine [no key 0.00022s] (32, 3, 0)
+
+// Print the heroes received, only 3
+[
+ Hero(age=35, secret_name='Trevor Challa', id=5, name='Black Lion'),
+ Hero(age=36, secret_name='Steve Weird', id=6, name='Dr. Weird'),
+ Hero(age=48, secret_name='Tommy Sharp', id=3, name='Rusty-Man')
+]
+```
+
+</div>
+
+## Recap
+
+Independently of how you filter the data with `.where()` or other methods, you can limit the query to get at maximum some number of results with `.limit()`.
+
+And the same way, you can skip the first results with `.offset()`.
--- /dev/null
+# Create Data with Many-to-Many Relationships
+
+Let's continue from where we left and create some data.
+
+We'll create data for this same **many-to-many** relationship with a link table:
+
+<img alt="many-to-many table relationships" src="/img/tutorial/many-to-many/many-to-many.svg">
+
+We'll continue from where we left off with the previous code.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+## Create Heroes
+
+As we have done before, we'll create a function `create_heroes()` and we'll create some teams and heroes in it:
+
+```Python hl_lines="11"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:42-60]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+This is very similar to what we have done before.
+
+We create a couple of teams, and then three heroes.
+
+The only new detail is that instead of using an argument `team` we now use `teams`, because that is the name of the new **relationship attribute**. And more importantly, we pass a **list of teams** (even if it contains a single team).
+
+See how **Deadpond** now belongs to the two teams?
+
+## Commit, Refresh, and Print
+
+Now let's do as we have done before, `commit` the **session**, `refresh` the data, and print it:
+
+```Python hl_lines="22-25 27-29 31-36"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:42-75]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+## Add to Main
+
+As before, add the `create_heroes()` function to the `main()` function to make sure it is called when running this program from the command line:
+
+```Python hl_lines="22-25 27-29 31-36"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:78-80]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+## Run the Program
+
+If we run the program from the command line, it would output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+// Insert the hero data first
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00041s] ('Deadpond', 'Dive Wilson', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001942s ago] ('Rusty-Man', 'Tommy Sharp', 48)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.002541s ago] ('Spider-Boy', 'Pedro Parqueador', None)
+// Insert the team data second
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [generated in 0.00037s] ('Z-Force', 'Sister Margaretβs Bar')
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [cached since 0.001239s ago] ('Preventers', 'Sharp Tower')
+// Insert the link data last, to be able to re-use the created IDs
+INFO Engine INSERT INTO heroteamlink (team_id, hero_id) VALUES (?, ?)
+INFO Engine [generated in 0.00026s] ((2, 3), (1, 1), (2, 1), (2, 2))
+// Commit and save the data in the database
+INFO Engine COMMIT
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+// Refresh the data
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00019s] (1,)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.001959s ago] (2,)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.003215s ago] (3,)
+
+// Print Deadpond
+Deadpond: name='Deadpond' age=None id=1 secret_name='Dive Wilson'
+
+// Accessing the .team attribute triggers a refresh
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team, heroteamlink
+WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
+INFO Engine [generated in 0.00025s] (1,)
+
+// Print Deadpond's teams, 2 teams! π
+Deadpond teams: [Team(id=1, name='Z-Force', headquarters='Sister Margaretβs Bar'), Team(id=2, name='Preventers', headquarters='Sharp Tower')]
+
+// Print Rusty-Man
+Rusty-Man: name='Rusty-Man' age=48 id=2 secret_name='Tommy Sharp'
+
+// Accessing the .team attribute triggers a refresh
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team, heroteamlink
+WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
+INFO Engine [cached since 0.001716s ago] (2,)
+
+// Print Rusty-Man teams, just one, but still a list
+Rusty-Man Teams: [Team(id=2, name='Preventers', headquarters='Sharp Tower')]
+
+// Print Spider-Boy
+Spider-Boy: name='Spider-Boy' age=None id=3 secret_name='Pedro Parqueador'
+
+// Accessing the .team attribute triggers a refresh
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team, heroteamlink
+WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
+INFO Engine [cached since 0.002739s ago] (3,)
+
+// Print Spider-Boy's teams, just one, but still a list
+Spider-Boy Teams: [Team(id=2, name='Preventers', headquarters='Sharp Tower')]
+
+// Automatic roll back any previous automatic transaction, at the end of the with block
+INFO Engine ROLLBACK
+```
+
+</div>
+
+## Recap
+
+After setting up the model link, using it with **relationship attributes** is fairly straighforward, just Python objects. β¨
--- /dev/null
+# Create Models with a Many-to-Many Link
+
+We'll now support **many-to-many** relationships using a **link table** like this:
+
+<img alt="many-to-many table relationships" src="/img/tutorial/many-to-many/many-to-many.svg">
+
+Let's start by defining the class models, including the **link table** model.
+
+## Link Table Model
+
+As we want to support a **many-to-many** relationship, now we need a **link table** to connect them.
+
+We can create it just as any other **SQLModel**:
+
+```Python hl_lines="6-12"
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:1-12]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+This is a **SQLModel** class model table like any other.
+
+It has two fields, `team_id` and `hero_id`.
+
+They are both **foreign keys** to their respective tables. We'll create those models in a second, but you already know how that works.
+
+And **both fields are primary keys**. We hadn't used this before. π€
+
+## Team Model
+
+Let's see the `Team` model, it's almost identical as before, but with a little change:
+
+```Python hl_lines="8"
+# Code above ommited π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:15-20]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+The **relationship attribute `heroes`** is still a list of heroes, annotatted as `List["Hero"]`. Again, we use `"Hero"` in quotes because we haven't declared that class yet by this point in the code (but as you know, editors and **SQLModel** understand that).
+
+We use the same **`Relationship()`** function.
+
+We use **`back_populates="teams"`**. Before we referenced an attribute `team`, but as now we can have many, we'll rename it to `teams` when creating the `Hero` model.
+
+And here's the important part to allow the **many-to-many** relationship, we use **`link_model=HeroTeamLink`**. That's it. β¨
+
+## Hero Model
+
+Let's see the other side, here's the `Hero` model:
+
+```Python hl_lines="9"
+# Code above ommited π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:23-29]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+We **removed** the previous `team_id` field (column) because now the relationship is done via the link table. π₯
+
+The relationship attribute is now named **`teams`** instead of `team`, as now we support multiple teams.
+
+It is no longer an `Optional[Team]` but a list of teams, annotated as **`List[Team]`**.
+
+We are using the **`Relationship()`** here too.
+
+We still have **`back_populates="heroes"`** as before.
+
+And now we have a **`link_model=HeroTeamLink`**. β¨
+
+## Create the Tables
+
+The same as before, we will have the rest of the code to create the **engine**, and a function to create all the tables `create_db_and_tables()`.
+
+```Python hl_lines="9"
+# Code above ommited π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:32-39]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+
+And as in previous examples, we will add that function to a function `main()`, and we will call that `main()` function in the main block:
+
+```Python hl_lines="4"
+# Code above ommited π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:78-79]!}
+ # We will do more stuff here later π
+
+{!./docs_src/tutorial/many_to_many/tutorial001.py[ln:83-84]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+
+## Run the Code
+
+If you run the code in the command line, it would output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Boilerplate ommited π
+
+INFO Engine
+CREATE TABLE team (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ headquarters VARCHAR NOT NULL,
+ PRIMARY KEY (id)
+)
+
+
+INFO Engine [no key 0.00033s] ()
+INFO Engine
+CREATE TABLE hero (
+ id INTEGER,
+ name VARCHAR NOT NULL,
+ secret_name VARCHAR NOT NULL,
+ age INTEGER,
+ PRIMARY KEY (id)
+)
+
+
+INFO Engine [no key 0.00016s] ()
+INFO Engine
+
+// Our shinny new link table β¨
+CREATE TABLE heroteamlink (
+ team_id INTEGER,
+ hero_id INTEGER,
+ PRIMARY KEY (team_id, hero_id),
+ FOREIGN KEY(team_id) REFERENCES team (id),
+ FOREIGN KEY(hero_id) REFERENCES hero (id)
+)
+
+
+INFO Engine [no key 0.00031s] ()
+INFO Engine COMMIT
+
+```
+
+</div>
+
+## Recap
+
+We can support **many-to-many** relationships between tables by declaring a link table.
+
+We can create it the same way as with other **SQLModel** classes, and then use it in the `link_model` parameter to `Relationship()`.
+
+Now let's work with data using these models in the next chapters. π€
--- /dev/null
+# Many to Many - Intro
+
+We saw how to work with <abbr title="Also called Many-to-One">One-to-Many</abbr> relationships in the data.
+
+But how do you handle **Many-to-Many** relationships?
+
+Let's explore them. π
+
+## Starting from One-to-Many
+
+Let's start with the familiar and simpler option of **One-to-Many**.
+
+We have one table with teams and one with heroes, and for each **one** team, we can have **many** heroes.
+
+As each team could have multiple heroes, we wouldn't be able to put the Hero IDs in columns for all of them in the `team` table.
+
+But as each hero can belong **only to one** team, we have a **single column** in the heroes table to point to the specific team (to a specific row in the `team` table).
+
+The `team` table looks like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+!!! tip
+ Notice that it doesn't have any foreign key to other tables.
+
+And the `hero` table looks like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+</table>
+
+We have a column in the `hero` table for the `team_id` that points to the ID of a specific team in the `team` table.
+
+This is how we connect each `hero` with a `team`:
+
+<img alt="table relationships" src="/img/databases/relationships.svg">
+
+Notice that each hero can only have **one** connection. But each team can receive **many** connections. In particular, the team **Preventers** has two heroes.
+
+## Introduce Many-to-Many
+
+But let's say that as **Deadpond** is a great chracter, they recruit him to the new **Preventers** team, but he's still part of the **Z-Force** team too.
+
+So, now, we need to be able to have a hero that is connected to **many** teams. And then, each team, should still be able to receive **many** heroes. So we need a **Many-to-Many** relationship.
+
+A naive approach that wouldn't work very well is to add more columns to the `hero` table. Imagine we add two extra columns. Now we could connect a single `hero` to 3 teams in total, but not more. So we haven't really solved the problem of supporting **many** teams, only a very limited fixed number of teams.
+
+We can do better! π€
+
+## Link Table
+
+We can create another table that would represent the link between the `hero` and `team` tables.
+
+All this table contains is two columns, `hero_id` and `team_id`.
+
+Both columns are **foreign keys** pointing to the ID of a specific row in the `hero` and `team` tables.
+
+As this will represent the **hero-team-link**, let's call the table `heroteamlink`.
+
+It would look like this:
+
+<img alt="many-to-many table relationships" src="/img/tutorial/many-to-many/many-to-many.svg">
+
+Notice that now the table `hero` **doesn't have a `team_id`** column anymore, it is replaced by this link table.
+
+And the `team` table, just as before, doesn't have any foreign key either.
+
+Specifically, the new link table `heroteamlink` would be:
+
+<table>
+<tr>
+<th>hero_id</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>1</td>
+</tr>
+<tr>
+<td>1</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>1</td>
+</tr>
+</table>
+
+!!! info
+ Other names used for this **link table** are:
+
+ * association table
+ * secondary table
+ * junction table
+ * intermediate table
+ * join table
+ * through table
+ * relationship table
+ * connection table
+
+ I'm using the term "link table" because it's short, doesn't collide with other terms already used (e.g. "relationship"), it's easy to remember how to write it, etc.
+
+## Link Primary Key
+
+Cool, we have a link table with **just two columns**. But remember that SQL databases [require each row to have a **primary key**](../../databases.md#identifications-primary-key){.internal-link target=_blank} that **uniquely identifies** the row in that table?
+
+Now, what is the **primary key** in this table?
+
+How to we identify each unique row?
+
+Should we add another column just to be the **primary key** of this link table? Nope! We don't have to do that. π
+
+**Both columns are the primary key** of each row in this table (and each row just has those two columns). β¨
+
+A primary key is a way to **uniquely identify** a particular row in a **single table**. But it doesn't have to be a single column.
+
+A primary key can be a group of the columns in a table, which combined are unique in this table.
+
+Check the table above again, see that **each row has a unique combination** of `hero_id` and `team_id`?
+
+We cannot have duplicated primary keys, which means that we cannot have duplicated links between `hero` and `team`, exactly what we want!
+
+For example, the database will now prevent an error like this, with a duplicated row:
+
+<table>
+<tr>
+<th>hero_id</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>1</td>
+</tr>
+<tr>
+<td>1</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>1</td>
+</tr>
+<tr>
+<td>3 π¨</td><td>1 π¨</td>
+</tr>
+</table>
+
+It wouldn't make sense to have a hero be part of the **same team twice**, right?
+
+Now, just by using the two columns as the primary keys of this table, SQL will take care of **preventing us from duplicating** a link between `hero` and `team`. β
+
+## Recap
+
+An intro with a recap! That's weird... but anyway. π€·
+
+Now you have the theory about the **many-to-many** relationships, and how to solve them with tables in SQL. π€
+
+Now let's check how to write the SQL and the code to work with them. π
--- /dev/null
+# Link Model with Extra Fields
+
+In the previous example we never interacted directly with the `HeroTeamLink` model, it was all through the automatic **many-to-many** relationship.
+
+But what if we needed to have additional data to describe the link between the two models?
+
+Let's say that we want to have an extra field/column to say if a hero **is still training** in that team or if they are already going on missions and stuff.
+
+Let's see how to achieve that.
+
+## Link Model with Two One-to-Many
+
+The way to handle this is to explicitly use the link model, to be able to get and modify its data (apart from the foreign keys pointing to the two models for `Hero` and `Team`).
+
+In the end, the way it works is just like two **one-to-many** relationships combined.
+
+A row in the table `heroteamlink` points to **one** particular hero, but a single hero can be connected to **many** hero-team links, so it's **one-to-many**.
+
+And also, the same row in the table `heroteamlink` points to **one** team, but a single team can be connected to **many** hero-team links, so it's also **one-to-many**.
+
+!!! tip
+ The previous many-to-many relationship was also just two one-to-many relationships combined, but now it's going to be much more explicit.
+
+## Update Link Model
+
+Let's update the `HeroTeamLink` model.
+
+We will add a new field `is_training`.
+
+And we will also add two **relationship attributes**, for the linked `team` and `hero`:
+
+```Python hl_lines="10 12-13"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:6-16]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+The new **relationship attributes** have their own `back_populates` pointing to new relationship attributes we will create in the `Hero` and `Team` models:
+
+* `team`: has `back_populates="hero_links"`, because in the `Team` model, the attribute will contain the links to the **team's heroes**.
+* `hero`: has `back_populates="team_links"`, because in the `Hero` model, the attribute will contain the links to the **hero's teams**.
+
+!!! info
+ In SQLAlchemy this is called an Association Object or Association Model.
+
+ I'm calling it **Link Model** just because that's easier to write avoiding typos. But you are also free to call it however you want. π
+
+## Update Team Model
+
+Now let's update the `Team` model.
+
+We no longer have the `heroes` relationship attribute, and instead we have the new `hero_links` attribute:
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:19-24]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+## Update Hero Model
+
+The same with the `Hero` model.
+
+We change the `teams` relationship attribute for `team_links`:
+
+```Python hl_lines="9"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:27-33]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+## Create Relationships
+
+Now the process to create relationships is very similar.
+
+But now we create the **explicit link models** manually, pointing to their hero and team instances, and specifying the additional link data (`is_training`):
+
+```Python hl_lines="21-30 32-35"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:46-85]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+We are just adding the link model instances to the session, because the link model instances are connected to the heroes and teams, they will be also automatically included in the session when we commit.
+
+## Run the Program
+
+Now, if we run the program, it will show almost the same output as before, because it is generating almost the same SQL, but this time including the new `is_training` column:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Insert the heroes
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00025s] ('Deadpond', 'Dive Wilson', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.00136s ago] ('Spider-Boy', 'Pedro Parqueador', None)
+INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
+INFO Engine [cached since 0.001858s ago] ('Rusty-Man', 'Tommy Sharp', 48)
+
+// Insert the teams
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [generated in 0.00019s] ('Z-Force', 'Sister Margaretβs Bar')
+INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
+INFO Engine [cached since 0.0007985s ago] ('Preventers', 'Sharp Tower')
+
+// Insert the hero-team links
+INFO Engine INSERT INTO heroteamlink (team_id, hero_id, is_training) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00023s] ((1, 1, 0), (2, 1, 1), (2, 2, 1), (2, 3, 0))
+// Save the changes in the transaction in the database
+INFO Engine COMMIT
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Automatically fetch the data on attribute access
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [generated in 0.00028s] (1,)
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.team_id
+INFO Engine [generated in 0.00026s] (1,)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00024s] (1,)
+
+// Print Z-Force hero data, including link data
+Z-Force hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: False
+
+// Automatically fetch the data on attribute access
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [cached since 0.008822s ago] (2,)
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.team_id
+INFO Engine [cached since 0.005778s ago] (2,)
+
+// Print Preventers hero data, including link data
+Preventers hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: True
+
+// Automatically fetch the data on attribute access
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.004196s ago] (2,)
+
+// Print Preventers hero data, including link data
+Preventers hero: name='Spider-Boy' age=None id=2 secret_name='Pedro Parqueador' is training: True
+
+// Automatically fetch the data on attribute access
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.006005s ago] (3,)
+
+// Print Preventers hero data, including link data
+Preventers hero: name='Rusty-Man' age=48 id=3 secret_name='Tommy Sharp' is training: False
+```
+
+</div>
+
+## Add Relationships
+
+Now, to add a new relationship, we have to create a new `HeroTeamLink` instance pointing to the hero and the team, add it to the session, and commit it.
+
+Here we do that in the `update_heroes()` function:
+
+```Python hl_lines="10-15"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:88-103]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+## Run the Program with the New Relationship
+
+If we run that program, we will see the output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Select the hero
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00014s] ('Spider-Boy',)
+
+// Select the team
+INFO Engine SELECT team.id, team.name, team.headquarters
+FROM team
+WHERE team.name = ?
+INFO Engine [no key 0.00012s] ('Z-Force',)
+
+// Create the link
+INFO Engine INSERT INTO heroteamlink (team_id, hero_id, is_training) VALUES (?, ?, ?)
+INFO Engine [generated in 0.00023s] (1, 2, 1)
+
+// Automatically refresh the data on attribute access
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.team_id
+INFO Engine [cached since 0.01514s ago] (1,)
+INFO Engine COMMIT
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.08953s ago] (2,)
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.hero_id
+INFO Engine [generated in 0.00018s] (2,)
+
+// Print updated hero links
+Updated Spider-Boy's Teams: [
+ HeroTeamLink(team_id=2, is_training=True, hero_id=2),
+ HeroTeamLink(team_id=1, is_training=True, hero_id=2)
+]
+
+// Automatically refresh team data on attribute access
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [cached since 0.1084s ago] (1,)
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.team_id
+INFO Engine [cached since 0.1054s ago] (1,)
+
+// Print team hero links
+Z-Force heroes: [
+ HeroTeamLink(team_id=1, is_training=False, hero_id=1),
+ HeroTeamLink(team_id=1, is_training=True, hero_id=2)
+]
+```
+
+</div>
+
+## Update Relationships with Links
+
+Now let's say that **Spider-Boy** has been training enough in the **Preventers**, and they say he can join the team full time.
+
+So now we want to update the status of `is_training` to `False`.
+
+We can do that by iterating on the links:
+
+```Python hl_lines="8-10"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:88-89]!}
+
+ # Code here omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial003.py[ln:105-113]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial003.py!}
+```
+
+</details>
+
+## Run the Program with the Updated Relationships
+
+And if we run the program now, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Automatically fetch team data on attribute access
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [generated in 0.00015s] (2,)
+
+// Update link row
+INFO Engine UPDATE heroteamlink SET is_training=? WHERE heroteamlink.team_id = ? AND heroteamlink.hero_id = ?
+INFO Engine [generated in 0.00020s] (0, 2, 2)
+
+// Save current transaction to database
+INFO Engine COMMIT
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Automatically fetch data on attribute access
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.2004s ago] (2,)
+INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
+FROM heroteamlink
+WHERE ? = heroteamlink.hero_id
+INFO Engine [cached since 0.1005s ago] (2,)
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [cached since 0.09707s ago] (2,)
+
+// Print Spider-Boy team, including link data, if is training
+Spider-Boy team: headquarters='Sharp Tower' id=2 name='Preventers' is training: False
+
+// Automatically fetch data on attribute access
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [cached since 0.2097s ago] (1,)
+
+// Print Spider-Boy team, including link data, if is training
+Spider-Boy team: headquarters='Sister Margaretβs Bar' id=1 name='Z-Force' is training: True
+INFO Engine ROLLBACK
+```
+
+</div>
+
+## Recap
+
+If you need to store more information about a **many-to-many** relationship you can use an explicit link model with extra data in it. π€
--- /dev/null
+# Update and Remove Many-to-Many Relationships
+
+Now we'll see how to update and remove these **many-to-many** relationships.
+
+We'll continue from where we left off with the previous code.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial001.py!}
+```
+
+</details>
+
+## Get Data to Update
+
+Let's now create a function `update_heroes()`.
+
+We'll get **Spider-Boy** and the **Z-Force** team.
+
+As you already know how these goes, I'll use the **short version** and get the data in a single Python statement.
+
+And because we are now using `select()`, we also have to import it.
+
+```Python hl_lines="3 7-12"
+{!./docs_src/tutorial/many_to_many/tutorial002.py[ln:1-3]!}
+
+# Some code here omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial002.py[ln:78-83]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial002.py!}
+```
+
+</details>
+
+And of course, we have to add `update_heroes()` to our `main()` function:
+
+```Python hl_lines="6"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial002.py[ln:100-107]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial002.py!}
+```
+
+</details>
+
+## Add Many-to-Many Relationships
+
+Now let's imagine that **Spider-Boy** thinks that the **Z-Force** team is super cool and decides to go there and join them.
+
+We can use the same **relationship attributes** to include `hero_spider_boy` in the `team_z_force.heroes`.
+
+```Python hl_lines="10-12 14-15"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial002.py[ln:78-90]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial002.py!}
+```
+
+</details>
+
+!!! tip
+ Because we are accessing an attribute in the models right after we commit, with `hero_spider_boy.teams` and `team_z_force.heroes`, the data is refreshed automatically.
+
+ So we don't have to call `session.refresh()`.
+
+We then commit the change, refresh, and print the updated **Spider-Boy**'s heroes to confirm.
+
+Notice that we only `add` **Z-Force** to the session, then we commit.
+
+We never add **Spider-Boy** to the session, and we never even refresh it. But we still print his teams.
+
+This still works correctly because we are using `back_populates` in the `Relationship()` in the models. That way, **SQLModel** (actually SQLAlchemy) can keep track of the changes and updates, and make sure they also happen on the relationships in the other related models. π
+
+## Run the Program
+
+You can confirm it's all working by running the program in the command line:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Create the new many-to-many relationship
+INFO Engine INSERT INTO heroteamlink (team_id, hero_id) VALUES (?, ?)
+INFO Engine [generated in 0.00020s] (1, 3)
+INFO Engine COMMIT
+
+// Start a new automatic transaction
+INFO Engine BEGIN (implicit)
+
+// Automatically refresh the data while accessing the attribute .teams
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00044s] (3,)
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team, heroteamlink
+WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
+INFO Engine [cached since 0.1648s ago] (3,)
+
+// Print Spider-Boy teams, including Z-Force π
+Updated Spider-Boy's Teams: [
+ Team(id=2, name='Preventers', headquarters='Sharp Tower'),
+ Team(id=1, name='Z-Force', headquarters='Sister Margaretβs Bar')
+]
+
+// Automatically refresh the data while accessing the attribute .heores
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero, heroteamlink
+WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
+INFO Engine [cached since 0.1499s ago] (1,)
+
+// Print Z-Force heroes, including Spider-Boy π
+Z-Force heroes: [
+ Hero(name='Deadpond', age=None, id=1, secret_name='Dive Wilson'),
+ Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', teams=[
+ Team(id=2, name='Preventers', headquarters='Sharp Tower'),
+ Team(id=1, name='Z-Force', headquarters='Sister Margaretβs Bar', heroes=[...])
+ ])
+]
+```
+
+</div>
+
+## Remove Many-to-Many Relationships
+
+Now let's say that right after joining the team, **Spider-Boy** realized that their "life preserving policies" are much more relaxed than what he's used to. π
+
+And their *occupational safety and health* is also not as great... π₯
+
+So, **Spider-Boy** decides to leave **Z-Force**.
+
+Let's update the relationships to remove `team_z_force` from `hero_spider_boy.teams`.
+
+Because `hero_spider_boy.teams` is just a list (a special list managed by SQLAlchemy, but a list), we can use the standard list methods.
+
+In this case, we use the method `.remove()`, that takes an item and removes it from the list.
+
+```Python hl_lines="17-19 21-22"
+# Code above omitted π
+
+{!./docs_src/tutorial/many_to_many/tutorial002.py[ln:78-97]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/many_to_many/tutorial002.py!}
+```
+
+</details>
+
+And this time, just to show again that by using `back_populates` **SQLModel** (actually SQLAlchemy) takes care of connecting the models by their relationships, even though we performed the operation from the `hero_spider_boy` object (modifying `hero_spider_boy.teams`), we are adding `team_z_force` to the **session**. And we commit that, without even add `hero_spider_boy`.
+
+This still works because by updating the teams in `hero_spider_boy`, because they are synchronized with `back_populates`, the changes are also reflected in `team_z_force`, so it also has changes to be saved in the DB (that **Spider-Boy** was removed).
+
+And then we add the team, and commit the changes, which updates the `team_z_force` object, and because it changed the table that also had a connection with the `hero_spider_boy`, it is also marked internally as updated, so it all works.
+
+And then we just print them again to confirm that everything worked correctly.
+
+## Run the Program Again
+
+To confirm that this last part worked, you can run the program again, it will output something like:
+
+<div style="font-size: 1rem;" class="termy">
+
+```console
+$ python app.py
+
+// Previous output omitted π
+
+// Delete the row in the link table
+INFO Engine DELETE FROM heroteamlink WHERE heroteamlink.team_id = ? AND heroteamlink.hero_id = ?
+INFO Engine [generated in 0.00043s] (1, 3)
+// Save the changes
+INFO Engine COMMIT
+
+// Automatically start a new transaction
+INFO Engine BEGIN (implicit)
+
+// Automatically refresh the data while accessing the attribute .heroes
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team
+WHERE team.id = ?
+INFO Engine [generated in 0.00029s] (1,)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero, heroteamlink
+WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
+INFO Engine [cached since 0.5625s ago] (1,)
+
+// Print the Z-Force heroes after reverting the changes
+Reverted Z-Force's heroes: [
+ Hero(name='Deadpond', age=None, id=1, secret_name='Dive Wilson')
+]
+
+// Automatically refresh the data while accessing the attribute .teams
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [cached since 0.4209s ago] (3,)
+INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
+FROM team, heroteamlink
+WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
+INFO Engine [cached since 0.5842s ago] (3,)
+
+// Print Spider-Boy's teams after reverting the changes
+Reverted Spider-Boy's teams: [
+ Team(id=2, name='Preventers', headquarters='Sharp Tower')
+]
+
+// Automatically roll back any possible previously unsaved transaction
+INFO Engine ROLLBACK
+
+```
+
+</div>
+
+## Recap
+
+Updating and removing many-to-many relationships is quite straightforward after setting up the **link model** and the relationship attributes.
+
+You can just use common list operation. π
--- /dev/null
+# Read One Row
+
+You already know how to filter rows to select using `.where()`.
+
+And you saw how when executing a `select()` it normally returns an **iterable** object.
+
+Or you can call `results.all()` to get a **list** of all the rows right away, instead of an iterable.
+
+But in many cases you really just want to read a **single row**, and having to deal with an iterable or a list is not as convenient.
+
+Let's see the utilities to read a single row.
+
+## Continue From Previous Code
+
+We'll continue with the same examples we have been using in the previous chapters to create and select data and we'll keep udpating them.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial006.py!}
+```
+
+</details>
+
+If you already executed the previous examples and have a database with data, **remove the database file** before running each example, that way you won't have duplicate data and you will be able to get the same results.
+
+## Read the First Row
+
+We have been iterating over the rows in a `result` object like:
+
+```Python hl_lines="7-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial006.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial006.py!}
+```
+
+</details>
+
+But let's say that we are not interested in all the rows, just the **first** one.
+
+We can call the `.first()` method on the `results` object to get the first row:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial001.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial001.py!}
+```
+
+</details>
+
+This will return the first object in the `results` (if there was any).
+
+That way, we don't have to deal with an iterable or a list.
+
+!!! tip
+ Notice that `.first()` is a method of the `results` object, not of the `select()` statement.
+
+Although this query would find two rows, by using `.first()` we get only the first row.
+
+If we run it in the command line it would output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age <= ?
+INFO Engine [no key 0.00021s] (35,)
+
+// Only print the first item
+Hero: secret_name='Natalia Roman-on' age=32 id=4 name='Tarantula'
+```
+
+</div>
+
+## First or `None`
+
+It would be possible that the SQL query doesn't find any row.
+
+In that case, `.first()` will return `None`:
+
+```Python hl_lines="5 7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial002.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial002.py!}
+```
+
+</details>
+
+In this case, as there's no hero with an age less than 25, `.first()` will return `None`.
+
+When we run it in the command line it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age <= ?
+INFO Engine [no key 0.00021s] (35,)
+
+// Now rows found, first is None
+Hero: None
+```
+
+</div>
+
+## Exactly One
+
+There might be cases where we want to ensure that there's exactly **one** row matching the query.
+
+And if there was more than one, it would mean that there's an error in the system, and we should terminate with an error.
+
+In that case, instead of `.first()` we can use `.one()`:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial003.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial003.py!}
+```
+
+</details>
+
+Here we know that there's only one `"Deadpond"`, and there shouldn't be any more than one.
+
+If we run it once will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00015s] ('Deadpond',)
+
+// Only one row found, we're good β
+Hero: secret_name='Dive Wilson' age=None id=1 name='Deadpond'
+```
+
+</div>
+
+But if we run it again, as it will create and insert all the heroes in the database again, they will be duplicated, and there will be more than one `"Deadpond"`. π±
+
+So, running it again, without first deleting the file `database.db` will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00015s] ('Deadpond',)
+
+// Oh, no, the database is in a broken state, with duplicates! π¨
+Traceback (most recent call last):
+
+// Some details about the error omitted
+
+sqlalchemy.exc.MultipleResultsFound: Multiple rows were found when exactly one was required
+```
+
+</div>
+
+## Exactly One with More Data
+
+Of course, even if we don't duplicate the data, we could get the same error if we send a query that finds more than one row and expect exactly one with `.one()`:
+
+```Python hl_lines="5 7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial004.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial004.py!}
+```
+
+</details>
+
+That would find 2 rows, and would end up with the same error.
+
+## Exactly One with No Data
+
+And also, if we get no rows at all with `.one()`, it will also raise an error:
+
+```Python hl_lines="5 7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial005.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial005.py!}
+```
+
+</details>
+
+In this case, as there are no heroes with an age less than 25, `.one()` will raise an error.
+
+This is what we would get if we run it in the command line:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age < ?
+INFO Engine [no key 0.00014s] (25,)
+
+// Oh, no, we expected one row but there aren't any! π¨
+Traceback (most recent call last):
+
+// Some details about the error omitted
+
+sqlalchemy.exc.NoResultFound: No row was found when one was required
+```
+
+</div>
+
+## Compact Version
+
+Of course, with `.first()` and `.one()` you would also probably write all that in a more compact form most of the time, all in a single line (or at least a single Python statement):
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial006.py[ln:44-47]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial006.py!}
+```
+
+</details>
+
+That would result in the same as some examples above.
+
+## Select by Id with `.where()`
+
+In many cases you might want to select a single row by its Id column with the **primary key**.
+
+You could do it the same way we have been doing with a `.where()` and then getting the first item with `.first()`:
+
+```Python hl_lines="5 7"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial007.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial007.py!}
+```
+
+</details>
+
+That would work correctly, as expected. But there's a shorter version. π
+
+## Select by Id with `.get()`
+
+As selecting a single row by its Id column with the **primary key** is a common operation, there's a shortcut for it:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial008.py[ln:44-47]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial008.py!}
+```
+
+</details>
+
+`session.get(Hero, 1)` is an equivalent to creating a `select()`, then filtering by Id using `.where()`, and then getting the first item with `.first()`.
+
+If you run it, it will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// SELECT with WHERE
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00021s] (1,)
+
+// The printed result
+Hero: secret_name='Dive Wilson' age=None id=1 name='Deadpond'
+```
+
+</div>
+
+## Select by Id with `.get()` with No Data
+
+`.get()` behaves similar to `.first()`, if there's no data it will simply return `None` (instead of raising an error):
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/one/tutorial009.py[ln:44-47]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/one/tutorial009.py!}
+```
+
+</details>
+
+Running that will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// SELECT with WHERE
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00024s] (9001,)
+
+// No data found, so the value is None
+Hero: None
+```
+
+</div>
+
+## Recap
+
+As querying the SQL database for a single row is a common operation, you know have several tools to do it in a short and simple way. π
--- /dev/null
+# Relationship back_populates
+
+Now you know how to use the **relationship attributes** to manipulate connected data in the database! π
+
+Let's now take a small step back and review how we defined those `Relationship()` attributes again, let's clarify that `back_populates` argument. π€
+
+## Relationship with `back_populates`
+
+So, what is that `back_populates` argument in each `Relationship()`?
+
+The value is a string with the name of the attribute in the **other** model class.
+
+<img src="/img/tutorial/relationships/attributes/back-populates.svg">
+
+That tells **SQLModel** that if something changes in this model, it should change that attribute in the other model, and it will work even before committing with the session (that would force a refresh of the data).
+
+Let's understand that better with an example.
+
+## An Incomplete Relationship
+
+Let's see how that works by writing an **incomplete** version first, without `back_populates`:
+
+```Python hl_lines="11 21"
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:1-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py!}
+```
+
+</details>
+
+## Read Data Objects
+
+Now, we will get the **Spider-Boy** hero and, *independently*, the **Preventers** team using two `select`s.
+
+As you already know how this works, I won't separate that in a select `statement`, `results`, etc. Let's use the shorter form in a single call:
+
+```Python hl_lines="5-7 9-11"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:105-113]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py!}
+```
+
+</details>
+
+!!! tip
+ When writing your own code, this is probably the style you will use most often, as it's shorter, more convenient, and you still get all the power of autocompletion and inline errors.
+
+## Print the Data
+
+Now, let's print the current **Spider-Boy**, the current **Preventers** team, and particularly, the current **Preventers** list of heroes:
+
+```Python hl_lines="13-15"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:105-117]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py!}
+```
+
+</details>
+
+Up to this point, it's all good. π
+
+In particular, the result of printing `preventers_team.heroes` is:
+
+``` hl_lines="3"
+Preventers Team Heroes: [
+ Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
+ Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', team_id=2),
+ Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
+ Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
+ Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
+]
+```
+
+Notice that we have **Spider-Boy** there.
+
+## Update Objects Before Committing
+
+Now let's update **Spider-Boy**, removing him from the team by setting `hero_spider_boy.team = None` and then let's print this object again:
+
+```Python hl_lines="8 12"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:105-106]!}
+
+ # Code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:119-123]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py!}
+```
+
+</details>
+
+The first important thing is, we *haven't commited* the hero yet, so accessing the list of heroes would not trigger an automatic refresh.
+
+But in our code, in this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. π₯
+
+!!! tip
+ We could revert that later by not committing the **session**, but that's not what we are interested in here.
+
+Here, at this point in the code, in memory, the code expects **Preventers** to *not include* **Spider-Boy**.
+
+The output of printing `hero_spider_boy` without team is:
+
+```
+Spider-Boy without team: name='Spider-Boy' age=None id=3 secret_name='Pedro Parqueador' team_id=2 team=None
+```
+
+Cool, the team is set to `None`, the `team_id` attribute still has the team ID until we save it. But that's okay as we are now working mainly with the **relationship attributes** and the objects. β
+
+But now, what happens when we print the `preventers_team.heroes`?
+
+``` hl_lines="3"
+Preventers Team Heroes again: [
+ Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
+ Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', team_id=2, team=None),
+ Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
+ Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
+ Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
+]
+```
+
+Oh, no! π± **Spider-Boy** is still listed there!
+
+## Commit and Print
+
+Now, if we commit it and print again:
+
+```Python hl_lines="8-9 15"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:105-106]!}
+
+ # Code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py[ln:125-132]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py!}
+```
+
+</details>
+
+When we access `preventers_team.heroes` after the `commit`, that triggers a refresh, so we get the latest list, without **Spider-Boy**, so that's fine again:
+
+```
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
+FROM hero
+WHERE ? = hero.team_id
+2021-08-13 11:15:24,658 INFO sqlalchemy.engine.Engine [cached since 0.1924s ago] (2,)
+
+Preventers Team Heroes after commit: [
+ Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
+ Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
+ Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
+ Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
+]
+```
+
+There's no **Spider-Boy** after committing, so that's good. π
+
+But we still have that inconsistency in that previous point above.
+
+If we use the objects before committing, we could end up having errors. π
+
+Let's fix that. π€
+
+## Fix It Using `back_populates`
+
+That's what `back_populates` is for. β¨
+
+Let's add it back:
+
+```Python hl_lines="11 21"
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:1-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py!}
+```
+
+</details>
+
+And we can keep the rest of the code the same:
+
+```Python hl_lines="8 12"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:105-106]!}
+
+ # Code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:119-123]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py!}
+```
+
+</details>
+
+!!! tip
+ This is the same section where we updated `hero_spider_boy.team` to `None` but we *haven't committed* that change yet.
+
+ The same section that caused a problem before.
+
+## Review the Result
+
+This time, **SQLModel** (actually SQLAlchemy) will be able to notice the change, and **automatically update the list of heroes** in the team, even before we commit.
+
+That second print would output:
+
+```
+Preventers Team Heroes again: [
+ Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
+ Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
+ Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
+ Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
+]
+```
+
+Notice that now **Spider-Boy** is not there, we fixed it with `back_populates`! π
+
+## The Value of `back_populates`
+
+Now that you know why `back_populates` is there, let's review the exact value again.
+
+It's quite simple code, it's just a string, but it might be confusing to think exactly *what* string should go there:
+
+```Python hl_lines="11 21"
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:1-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py!}
+```
+
+</details>
+
+The string in `back_populates` is the name of the attribute *in the other* model, that will reference *the current* model.
+
+<img src="/img/tutorial/relationships/attributes/back-populates.svg">
+
+So, in the class `Team`, we have an attribute `heroes` and we declare it with `Relationship(back_populates="team")`.
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:6-11]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py!}
+```
+
+</details>
+
+The string in `back_populates="team"` refers to the attribute `team` in the class `Hero` (the other class).
+
+And, in the class `Hero`, we declare an attribute `team`, and we declare it with `Relationship(back_populates="heroes")`.
+
+So, the string `"heroes"` refers to the attribute `heroes` in the class `Team`.
+
+```Python hl_lines="10"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py[ln:14-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py!}
+```
+
+</details>
+
+!!! tip
+ Each **relationship attribute** points to the other one, in the other model, using `back_populates`.
+
+Although it's simple code, it can be confusing to think about π΅, because the same line has concepts related to both models in multiple places:
+
+* Just by being in the **current** model, the line has something to do with the current model.
+* The name of the attribute is about the **other** model.
+* The type annotation is about the **other** model.
+* And the `back_populates` refers to an attribute in the **other** model, that points to the **current** model.
+
+## A Mental Trick to Remember `back_populates`
+
+A mental trick you can use to remember is that the string in `back_populates` is always about the current model class you are editing. π€
+
+So, if you are in the class `Hero`, the value of `back_populates` for any relationship attribute connecting to **any** other table (to any other model, it could be `Team`, `Weapon`, `Powers`, etc) will still always refer to this same class.
+
+So, `back_populates` would most probably be something like `"hero"` or `"heroes"`.
+
+<img src="/img/tutorial/relationships/attributes/back-populates2.svg">
+
+```Python hl_lines="3 10 13 15"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py[ln:29-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py!}
+```
+
+</details>
--- /dev/null
+# Create and Update Relationships
+
+Let's see now how to create data with relationships using these new **relationship attributes**. β¨
+
+## Create Instances with Fields
+
+Let's check the old code we used to create some heroes and teams:
+
+```Python hl_lines="9 12 18 24"
+# Code above omitted π
+
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-60]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+There are several things to **notice** here.
+
+First, we **create** some `Team` instance objects. We want to use the IDs of these teams when creating the `Hero` instances, in the `team_id` field.
+
+But model instances **don't have an ID** generated by the database until we `add` and `commit` them to the **session**. Before that, they are just `None`, and we want to use the actual IDs.
+
+So, we have to `add` them and `commit` the session first, before we start creating the `Hero` instances, to be able to **use their IDs**.
+
+Then, we use those IDs when creating the `Hero` instances. We `add` the new heroes to the session, and then we `commit` them.
+
+So, we are **committing twice**. And we have to remember to `add` some things first, and then `commit`, and do all that **in the right order**, otherwise we could end up using a `team.id` that is currently `None` because it hasn't been saved.
+
+This is the first area where these **relationship attributes** can help. π€
+
+## Create Instances with Relationship Attributes
+
+Now let's do all that, but this time using the new, shiny `Relationship` attributes:
+
+```Python hl_lines="9 12 18"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py[ln:34-57]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py!}
+```
+
+</details>
+
+Now we can create the `Team` instances and pass them directly to the new `team` argument when creating the `Hero` instances, as `team=team_preventers` instead of `team_id=team_preventers.id`.
+
+And thanks to SQLAlchemy and how it works underneath, these teams don't even have to have an ID yet, but because we are assigning the whole object to each hero, those teams **will be automatically created** in the database, the automatic ID will be generated, and will be set in the `team_id` column for each of the corresponding hero rows.
+
+In fact, now we don't even have to put the teams explicitly in the session with `session.add(team)`, because these `Team` instances are **already associated** with heroes that **we do** `add` to the session.
+
+SQLAlchemy knows that it also has to include those teams in the next commit to be able to save the heroes correctly.
+
+And then, as you can see, we only have to do one `commit()`.
+
+## Assign a Relationship
+
+The same way we could assign an integer with a `team.id` to a `hero.team_id`, we can also assign the `Team` instance to the `hero.team`:
+
+```Python hl_lines="8"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:34-35]!}
+
+ # Previous code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:59-63]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py!}
+```
+
+</details>
+
+## Create a Team with Heroes
+
+Before, we created some `Team` instances and passed them in the `team=` argument when creating `Hero` instances.
+
+We could also create the `Hero` instances first, and then pass them in the `heroes=` argument that takes a list, when creating a `Team` instance:
+
+```Python hl_lines="13 15-16"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:34-35]!}
+
+ # Previous code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:65-75]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py!}
+```
+
+</details>
+
+Here we create two heroes first, **Black Lion** and **Princess Sure-E**, and then we pass them in the `heroes` argument.
+
+Notice that, the same as before, we only have to `add` the `Team` instance to the session, and because the heroes are connected to it, they will be automatically saved too when we `commit`.
+
+## Include Relationship Objects in the Many Side
+
+We said before that this is a **many-to-one** relationship, because there can be **many** heroes that belong to **one** team.
+
+We can also connect data with these relationship attributes on the **many** side.
+
+As the attribute `team.heroes` behaves like a list, we can simply append to it.
+
+Let's create some more heroes and add them to the `team_preventers.heroes` list attribute:
+
+```Python hl_lines="14-18"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:34-35]!}
+
+ # Previous code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py[ln:77-93]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py!}
+```
+
+</details>
+
+The attribute `team_preventers.heroes` behaves like a list. But it's a special type of list, because when we modify it adding heroes to it, **SQLModel** (actually SQLAlchemy) **keeps track of the necessary changes** to be done in the database.
+
+Then we `add()` the team to the session and `commit()` it.
+
+And in the same way as before, we don't even have to `add()` the independent heroes to the session, because they are **connected to the team**.
+
+## Recap
+
+We can use common Python objects and attributes to create and update data connections with these **relationship attributes**. π
+
+Next we'll see how to use these relationship attributes to read connected data. π€
--- /dev/null
+# Define Relationships Attributes
+
+Now we are finally in one of the most exciting parts of **SQLModel**.
+
+Relationship Attributes. β¨
+
+We currently have a `team` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>headquarters</th>
+</tr>
+<tr>
+<td>1</td><td>Preventers</td><td>Sharp Tower</td>
+</tr>
+<tr>
+<td>2</td><td>Z-Force</td><td>Sister Margaretβs Bar</td>
+</tr>
+</table>
+
+And a `hero` table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team_id</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>2</td>
+</tr>
+<tr>
+<td>2</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>1</td>
+</tr>
+<tr>
+<td>3</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>1</td>
+</tr>
+</table>
+
+Now that you know how these tables work underneath and how the model classes represent them, it's time to add a little convenience that will make many operations in code simpler.
+
+## Declare Relationship Attributes
+
+Up to now, we have only used the `team_id` column to connect the tables when querying with `select()`:
+
+```Python hl_lines="18"
+{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:1-18]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/connect/insert/tutorial001.py!}
+```
+
+</details>
+
+This is a **plain field** like all the others, all representing a **column in the table**.
+
+But now let's add a couple of new special attributes to these model classes, let's add `Relationship` attributes.
+
+First, import `Relationship` from `sqlmodel`:
+
+```Python hl_lines="3"
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py[ln:1-3]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py!}
+```
+
+</details>
+
+Next, use that `Relationship` to declare a new attribute in the model classes:
+
+```Python hl_lines="11 21"
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py[ln:1-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py!}
+```
+
+</details>
+
+## What Are These Relationship Attributes
+
+This new attributes are not the same as fields, they **don't represent a column** directly in the database, and their value is not a singular value like an integer. Their value is the actual **entire object** that is related.
+
+So, in the case of a `Hero` instance, if you call `hero.team`, you will get the entire `Team` instance object that this hero belongs to. β¨
+
+For example, you could check if a `hero` belongs to any `team` (if `.team` is not `None`) and then print the team's `name`:
+
+```Python
+if hero.team:
+ print(hero.team.name)
+```
+
+## Optional Relationship Attributes
+
+Notice that in the `Hero` class, the type annotation for `team` is `Optional[Team]`.
+
+This means that this attribute could be `None`, or it could be a full `Team` object.
+
+This is because the related **`team_id` could also be `None`** (or `NULL` in the database).
+
+If it was required for a `Hero` instance to belong to a `Team`, then the `team_id` would be `int` instead of `Optional[int]`.
+
+And the `team` attribute would be a `Team` instead of `Optional[Team]`.
+
+## Relationship Attributes With Lists
+
+And in the `Team` class, the `heroes` attribute is annotated as a list of `Hero` objects, because that's what it will have.
+
+**SQLModel** (actually SQLAlchemy) is smart enough to know that the relationship is established by the `team_id`, as that's the foreign key that points from the `hero` table to the `team` table, so we don't have to specify that explicitly here.
+
+!!! tip
+ There's a couple of things we'll check again in some of the next chapters, about the `List["Hero"]` and the `back_populates`.
+
+ But for now, let's first see how to use these relationship attributes.
+
+## Next Steps
+
+Now let's see some real examples of how to use these new **relationship attributes** in the next chapters. β¨
--- /dev/null
+# Relationship Attributes - Intro
+
+In the previous chapters we discussed how to manage databases with tables that have **relationships** by using fields (columns) with **foreign keys** pointing to other columns.
+
+And then we read the data together with `select()` and using `.where()` or `.join()` to connect it.
+
+Now we will see how to use **Relationship Attributes**, an extra feature of **SQLModel** (and SQLAlchemy) to work with the data in the database in way much more familiar way, and closer to normal Python code.
+
+!!! info
+ When I say "**relationship**" I mean the standard dictionary term, of data related to other data.
+
+ I'm not using the term "**relation**" that is the technical, academical, SQL term for a single table.
+
+And using those **relationship attributes** is where a tool like **SQLModel** really shines. β¨
--- /dev/null
+# Read Relationships
+
+Now that we know how to connect data using **relationship Attributes**, let's see how to get and read the objects from a relationship.
+
+## Select a Hero
+
+First, add a function `select_heroes()` where we get a hero to start working with, and add that function to the `main()` function:
+
+```Python hl_lines="3-7 14"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py[ln:96-100]!}
+
+# Previous code here omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py[ln:110-113]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py!}
+```
+
+</details>
+
+## Select the Related Team - Old Way
+
+Now that we have a hero, we can get the team this hero belongs to.
+
+With what we have learned **up to now**, we could use a `select()` statement, then execute it with `session.exec()`, and then get the `.first()` result, for example:
+
+```Python hl_lines="9-12"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py[ln:96-105]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py!}
+```
+
+</details>
+
+## Get Relationship Team - New Way
+
+But now that we have the **relationship attributes**, we can just access them, and **SQLModel** (actually SQLAlchemy) will go and fetch the correspoinding data from the database, and make it available in the attribute. β¨
+
+So, the highlighted block above, has the same results as the block below:
+
+```Python hl_lines="11"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py[ln:96-100]!}
+
+ # Code from the previous example omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py[ln:107]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py!}
+```
+
+</details>
+
+!!! tip
+ The automatic data fetching will work as long as the starting object (in this case the `Hero`) is associated with an **open** session.
+
+ For example, here, **inside** a `with` block with a `Session` object.
+
+## Get a List of Relationship Objects
+
+And the same way, when we are working on the **many** side of the **one-to-many** relationship, we can get a list of of the related objects just by accessing the relationship attribute:
+
+```Python hl_lines="9"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py[ln:96-102]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py!}
+```
+
+</details>
+
+That would print a list with all the heroes in the Preventers team:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Automatically fetch the heroes
+INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
+FROM hero
+WHERE ? = hero.team_id
+INFO Engine [cached since 0.8774s ago] (2,)
+
+// Print the list of Preventers
+Preventers heroes: [
+ Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
+ Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', team_id=2),
+ Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
+ Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
+ Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
+]
+```
+
+</div>
+
+## Recap
+
+With **relationship attributes** you can use the power of common Python objects to easily access related data from the database. π
--- /dev/null
+# Remove Relationships
+
+Now let's say that **Spider-Boy** tells **Rusty-Man** something like:
+
+> I don't feel so good Mr. Sharp
+
+And then for some reason needs to leave the **Preventers** for some years. π
+
+We can remove the relationship by setting it to `None`, the same as with the `team_id`, it also works with the new relationship attribute `.team`:
+
+```Python hl_lines="9"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py[ln:105-116]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py!}
+```
+
+</details>
+
+And of course, we should remember to add this `update_heroes()` function to `main()` so that it runs when we call this program from the command line:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py[ln:119-123]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py!}
+```
+
+</details>
+
+## Recap
+
+This chapter was too short for a recap, wasn't it? π€
+
+Anyway, **relationship attributes** make it easy and intuitive to work with relationships stored in the database. π
--- /dev/null
+## About the String in `List["Hero"]`
+
+In the first Relationship attribute, we declare it with `List["Hero"]`, putting the `Hero` in quotes instead of just normally there:
+
+```Python hl_lines="11"
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py[ln:1-21]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py!}
+```
+
+</details>
+
+What's that about? Can't we just write it normally as `List[Hero]`?
+
+By that point, in that line in the code, the Python interpreter **doesn't know of any class `Hero`**, and if we put it just there, it would try to find it unsuccessfully, and then fail. π
+
+But by putting it in quotes, in a string, the interpreter sees it as just a string with the text `"Hero"` inside.
+
+But the editor and other tools can see that **the string is actually a type annotation inside**, and provide all the autocompletion, type checks, etc. π
+
+And of course, **SQLModel** can also understand it in the string correctly. β¨
+
+That is actually part of Python, it's the current official solution to handle it.
+
+!!! info
+ There's a lot of work going on in Python itself to make that simpler and more intuitive, and find ways to make it possible to not wrap the class in a string.
--- /dev/null
+# Read Data - SELECT
+
+We already have a database and a table with some data in it that looks more or less like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+Things are getting more exciting! Let's now see how to read data from the database! π€©
+
+## Continue From Previous Code
+
+Let's continue from the last code we used to create some data.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/insert/tutorial002.py!}
+```
+
+</details>
+
+We are creating a **SQLModel** `Hero` class model and creating some records.
+
+We will need the `Hero` model and the **engine**, but we will create a new session to query data in a new function.
+
+## Read Data with SQL
+
+Before writing Python code let's do a quick review of how querying data with SQL looks like:
+
+```SQL
+SELECT id, name, secret_name, age
+FROM hero
+```
+
+It means, more or less:
+
+> Hey SQL database π, please go and `SELECT` some data for me.
+>
+> I'll first tell you the columns I want:
+>
+> * `id`
+> * `name`
+> * `secret_name`
+> * `age`
+>
+> And I want you to get them `FROM` the table called `"hero"`.
+
+Then the database will go and get the data and return it to you in a table like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+You can try that out in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/select/image01.png">
+
+!!! warning
+ Here we are getting all the rows.
+
+ If you have thousands of rows, that could be expensive to compute for the database.
+
+ You would normally want to filter the rows to receive only the ones you want. But we'll learn about that later in the next chapter.
+
+### A SQL Shortcut
+
+If we want to get all the columns like in this case above, in SQL there's a shortcut, instead of specifying each of the column names wew could write a `*`:
+
+```SQL
+SELECT *
+FROM hero
+```
+
+That would end up in the same result. Although we won't use that for **SQLModel**.
+
+### `SELECT` Less Columns
+
+We can also `SELECT` less columns, for example:
+
+```SQL
+SELECT id, name
+FROM hero
+```
+
+Here we are only selecting the `id` and `name` columns.
+
+And it would result in a table like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td>
+</tr>
+</table>
+
+And here is something interesting to notice. SQL databases store their data in tables. And they also always communicate their results in **tables**.
+
+### `SELECT` Variants
+
+The SQL language allows several **variations** in several places.
+
+One of those variations is that in `SELECT` statements you can use the names of the columns directly, or you can prefix them with the name of the table and a dot.
+
+For example, the same SQL code above could be written as:
+
+```SQL
+SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+```
+
+This will be particularly important later when working with multiple tables at the same time that could have the same name for some columns.
+
+For example `hero.id` and `team.id`, or `hero.name` and `team.name`.
+
+Another variation is that most of the SQL keywords like `SELECT` can also be written in lowercase, like `select`.
+
+### Result Tables Don't Have to Exist
+
+This is the interesting part. The tables returned by SQL databases **don't have to exist** in the database as independent tables. π§
+
+For example, in our database, we only have one table that has all the columns, `id`, `name`, `secret_name`, `age`. And here we are getting a result table with less columns.
+
+One of the main points of SQL is to be able to keep the data structured in different tables, without repeating data, etc, and then query the database in many ways and get many different tables as a result.
+
+## Read Data with **SQLModel**
+
+Now let's do the same query to read all the heroes, but with **SQLModel**.
+
+## Create a **Session**
+
+The first step is to create a **Session**, the same way we did when creating the rows.
+
+We will start with that in a new function `select_heroes()`:
+
+```Python hl_lines="3-4"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-37]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+## Create a `select` Statement
+
+Next, pretty much the same way we wrote a SQL `SELECT` statement above, now we'll create a **SQLModel** `select` statement.
+
+First we have to import `select` from `sqlmodel` at the top of the file:
+
+```Python hl_lines="3"
+{!./docs_src/tutorial/select/tutorial001.py[ln:1-3]!}
+
+# More code below ommitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+And then we will use it to create a `SELECT` statement in Python code:
+
+```Python hl_lines="9"
+{!./docs_src/tutorial/select/tutorial001.py[ln:1-3]!}
+
+# More code here omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-38]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+It's a very simple line of code that conveys a lot of information:
+
+```Python
+statement = select(Hero)
+```
+
+This is equivalent to the first SQL `SELECT` statement above:
+
+```SQL
+SELECT id, name, secret_name, age
+FROM hero
+```
+
+We pass the class model `Hero` to the `select()` function. And that tells it that we want to select all the columns necessary for the `Hero` class.
+
+And notice that in the `select()` function we don't explicitly specify the `FROM` part. It is already obvious to **SQLModel** (actually to SQLAlchemy) that we want to select `FROM` the table `hero`, because that's the one associated with the `Hero` class model.
+
+!!! tip
+ The value of the `statement` returned by `select()` is a special object that allows us to do other things.
+
+ I'll tell you about that in the next chapters.
+
+## Execute the Statement
+
+Now that we have the `select` statement, we can execute it with the **session**:
+
+```Python hl_lines="6"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-39]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+This will tell the **session** to go ahead and use the **engine** to execute that `SELECT` statement in the database and bring the results back.
+
+Because we created the **engine** with `echo=True`, it will show the SQL it executes in the output.
+
+This `session.exec(statement)` will generate this output:
+
+```
+INFO Engine BEGIN (implicit)
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+INFO Engine [no key 0.00032s] ()
+```
+
+The database returns the table with all the data, just like above when we wrote SQL directly:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+## Iterate Through the Results
+
+The `results` object is an <abbr title="Something that can be used in a for loop">iterable</abbr> that can be used to go through each one of the rows.
+
+Now we can put it in a `for` loop and print each one of the heroes:
+
+```Python hl_lines="7-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+This will print the output:
+
+```
+id=1 name='Deadpond' age=None secret_name='Dive Wilson'
+id=2 name='Spider-Boy' age=None secret_name='Pedro Parqueador'
+id=3 name='Rusty-Man' age=48 secret_name='Tommy Sharp'
+```
+
+## Add `select_heroes()` to `main()`
+
+Now include a call to `select_heroes()` in the `main()` function so that it is executed when we run the program from the command line:
+
+```Python hl_lines="14"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-47]!}
+
+# More code here later π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+## Review The Code
+
+Great, you're now being able to read the data from the database! π
+
+Let's review the code up to this point:
+
+```{ .python .annotate }
+{!./docs_src/tutorial/select/tutorial002.py!}
+```
+
+{!./docs_src/tutorial/select/annotations/en/tutorial002.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+Here it starts to become more evident why we should have a single **engine** for the whole application, but different **sessions** for each group of operations.
+
+This new session we created uses the *same* **engine**, but it's a new and independent **session**.
+
+The code above creating the models could, for example, live in a function handling web API requests and creating models.
+
+And the second section reading data from the database could be in another function for other requests.
+
+So, both sections could be in **different places** and would need their own sessions.
+
+!!! info
+ To be fair, in this example all that code could actually share the same **session**, there's actually no need to have two here.
+
+ But it allows me to show you how they could be separated and to reinforce the idea that you should have **one engine** per application, and **multiple sessions**, one per each group of operations.
+
+## Get a List of `Hero` Objects
+
+Up to now we are using the `results` to iterate over them.
+
+But for different reasons you might want to have the full **list of `Hero`** objects right away instead of just an *iterable*. For example, if you want to return them in a web API.
+
+The special `results` object also has a method `results.all()` that returns a list with all the objects:
+
+```Python hl_lines="7"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial003.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial003.py!}
+```
+
+</details>
+
+With this now we have all the heroes in a list in the `heroes` variable.
+
+After printing it, we would see something like:
+
+```
+[
+ Hero(id=1, name='Deadpond', age=None, secret_name='Dive Wilson'),
+ Hero(id=2, name='Spider-Boy', age=None, secret_name='Pedro Parqueador'),
+ Hero(id=3, name='Rusty-Man', age=48, secret_name='Tommy Sharp')
+]
+```
+
+!!! info
+ It would actually look more compact, I'm formatting it a bit for you to see that it is actually a list with all the data.
+
+## Compact Version
+
+I have been creating several variables to be able to explain to you what each thing is doing.
+
+But knowing what is each object and what it is all doing, we can simplify it a bit and put it in a more compact form:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial004.py[ln:36-39]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial004.py!}
+```
+
+</details>
+
+Here we are putting it all on a single line, you will probably put the select statements in a single line like this more often.
+
+## SQLModel or SQLAlchemy - Technical Details
+
+**SQLModel** is actually, more or less, just **SQLAlchemy** and **Pydantic** underneath, combined together.
+
+It uses and returns the same types of objects and is compatible with both libraries.
+
+Nevertheless, **SQLModel** defines a few of its own internal parts to improve the developer experience.
+
+In this chapter we are touching some of them.
+
+### SQLModel's `select`
+
+When importing from `sqlmodel` the `select()` function, you are using **SQLModel**'s version of `select`.
+
+SQLAchemy also has it's own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally.
+
+But SQLModel's version does a lot of **tricks** with type annotations to make sure you get the best **editor support** possible, no matter if you use **VS Code**, **PyCharm**, or something else. β¨
+
+!!! info
+ There was a lot of work and research, with different versions of the internal code, to improve this as much as possible. π€
+
+### SQLModel's `session.exec`
+
+π’ This is one to pay special attention to.
+
+SQLAlchemy's own `Session` has a method `session.execute()`. It doesn't have a `session.exec()` method.
+
+If you see SQLAlchemy tutorials, they will always use `session.execute()`.
+
+**SQLModel**'s own `Session` inherits directly from SQLAlchemy's `Session`, and adds this additonal method `session.exec()`. Underneath, it uses the same `session.execute()`.
+
+But `session.exec()` does several **tricks** combined with the tricks in `session()` to give you the **best editor support**, with **autocompletion** and **inline errors** everywhere, even after getting data from a select. β¨
+
+For example, in SQLAlchemy you would need to add a `.scalars()` here:
+
+```Python
+heroes = session.execute(select(Hero)).scalars().all()
+```
+
+But you would have to remove it when selecting multiple things (we'll see that later).
+
+SQLModel's `session.exec()` takes care of that for you, so you don't have to add the `.scalars()`.
+
+This is something that SQLAlchemy currently can't provide, because the regular `session.execute()` supports several other use cases, including legacy ones, so it can't have all the internal type annotations and tricks to support this.
+
+On top of that, **SQLModel**'s `session.exec()` also does some tricks to reduce the amount of code you have to write and to make it as intuitive as possible.
+
+But SQLModel's `Session` still has access to `session.execute()` too.
+
+!!! tip
+ Your editor will give you autocompletion for both `session.exec()` and `session.execute()`.
+
+ π’ Remember to **always use `session.exec()`** to get the best editor support and developer experience.
+
+### Caveats of **SQLModel** Flavor
+
+SQLModel is designed to have the best **developer experience** in a narrow set of **very common use cases**. β¨
+
+You can still combine it with SQLAlchemy directly and use **all the features** of SQLAlchemy when you need to, including lower level more "pure" SQL constructs, exotic patterns, and even legacy ones. π€
+
+But **SQLModel**'s design (e.g. type annotations) assume you are using it in the ways I explain here in the documentation.
+
+Thanks to this, you will get as much **autocompletion** and **inline errors** as possible. π
+
+But this also means that if you use SQLModel with some more **exotic patterns** from SQLAlchemy, your editor might tell you that *there's an error*, while in fact, the code would still work.
+
+That's the trade-off. π€·
+
+But for the situations where you need those exotic patterns, you can always use SQLAlchemy directly combined with SQLModel (using the same models, etc).
--- /dev/null
+# Update Data - UPDATE
+
+Now let's see how to update data using **SQLModel**.
+
+## Continue From Previous Code
+
+As before, we'll continue from where we left off with the previous code.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial006.py!}
+```
+
+</details>
+
+Remember to remove the `database.db` file before running the examples to get the same results.
+
+## Update with SQL
+
+Let's quickly check how to update data with SQL:
+
+```SQL hl_lines="1-2"
+UPDATE hero
+SET age=16
+WHERE name = "Spider-Boy"
+```
+
+This means, more or less:
+
+> Hey SQL database π, I want to `UPDATE` the table called `hero`.
+>
+> Please `SET` the value of the `age` column to `16`...
+>
+> ...for each of the rows `WHERE` the value of the column `name` is equal to `"Spider-Boy"`.
+
+In a similar way to `SELECT` statements, the first part defines the columns to work with: what are the columns that have to be updated and to which value. The rest of the columns stay as they were.
+
+And the second part, with the `WHERE`, defines to which rows it should apply that update.
+
+In this case, as we only have one hero with the name `"Spider-Boy"`, it will only apply the udpate in that row.
+
+!!! info
+ Notice that in the `UPDATE` the single equals sign (`=`) means **assignment**, setting a column to some value.
+
+ And in the `WHERE` the same single equals sign (`=`) is used for **comparison** between two values, to find rows that match.
+
+ This is in contrast to Python and most programming languages, where a single equals sign (`=`) is used for assignment, and two equal signs (`==`) are used for comparisons.
+
+You can try that in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/update/image01.png">
+
+After that update, the data in the table will look like this, with the new age for Spider-Boy:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>16 β¨</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+!!! tip
+ It will probably be more common to find the row to update by Id, for example:
+
+ ```SQL
+ UPDATE hero
+ SET age=16
+ WHERE id = 2
+ ```
+
+ But in the example above I used `name` to make it more intuitive.
+
+Now let's do the same update in code, with **SQLModel**.
+
+To get the same results, delete the `database.db` file before running the examples.
+
+## Read From the Database
+
+We'll start by selecting the hero `"Spider-Boy"`, this is the one we will update:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+Let's not forget to add that `update_heroes()` function to the `main()` function so that we call it when executing the program from the command line:
+
+```Python hl_lines="6"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:58-65]!}
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+Up to that point, running that in the command line will output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate and previous output omitted π
+
+// The SELECT with WHERE
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00017s] ('Spider-Boy',)
+
+// Print the hero as obtained from the database
+Hero: name='Spider-Boy' secret_name='Pedro Parqueador' age=None id=2
+```
+
+</div>
+
+!!! tip
+ Notice that by this point, the hero still doesn't have an age.
+
+## Set a Field Value
+
+Now that you have a `hero` object, you can simply set the value of the field (the attribute representing a column) that you want.
+
+In this case, we will set the `age` to `16`:
+
+```Python hl_lines="10"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-51]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+## Add the Hero to the Session
+
+Now that the hero object in memory has a change, in this case a new value for the `age`, we need to add it to the session.
+
+This is the same we did when creating new hero instances:
+
+```Python hl_lines="11"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-52]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+## Commit the Session
+
+To save the current changes in the session, **commit** it.
+
+This will save the updated hero in the database:
+
+```Python hl_lines="12"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-53]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+It will also save anything else that was added to the session.
+
+For example, if you were also creating new heroes and had added those objects to the session before, they would now be saved too in this single commit.
+
+This commit will generate this output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// The SQL to update the hero in the database
+INFO Engine UPDATE hero SET age=? WHERE hero.id = ?
+INFO Engine [generated in 0.00017s] (16, 2)
+INFO Engine COMMIT
+```
+
+</div>
+
+## Refresh the Object
+
+At this point, the hero is updated in the database and it has the new data saved there.
+
+The data in the object would be automatically refreshed if we accessed an attribute, like `hero.name`.
+
+But in this example we are not accessing any attribute, we will only print the object. And we also want to be explicit, so we will `.refresh()` the object directly:
+
+```Python hl_lines="13"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-54]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+This refresh will trigger the same SQL query that would be automatically triggered by accessing an attribute. So it will generate this output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// The SQL to SELECT the fresh hero data
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.id = ?
+INFO Engine [generated in 0.00018s] (2,)
+```
+
+</div>
+
+## Print the Updated Object
+
+Now we can just print the hero:
+
+```Python hl_lines="14"
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial001.py[ln:44-55]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial001.py!}
+```
+
+</details>
+
+Because we refreshed it right after updating it, it has fresh data, including the new `age` we just updated.
+
+So, printing it will show the new `age`:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Previous output omitted π
+
+// Print the hero with the new age
+Updated hero: name='Spider-Boy' secret_name='Pedro Parqueador' age=16 id=2
+```
+
+</div>
+
+## Review the Code
+
+Now let's review all that code:
+
+```{ .python .annotate hl_lines="44-55" }
+{!./docs_src/tutorial/update/tutorial002.py!}
+```
+
+{!./docs_src/tutorial/update/annotations/en/tutorial002.md!}
+
+!!! tip
+ Check out the number bubbles to see what is done by each line of code.
+
+## Multiple Updates
+
+The update process with **SQLModel** is more or less the same as with creating new objects, you add them to the session, and then commit them.
+
+This also means that you can update several fields (atributes, columns) at once, and you can also update several objects (heroes) at once:
+
+```{ .python .annotate hl_lines="15-17 19-21 23" }
+# Code above omitted π
+
+{!./docs_src/tutorial/update/tutorial004.py[ln:44-70]!}
+
+# Code below omitted π
+```
+
+{!./docs_src/tutorial/update/annotations/en/tutorial004.md!}
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/update/tutorial004.py!}
+```
+
+</details>
+
+!!! tip
+ Review what each line does by clicking each number bubble in the code. π
+
+## Recap
+
+Update **SQLModel** objects just as you would with other Python objects. π
+
+Just remember to `add` them to a **session**, and then `commit` it. And if necessary, `refresh` them.
--- /dev/null
+# Filter Data - WHERE
+
+In the previous chapter we saw how to `SELECT` data from the database.
+
+We did it using pure **SQL** and using **SQLModel**.
+
+But we always got all the rows, the whole table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+In most of the cases we will want to get only one row, or only a group of rows.
+
+We will see how to do that now, to filter data and get only the rows **where** a condition is true.
+
+## Continue From Previous Code
+
+We'll continue with the same examples we have been using in the previous chapters to create and select data.
+
+And now we will update `select_heroes()` to filter the data.
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python hl_lines="36-41"
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+If you already executed the previous examples and have a database with data, **remove the database file** before running each example, that way you won't have duplicate data and you will be able to get the same results.
+
+## Filter Data with SQL
+
+Let's check first how to filter data with **SQL** using the `WHERE` keyword.
+
+```SQL hl_lines="3"
+SELECT id, name, secret_name, age
+FROM hero
+WHERE name = "Deadpond"
+```
+
+The first part means the same as before:
+
+> Hey SQL database π, please go and `SELECT` some data for me.
+>
+> I'll first tell you the columns I want:
+>
+> * `id`
+> * `name`
+> * `secret_name`
+> * `age`
+>
+> And I want you to get them `FROM` the table called `"hero"`.
+
+Then the `WHERE` keyword adds the following:
+
+> So, SQL database, I already told you what columns to `SELECT` and where to select them `FROM`.
+> But I don't want you to bring me all the rows, I only want the rows `WHERE` the `name` column has a value of `"Deadpond"`.
+
+Then the database will bring a table like this:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+</table>
+
+!!! tip
+ Even if the result is only one row, the database always returns a **table**.
+
+ In this case, a table with only one row.
+
+You can try that out in **DB Browser for SQLite**:
+
+<img class="shadow" src="/img/tutorial/where/image01.png">
+
+### `WHERE` and `FROM` are "clauses"
+
+These additional keywords with some sections like `WHERE` and `FROM` that go after `SELECT` (or others) have a technical name, they are called **clauses**.
+
+There are others clauses too, with their own SQL keywords.
+
+I won't use the term **clause** too much here, but it's good for you to know it as it will probably show up in other tutorials you could study later. π€
+
+## `SELECT` and `WHERE`
+
+Here's a quick tip that helps me think about it.
+
+* **`SELECT`** is used to tell the SQL database what **columns** to return.
+* **`WHERE`** is used to tell the SQL database what **rows** to return.
+
+The size of the table in the two dimensions depend mostly on those two keywords.
+
+### `SELECT` Land
+
+If the table has too many or too few **columns**, that's changed in the **`SELECT`** part.
+
+Starting with some table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+...and changing the number of **columns**:
+
+<table>
+<tr>
+<th>name</th>
+</tr>
+<tr>
+<td>Deadpond</td>
+</tr>
+<tr>
+<td>Spider-Boy</td>
+</tr>
+<tr>
+<td>Rusty-Man</td>
+</tr>
+</table>
+
+...is all `SELECT` land.
+
+### `WHERE` Land
+
+If the table has too many or too few **rows**, that's changed in the **`WHERE`** part.
+
+Starting with some table:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+<tr>
+<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td>
+</tr>
+</table>
+
+...and changing the number of **rows**:
+
+<table>
+<tr>
+<th>id</th><th>name</th><th>secret_name</th><th>age</th>
+</tr>
+<tr>
+<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td>
+</tr>
+</table>
+
+...is all `WHERE` land.
+
+## Review `SELECT` with **SQLModel**
+
+Let's review some of the code we used to read data with **SQLModel**.
+
+We care specially about the **select** statement:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/select/tutorial001.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/select/tutorial001.py!}
+```
+
+</details>
+
+## Filter Rows Using `WHERE` wtih **SQLModel**
+
+Now, the same way that we add `WHERE` to a SQL statement to filter rows, we can add a `.where()` to a **SQLModel** `select()` statment to filter rows, which will filter the objects returned:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial001.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial001.py!}
+```
+
+</details>
+
+It's a very small change, but it's packed of details. Let's explore them.
+
+## `select()` Objects
+
+The object returned by `select(Hero)` is a special type of object with some methods.
+
+One of those methods is `.where()` used to (unsurprisingly) add a `WHERE` to the SQL statement in that **select** object.
+
+There are other methods that will will explore later. π‘
+
+Most of these methods return the same object again after modifying it.
+
+So we could call one after the other:
+
+```Python
+statement = select(Hero).where(Hero.name == "Deadpond").where(Hero.age == 48)
+```
+
+## Calling `.where()`
+
+Now, this `.where()` method is special and very powerful. It is tightly integrated with **SQLModel** (actually SQLAlchemy) to let you use very familiar Python syntax and code.
+
+Notice that we didn't call it with a single equal (`=`) sign, and with something like:
+
+```Python
+# Not supported π¨
+select(Hero).where(name="Deadpond")
+```
+
+That would have been shorter, of course, but it would have been much more error prone and limited. I'll show you why in a bit.
+
+Instead, we used two `==`:
+
+```Python
+select(Hero).where(Hero.name == "Deadpond")
+```
+
+So, what's happening there?
+
+## `.where()` and Expressions
+
+In the example above we are using two equal signs (`==`). That's called the "**equality operator**".
+
+!!! tip
+ An **operator** is just a symbol that is put beside one value or in the middle of two values to do something with them.
+
+ `==` is called **equality** operator because it checks if two things are **equal**.
+
+When writing Python, if you write something using this equality operator (`==`) like:
+
+```Python
+some_name == "Deadpond"
+```
+
+...that's called an equality "**comparison**", and it normally results in a value of:
+
+```Python
+True
+```
+
+...or
+
+```Python
+False
+```
+
+!!! tip
+ `<`, `>`, `==`, `>=`, `<=`, and `!=` are all **operators** used for **comparisons**.
+
+But SQLAlchemy adds some magic to the columns/fields in a **model class** to make those Python comparisons have super powers.
+
+So, if you write something like:
+
+```Python
+Hero.name == "Deadpond"
+```
+
+...that doesn't result in a value of `True` or `False`. π€―
+
+Instead, it results in a special type of object. If you tried that in an interactive Python session, you'd see something like:
+
+```Python
+>>> Hero.name == "Deadpond"
+<sqlalchemy.sql.elements.BinaryExpression object at 0x7f4aec0d6c90>
+```
+
+So, that result value is an **expession** object. π‘
+
+And `.where()` takes one (or more) of these **expression** objects to update the SQL statement.
+
+## Model Class Attributes, Expressions, and Instances
+
+Now, let's stop for a second to make a clear distinction that is very important and easy to miss.
+
+**Model class attributes** for each of the columns/fields are special and can be used for expressions.
+
+But that's only for the **model class attributes**. π¨
+
+**Instance** attributes behave like normal Python values. β
+
+So, using the class (`Hero`, with capital `H`) in a Python comparison:
+
+```Python
+Hero.name == "Deadpond"
+```
+
+...results in one of those **expression** objects to be used with `.where()`:
+
+```Python
+<sqlalchemy.sql.elements.BinaryExpression object at 0x7f4aec0d6c90>
+```
+
+But if you take an instance:
+
+```Python
+some_hero = Hero(name="Deadpond", secret_name="Dive Wilson")
+```
+
+...and use it in a comparison:
+
+```Python
+some_hero.name == "Deadpond"
+```
+
+...that results in a Python value of:
+
+```Python
+True
+```
+
+...or if it was a different object with a different name, it could have been:
+
+```Python
+False
+```
+
+The difference is that one is using the **model class**, the other is using an **instance**.
+
+## Class or Instance
+
+It's quite probable that you will end up having some variable `hero` (with lowercase `h`) like:
+
+```Python
+hero = Hero(name="Deadpond", secret_name="Dive Wilson")
+```
+
+And now the class is `Hero` (with capital `H`) and the instance is `hero` (with a lowercase `h`).
+
+So now you have `Hero.name` and `hero.name` that look very similar, but are two different things:
+
+```Python
+>>> Hero.name == "Deadpond"
+<sqlalchemy.sql.elements.BinaryExpression object at 0x7f4aec0d6c90>
+
+>>> hero.name == "Deadpond"
+True
+```
+
+It's just something to pay attention to. π€
+
+But after understanding that difference between classes and instances it can feel natural, and you can do very powerful things. π
+
+For example, as `hero.name` works like a `str` and `Hero.name` works like a special object for comparisons, you could write some code like:
+
+```Python
+select(Hero).where(Hero.name == hero.name)
+```
+
+That would mean:
+
+> Hey SQL Database π, please `SELECT` all the columns
+>
+> `FROM` the table for the model class `Hero` (the table `"hero"`)
+>
+> `WHERE` the column `"name"` is equal to the name of this hero instance I have here: `hero.name` (in the example above, the value `"Deadpond"`).
+
+## `.where()` and Expressions Instead of Keyword Arguments
+
+Now, let me tell you why I think that for this use case of interacting with SQL databases it's better to have these expressions:
+
+```Python
+# Expression β¨
+select(Hero).where(Hero.name == "Deadpond")
+```
+
+...instead of keyword arguments like this:
+
+```Python
+# Not supported, keyword argument π¨
+select(Hero).where(name="Deadpond")
+```
+
+Of course, the keyword arguments would have been a bit shorter.
+
+But with the **expressions** your editor can help you a lot with autocompletion and inline error checks. β¨
+
+Let me give you an example. Let's imagine that keword arguments were supported in SQLModel and you wanted to filter using the secret identity of Spider-Boy.
+
+You could write:
+
+```Python
+# Don't copy this π¨
+select(Hero).where(secret_identity="Pedro Parqueador")
+```
+
+The editor would see the code, and because it doesn't have any information of which keyword arguments are allowed and which not, it would have no way to help you **detect the error**.
+
+Maybe your code could even run and seem like it's all fine, and then some months later you would be wondering why your app *never finds rows* although you were sure that there was one `"Pedro Parqueador"`. π±
+
+And maybe finally you would realize that we wrote the code using `secret_identity` which is not a column in the table. We should have written `secret_name` instead.
+
+Now, with the the expressions, your editor would show you an error right away if you tried this:
+
+```Python
+# Expression β¨
+select(Hero).where(Hero.secret_identity == "Pedro Parqueador")
+```
+
+Even better, it would autocomplete the correct one for you, to get:
+
+```Python
+select(Hero).where(Hero.secret_name == "Pedro Parqueador")
+```
+
+I think that alone, having better editor support, autocompletion, and inline errors, is enough to make it worth having expressions instead of keyword arguments. β¨
+
+!!! tip
+ **Expressions** also provide more features for other types of comparisons, shown down below. π
+
+## Exec the Statement
+
+Now that we know how `.where()` works, let's finish the code.
+
+It's actually the same as in previous chapters for selecting data:
+
+```Python hl_lines="6-8"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial001.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial001.py!}
+```
+
+</details>
+
+We take that statement, that now includes a `WHERE`, and we `exec()` it to get the results.
+
+And in this case the results will be just one:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// Now the important part, the SELECT with WHERE π‘
+
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.name = ?
+INFO Engine [no key 0.00014s] ('Deadpond',)
+
+// Here's the only printed hero
+secret_name='Dive Wilson' age=None id=1 name='Deadpond'
+```
+
+</div>
+
+
+!!! tip
+ The `results` object is an iterable to be used in a `for` loop.
+
+ Even if we got only one row, we iterate over that `results` object. Just as if it was a list of one element.
+
+ We'll see other ways to get the data later.
+
+## Other Comparisons
+
+Here's another great advantage of these special **expressions** passed to `.where()`.
+
+Above, we have been using an "equality" comparison (using `==`), only checking if two things are the same value.
+
+But we can use other standard Python comparisons. β¨
+
+### Not Equal
+
+We could get the rows where a column is **not** equal to a value using `!=`:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial002.py[ln:36-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial002.py!}
+```
+
+</details>
+
+That would output:
+
+```
+secret_name='Pedro Parqueador' age=None id=2 name='Spider-Boy'
+secret_name='Tommy Sharp' age=48 id=3 name='Rusty-Man'
+```
+
+### Pause to Add Data
+
+Let's update the function `create_heroes()` and add some more rows to make the next comparison examples clearer:
+
+```Python hl_lines="4-10 13-19"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial003.py[ln:23-41]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial003.py!}
+```
+
+</details>
+
+Now that we have several heroes with different ages, it's gonna be more obvious what the next comparisons do.
+
+### More Than
+
+Now let's use `>` to get the rows where a column is **more than** a value:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial003.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial003.py!}
+```
+
+</details>
+
+That would output:
+
+```
+age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
+age=93 id=7 name='Captain North America' secret_name='Esteban Rogelios'
+```
+
+!!! tip
+ Notice that it didn't select `Black Lion`, because the age is not *strictly* greater than `35`.
+
+### More Than or Equal
+
+Let's do that again, but with `>=` to get the rows where a column is **more than or equal** to a value:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial004.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial004.py!}
+```
+
+</details>
+
+Because we are using `>=`, the age `35` will be included in the output:
+
+``` hl_lines="2"
+age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
+age=35 id=5 name='Black Lion' secret_name='Trevor Challa'
+age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
+age=93 id=7 name='Captain North America' secret_name='Esteban Rogelios'
+```
+
+!!! tip
+ This time we got `Black Lion` too because although the age is not *strictly* greater than `35`it is *equal* to `35`.
+
+### Less Than
+
+Similarly, we can use `<` to get the rows where a column is **less than** a value:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial005.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial005.py!}
+```
+
+</details>
+
+And we get the younger one with an age in the database:
+
+```
+age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on'
+```
+
+!!! tip
+ We could imagine that **Spider-Boy** is even **younger**. But because we don't know the age, it is `NULL` in the database (`None` in Python), it doesn't match any of these age comparisons with numbers.
+
+### Less Than or Equal
+
+Finally, we can use `<=` to get the rows where a column is **less than or equal** to a value:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial006.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial006.py!}
+```
+
+</details>
+
+And we get the younger ones, `35` and below:
+
+``` hl_lines="2"
+age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on'
+age=35 id=5 name='Black Lion' secret_name='Trevor Challa'
+```
+
+!!! tip
+ We get `Black Lion` here too because although the age is not *strictly* less than `35` it is *equal* to `35`.
+
+### Benefits of Expresions
+
+Here's a good moment to see that being able to use these pure Python expressions instead of keyword arguments can help a lot. β¨
+
+We can use the same standard Python comparison operators like `.
+
+## Multiple `.where()`
+
+Because `.where()` returns the same special select object back, we can add more `.where()` calls to it:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial007.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial007.py!}
+```
+
+</details>
+
+This will select the rows `WHERE` the `age` is **greater than or equal** to `35`, `AND` also the `age` is **less than** `40`.
+
+The equivalent SQL would be:
+
+```SQL hl_lines="3"
+SELECT id, name, secret_name, age
+FROM hero
+WHERE age >= 35 AND age < 40
+```
+
+This uses `AND` to put both comparisons together.
+
+We can then run it to see the output from the program:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT statement with WHERE, also using AND
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age >= ? AND hero.age < ?
+INFO Engine [no key 0.00014s] (35, 40)
+
+// The two heros printed
+age=35 id=5 name='Black Lion' secret_name='Trevor Challa'
+age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
+
+```
+
+</div>
+
+## `.where()` With Multiple Expressions
+
+As an alternative to using multiple `.where()` we can also pass several expressions to a single `.where()`:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial008.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial008.py!}
+```
+
+</details>
+
+This is the same as the above, and will result in the same output with the two heroes:
+
+```
+age=35 id=5 name='Black Lion' secret_name='Trevor Challa'
+age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
+```
+
+## `.where()` With Multiple Expressions Using `OR`
+
+These last examples use `where()` with multiple expressions. And then those are combined in the final SQL using `AND`, which means that *all* of the expressions must be true in a row for it to be included in the results.
+
+But we can also combine expressions using `OR`. Which means that **any** (but not necessarily all) of the expressions should be true in a row for it to be included.
+
+To do it, you can import `or_`:
+
+```Python hl_lines="3"
+{!./docs_src/tutorial/where/tutorial009.py[ln:1-3]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial009.py!}
+```
+
+</details>
+
+And then pass both expressions to `or_()` and put it inside `.where()`.
+
+For example, here we select the heroes that are the youngest OR the oldest:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial009.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial009.py!}
+```
+
+</details>
+
+When we run it, this generates the output:
+
+<div class="termy">
+
+```console
+$ python app.py
+
+// Some boilerplate output omitted π
+
+// The SELECT statement with WHERE, also using OR π
+INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
+FROM hero
+WHERE hero.age <= ? OR hero.age > ?
+INFO Engine [no key 0.00021s] (35, 90)
+
+// The results include the youngest and oldest β¨
+secret_name='Natalia Roman-on' age=32 id=4 name='Tarantula'
+secret_name='Trevor Challa' age=35 id=5 name='Black Lion'
+secret_name='Esteban Rogelios' age=93 id=7 name='Captain North America'
+```
+
+</div>
+
+## Type Annotations and Errors
+
+There's a chance that your editor gives you an error when using these comparisons, like:
+
+```Python
+Hero.age > 35
+```
+
+It would be an error telling you that
+
+> `Hero.age` is potentially `None`, and you cannot compare `None` with `>`
+
+This is because as we are using pure and plain Python annotations for the fields, `age` is indeed annotated as `Optional[int]`, which means `int` or `None`.
+
+By using this simple and standard Python type annotations We get the benefit of the extra simplicity and the inline error checks when creating or using instances. β¨
+
+And when we use these special **class attributes** in a `.where()`, during execution of the program, the special class attribute will know that the comparison only applies for the values that are not `NULL` in the database, and it will work correctly.
+
+But the editor doesn't know that it's a special **class attribute**, so it tries to help us preventing an error (that in this case is a false alarm).
+
+Nevertheless, we can easily fix. π
+
+We can tell the editor that this class attribute is actually a special **SQLModel** column (instead of an instance attribute with a normal value).
+
+To do that, we can import `col()` (as short for "column"):
+
+```Python hl_lines="3"
+{!./docs_src/tutorial/where/tutorial011.py[ln:1-3]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial011.py!}
+```
+
+</details>
+
+And then put the **class attribute** inside `col()` when using it in a `.where()`:
+
+```Python hl_lines="5"
+# Code above omitted π
+
+{!./docs_src/tutorial/where/tutorial011.py[ln:44-49]!}
+
+# Code below omitted π
+```
+
+<details>
+<summary>π Full file preview</summary>
+
+```Python
+{!./docs_src/tutorial/where/tutorial011.py!}
+```
+
+</details>
+
+So, now the comparison is not:
+
+```Python
+Hero.age > 35
+```
+
+...but:
+
+```Python
+col(Hero.age) > 35
+```
+
+And with that the editor knows this code is actually fine, because this is a special **SQLModel** column.
+
+!!! tip
+ That `col()` will come handy later, giving autocompletion to several other things we can do with these special **class attributes** for columns.
+
+ But we'll get there later.
+
+## Recap
+
+You can use `.where()` with powerful expressions using **SQLModel** columns (the special class attributes) to filter the rows that you want. π