You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/advanced/advanced-relationships/self-referential.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,19 +6,19 @@ In database terms this means having a table with a foreign key reference to the
6
6
7
7
Say, for example, we want to introduce a `Villain` class. 😈 Every villain can have a **boss**, who also must be a villain. If a villain is the boss to other villains, we want to call those his **minions**.
8
8
9
-
Let's do this with **SQLModel**. 🤓
9
+
Let's implement this with **SQLModel**. 🤓
10
10
11
11
## Using SQLAlchemy arguments
12
12
13
-
We already learned a lot about [Relationship attributes](../../tutorial/relationship-attributes/index.md){.internal-link target=_blank} in previous chapters. We know that **SQLModel** is built on top of **SQLAlchemy** and we know that the latter allows defining self-referential relationships (see [their documentation](https://docs.sqlalchemy.org/en/20/orm/self_referential.html){.external-link target=_blank}).
13
+
We already learned a lot about [Relationship attributes](../../tutorial/relationship-attributes/index.md){.internal-link target=_blank} in previous chapters. We know that **SQLModel** is built on top of **SQLAlchemy**, which supports defining self-referential relationships (see [their documentation](https://docs.sqlalchemy.org/en/20/orm/self_referential.html){.external-link target=_blank}).
14
14
15
-
To allow more fine-grained control over it, the `Relationship` constructor allows explicitly passing additional keyword-arguments to the [`sqlalchemy.orm.relationship`](https://docs.sqlalchemy.org/en/20/orm/relationship_api.html#sqlalchemy.orm.relationship){.external-link target=_blank} constructor that is being called under the hood via the `sa_relationship_kwargs` parameter. This is supposed to be a mapping (e.g. a dictionary) of strings representing the SQLAlchemy **parameter names** to the **values** we want to pass through as arguments.
15
+
To allow more fine-grained control over it, the `Relationship` constructor allows explicitly passing additional keyword-arguments to the [`sqlalchemy.orm.relationship`](https://docs.sqlalchemy.org/en/20/orm/relationship_api.html#sqlalchemy.orm.relationship){.external-link target=_blank} constructor that is being called under the hood via the `sa_relationship_kwargs` parameter. This should be a mapping (e.g. a dictionary) of strings representing the SQLAlchemy **parameter names** to the **values** we want to pass through as arguments.
16
16
17
17
Since SQLAlchemy relationships provide the [`remote_side`](https://docs.sqlalchemy.org/en/20/orm/relationship_api.html#sqlalchemy.orm.relationship.params.remote_side){.external-link target=_blank} parameter for just such an occasion, we can leverage that directly to construct the self-referential pattern with minimal code.
Using the `sa_relationship_kwargs` parameter, we pass the keyword-argument `remote_side='Villain.id'` to the underlying relationship property.
21
+
Using the `sa_relationship_kwargs` parameter, we pass the keywordargument `remote_side='Villain.id'` to the underlying relationship property.
22
22
23
23
/// info
24
24
@@ -40,31 +40,31 @@ For our purposes, it is necessary that we also provide the `back_populates` para
40
40
41
41
In addition, the type annotations were made by enclosing our `Villain` class name in quotes, since we are referencing a class that is not yet fully defined by the time the interpreter reaches those lines. See the chapter on [type annotation strings](../../tutorial/relationship-attributes/type-annotation-strings.md){.internal-link target=_blank} for a detailed explanation.
42
42
43
-
Finally, as with regular (i.e. non-self-referential) foreign key relationships, it is up to us to decide, whether it makes sense to allow the field to be **empty** or not. In our example, not every villain must have a boss (in fact, we would otherwise introduce a circular reference chain, which would not make sense in this context). Therefore we declare `boss_id: Optional[int]` and `boss: Optional['Villain']`. This is analogous to the `Hero`→`Team` relationship we saw [in an earlier chapter](../../tutorial/relationship-attributes/define-relationships-attributes.md#relationship-attributes-or-none){.internal-link target=_blank}.
43
+
Finally, as with regular (i.e. non-self-referential) foreign key relationships, it is up to us to decide whether it makes sense to allow the field to be **empty** or not. In our example, not every villain must have a boss (in fact, we would otherwise introduce a circular reference chain, which would not make sense in this context). Therefore we declare `boss_id: Optional[int]` and `boss: Optional['Villain']`. This is analogous to the `Hero`→`Team` relationship we saw [in an earlier chapter](../../tutorial/relationship-attributes/define-relationships-attributes.md#relationship-attributes-or-none){.internal-link target=_blank}.
44
44
45
45
## Creating instances
46
46
47
-
Now let us see how we can create villains with a boss:
47
+
Now let's see how we can create villains with a boss:
Just as with regular relationships, we can simply pass our boss villain as an argument to the constructor with`boss=thinnus`.
51
+
Just as with regular relationships, we can simply pass our boss villain as an argument to the constructor using`boss=thinnus`.
52
52
53
-
If we only learn that a villain actually had a secret boss after we have already created him, we can just as easily assign him that boss retroactively:
53
+
If we later learn that a villain actually had a secret boss after we've already created him, we can just as easily assign that boss retroactively:
Since our relationships work both ways, we don't even need to add all our `clone_bot_`s to the session individually. Instead we can simply add `ultra_bot` once again and commit the changes. We do need to refresh them all individually though, if we want to get their updated attributes.
61
+
Since our relationships work both ways, we don't even need to add all our `clone_bot_`s to the session individually. Instead, we can simply add ultra_botagain and commit the changes. We do need to refresh them individually, though, if we want to access their updated attributes.
62
62
63
63
## Traversing the relationship graph
64
64
65
-
By setting up our relationships this way, we can easily go back and forth along the graph representing all relationships we have created so far.
65
+
By setting up our relationships this way, we can easily go back and forth along the graph representing all the relationships we've created so far.
66
66
67
-
For example, we can verify that our `clone_bot_1` has a boss, who has his own boss, and one of that top-boss' minions is `ebonite_mew`:
67
+
For example, we can verify that our `clone_bot_1` has a boss, who has his own boss, and that one of that topboss's minions is `ebonite_mew`:
68
68
69
69
```Python
70
70
top_boss_minions = clone_bot_3.boss.boss.minions
@@ -73,6 +73,6 @@ assert any(minion is ebonite_mew for minion in top_boss_minions) # passes
73
73
74
74
/// info
75
75
76
-
Notice that we can in fact check for **identity** using `is`as opposed to `==` here, since we are dealing with those exact same objects, not just objects that hold the same **data**.
76
+
Notice that we can, in fact, check for **identity** using `is`instead of `==` here, since we are dealing with the exact same objects, not just objects containing the same **data**.
0 commit comments