3.6 KiB
Define Relationships Attributes
Now we are finally in one of the most exciting parts of SQLModel.
Relationship Attributes. ✨
We currently have a team
table:
id | name | headquarters |
---|---|---|
1 | Preventers | Sharp Tower |
2 | Z-Force | Sister Margaret's Bar |
And a hero
table:
id | name | secret_name | age | team_id |
---|---|---|---|---|
1 | Deadpond | Dive Wilson | null | 2 |
2 | Rusty-Man | Tommy Sharp | 48 | 1 |
3 | Spider-Boy | Pedro Parqueador | null | 1 |
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()
:
{* ./docs_src/tutorial/connect/insert/tutorial001_py310.py ln[1:16] hl[16] *}
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
:
{* ./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py ln[1] hl[1] *}
Next, use that Relationship
to declare a new attribute in the model classes:
{* ./docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py ln[1:19] hl[9,19] *}
What Are These Relationship Attributes
These 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
:
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]
, its Field
would be Field(foreign_key="team.id")
instead of Field(default=None, foreign_key="team.id")
and the team
attribute would be a Team
instead of Optional[Team]
.
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. ✨