sqlmodel/docs/tutorial/relationship-attributes/create-and-update-relationships.md

169 lines
6.2 KiB
Markdown
Raw Normal View History

2021-08-24 13:02:48 +00:00
# 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. 🤝