10
10
:ref:`collections_toplevel`, which has additional detail on configuration
11
11
of collections via :func:`relationship`.
13
.. _relationship_patterns:
13
15
Basic Relational Patterns
14
16
--------------------------
16
A quick walkthrough of the basic relational patterns. In this section we
17
illustrate the classical mapping using :func:`mapper` in conjunction with
18
:func:`relationship`. Then (by popular demand), we illustrate the declarative
19
form using the :mod:`~sqlalchemy.ext.declarative` module.
21
Note that :func:`.relationship` is historically known as
22
:func:`.relation` in older versions of SQLAlchemy.
18
A quick walkthrough of the basic relational patterns.
20
The imports used for each of the following sections is as follows::
22
from sqlalchemy import Table, Column, Integer, ForeignKey
23
from sqlalchemy.orm import relationship, backref
24
from sqlalchemy.ext.declarative import declarative_base
26
Base = declarative_base()
27
A one to many relationship places a foreign key in the child table referencing
28
the parent. SQLAlchemy creates the relationship as a collection on the parent
29
object containing instances of the child object.
31
.. sourcecode:: python+sql
33
parent_table = Table('parent', metadata,
34
Column('id', Integer, primary_key=True))
36
child_table = Table('child', metadata,
37
Column('id', Integer, primary_key=True),
38
Column('parent_id', Integer, ForeignKey('parent.id'))
47
mapper(Parent, parent_table, properties={
48
'children': relationship(Child)
51
mapper(Child, child_table)
53
To establish a bi-directional relationship in one-to-many, where the "reverse" side is a many to one, specify the ``backref`` option:
55
.. sourcecode:: python+sql
57
mapper(Parent, parent_table, properties={
58
'children': relationship(Child, backref='parent')
61
mapper(Child, child_table)
32
A one to many relationship places a foreign key on the child table referencing
33
the parent. :func:`.relationship` is then specified on the parent, as referencing
34
a collection of items represented by the child::
37
__tablename__ = 'parent'
38
id = Column(Integer, primary_key=True)
39
children = relationship("Child")
42
__tablename__ = 'child'
43
id = Column(Integer, primary_key=True)
44
parent_id = Column(Integer, ForeignKey('parent.id'))
46
To establish a bidirectional relationship in one-to-many, where the "reverse"
47
side is a many to one, specify the ``backref`` option::
50
__tablename__ = 'parent'
51
id = Column(Integer, primary_key=True)
52
children = relationship("Child", backref="parent")
55
__tablename__ = 'child'
56
id = Column(Integer, primary_key=True)
57
parent_id = Column(Integer, ForeignKey('parent.id'))
63
59
``Child`` will get a ``parent`` attribute with many-to-one semantics.
67
from sqlalchemy.ext.declarative import declarative_base
68
Base = declarative_base()
71
__tablename__ = 'parent'
72
id = Column(Integer, primary_key=True)
73
children = relationship("Child", backref="parent")
76
__tablename__ = 'child'
77
id = Column(Integer, primary_key=True)
78
parent_id = Column(Integer, ForeignKey('parent.id'))
84
64
Many to one places a foreign key in the parent table referencing the child.
85
The mapping setup is identical to one-to-many, however SQLAlchemy creates the
86
relationship as a scalar attribute on the parent object referencing a single
87
instance of the child object.
89
.. sourcecode:: python+sql
91
parent_table = Table('parent', metadata,
92
Column('id', Integer, primary_key=True),
93
Column('child_id', Integer, ForeignKey('child.id')))
95
child_table = Table('child', metadata,
96
Column('id', Integer, primary_key=True),
105
mapper(Parent, parent_table, properties={
106
'child': relationship(Child)
109
mapper(Child, child_table)
111
Backref behavior is available here as well, where ``backref="parents"`` will
112
place a one-to-many collection on the ``Child`` class::
114
mapper(Parent, parent_table, properties={
115
'child': relationship(Child, backref="parents")
120
from sqlalchemy.ext.declarative import declarative_base
121
Base = declarative_base()
65
:func:`.relationship` is declared on the parent, where a new scalar-holding
66
attribute will be created::
69
__tablename__ = 'parent'
70
id = Column(Integer, primary_key=True)
71
child_id = Column(Integer, ForeignKey('child.id'))
72
child = relationship("Child")
75
__tablename__ = 'child'
76
id = Column(Integer, primary_key=True)
78
Bidirectional behavior is achieved by specifying ``backref="parents"``,
79
which will place a one-to-many collection on the ``Child`` class::
123
81
class Parent(Base):
124
82
__tablename__ = 'parent'
126
84
child_id = Column(Integer, ForeignKey('child.id'))
127
85
child = relationship("Child", backref="parents")
130
__tablename__ = 'child'
131
id = Column(Integer, primary_key=True)
136
One To One is essentially a bi-directional relationship with a scalar
90
One To One is essentially a bidirectional relationship with a scalar
137
91
attribute on both sides. To achieve this, the ``uselist=False`` flag indicates
138
92
the placement of a scalar attribute instead of a collection on the "many" side
139
93
of the relationship. To convert one-to-many into one-to-one::
141
parent_table = Table('parent', metadata,
142
Column('id', Integer, primary_key=True)
145
child_table = Table('child', metadata,
146
Column('id', Integer, primary_key=True),
147
Column('parent_id', Integer, ForeignKey('parent.id'))
150
mapper(Parent, parent_table, properties={
151
'child': relationship(Child, uselist=False, backref='parent')
154
mapper(Child, child_table)
96
__tablename__ = 'parent'
97
id = Column(Integer, primary_key=True)
98
child = relationship("Child", uselist=False, backref="parent")
101
__tablename__ = 'child'
102
id = Column(Integer, primary_key=True)
103
parent_id = Column(Integer, ForeignKey('parent.id'))
156
105
Or to turn a one-to-many backref into one-to-one, use the :func:`.backref` function
157
106
to provide arguments for the reverse side::
159
from sqlalchemy.orm import backref
161
parent_table = Table('parent', metadata,
162
Column('id', Integer, primary_key=True),
163
Column('child_id', Integer, ForeignKey('child.id'))
166
child_table = Table('child', metadata,
167
Column('id', Integer, primary_key=True)
170
mapper(Parent, parent_table, properties={
171
'child': relationship(Child, backref=backref('parent', uselist=False))
174
mapper(Child, child_table)
176
The second example above as declarative::
178
from sqlalchemy.ext.declarative import declarative_base
179
Base = declarative_base()
181
108
class Parent(Base):
182
109
__tablename__ = 'parent'
183
110
id = Column(Integer, primary_key=True)
188
115
__tablename__ = 'child'
189
116
id = Column(Integer, primary_key=True)
118
.. _relationships_many_to_many:
194
123
Many to Many adds an association table between two classes. The association
195
124
table is indicated by the ``secondary`` argument to
196
:func:`.relationship`.
198
.. sourcecode:: python+sql
200
left_table = Table('left', metadata,
201
Column('id', Integer, primary_key=True)
204
right_table = Table('right', metadata,
205
Column('id', Integer, primary_key=True)
208
association_table = Table('association', metadata,
125
:func:`.relationship`. Usually, the :class:`.Table` uses the :class:`.MetaData`
126
object associated with the declarative base class, so that the :class:`.ForeignKey`
127
directives can locate the remote tables with which to link::
129
association_table = Table('association', Base.metadata,
209
130
Column('left_id', Integer, ForeignKey('left.id')),
210
131
Column('right_id', Integer, ForeignKey('right.id'))
213
mapper(Parent, left_table, properties={
214
'children': relationship(Child, secondary=association_table)
217
mapper(Child, right_table)
219
For a bi-directional relationship, both sides of the relationship contain a
135
__tablename__ = 'left'
136
id = Column(Integer, primary_key=True)
137
children = relationship("Child",
138
secondary=association_table)
141
__tablename__ = 'right'
142
id = Column(Integer, primary_key=True)
144
For a bidirectional relationship, both sides of the relationship contain a
220
145
collection. The ``backref`` keyword will automatically use
221
the same ``secondary`` argument for the reverse relationship:
223
.. sourcecode:: python+sql
225
mapper(Parent, left_table, properties={
226
'children': relationship(Child, secondary=association_table,
230
With declarative, we still use the :class:`.Table` for the ``secondary``
231
argument. A class is not mapped to this table, so it remains in its
232
plain schematic form::
234
from sqlalchemy.ext.declarative import declarative_base
235
Base = declarative_base()
146
the same ``secondary`` argument for the reverse relationship::
237
148
association_table = Table('association', Base.metadata,
238
149
Column('left_id', Integer, ForeignKey('left.id')),
261
185
``secondary`` argument, you map a new class directly to the association table.
262
186
The left side of the relationship references the association object via
263
187
one-to-many, and the association class references the right side via
266
.. sourcecode:: python+sql
268
left_table = Table('left', metadata,
269
Column('id', Integer, primary_key=True)
272
right_table = Table('right', metadata,
273
Column('id', Integer, primary_key=True)
276
association_table = Table('association', metadata,
277
Column('left_id', Integer, ForeignKey('left.id'), primary_key=True),
278
Column('right_id', Integer, ForeignKey('right.id'), primary_key=True),
279
Column('data', String(50))
282
mapper(Parent, left_table, properties={
283
'children':relationship(Association)
286
mapper(Association, association_table, properties={
287
'child':relationship(Child)
290
mapper(Child, right_table)
292
The bi-directional version adds backrefs to both relationships:
294
.. sourcecode:: python+sql
296
mapper(Parent, left_table, properties={
297
'children':relationship(Association, backref="parent")
300
mapper(Association, association_table, properties={
301
'child':relationship(Child, backref="parent_assocs")
304
mapper(Child, right_table)
308
from sqlalchemy.ext.declarative import declarative_base
309
Base = declarative_base()
190
class Association(Base):
191
__tablename__ = 'association'
192
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
193
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
194
child = relationship("Child")
197
__tablename__ = 'left'
198
id = Column(Integer, primary_key=True)
199
children = relationship("Association")
202
__tablename__ = 'right'
203
id = Column(Integer, primary_key=True)
205
The bidirectional version adds backrefs to both relationships::
311
207
class Association(Base):
312
208
__tablename__ = 'association'
363
257
-----------------------------
365
259
The **adjacency list** pattern is a common relational pattern whereby a table
366
contains a foreign key reference to itself. This is the most common and simple
367
way to represent hierarchical data in flat tables. The other way is the
368
"nested sets" model, sometimes called "modified preorder". Despite what many
369
online articles say about modified preorder, the adjacency list model is
260
contains a foreign key reference to itself. This is the most common
261
way to represent hierarchical data in flat tables. Other methods
262
include **nested sets**, sometimes called "modified preorder",
263
as well as **materialized path**. Despite the appeal that modified preorder
264
has when evaluated for its fluency within SQL queries, the adjacency list model is
370
265
probably the most appropriate pattern for the large majority of hierarchical
371
266
storage needs, for reasons of concurrency, reduced complexity, and that
372
267
modified preorder has little advantage over an application which can fully
373
268
load subtrees into the application space.
375
SQLAlchemy commonly refers to an adjacency list relationship as a
376
**self-referential mapper**. In this example, we'll work with a single table
377
called ``nodes`` to represent a tree structure::
379
nodes = Table('nodes', metadata,
380
Column('id', Integer, primary_key=True),
381
Column('parent_id', Integer, ForeignKey('nodes.id')),
382
Column('data', String(50)),
385
A graph such as the following::
270
In this example, we'll work with a single mapped
271
class called ``Node``, representing a tree structure::
274
__tablename__ = 'node'
275
id = Column(Integer, primary_key=True)
276
parent_id = Column(Integer, ForeignKey('node.id'))
277
data = Column(String(50))
278
children = relationship("Node")
280
With this structure, a graph such as the following::
387
282
root --+---> child1
388
283
+---> child2 --+--> subchild1
403
SQLAlchemy's :func:`.mapper` configuration for a self-referential one-to-many
404
relationship is exactly like a "normal" one-to-many relationship. When
405
SQLAlchemy encounters the foreign key relationship from ``nodes`` to
406
``nodes``, it assumes one-to-many unless told otherwise:
408
.. sourcecode:: python+sql
414
mapper(Node, nodes, properties={
415
'children': relationship(Node)
418
To create a many-to-one relationship from child to parent, an extra indicator
419
of the "remote side" is added, which contains the
420
:class:`~sqlalchemy.schema.Column` object or objects indicating the remote
421
side of the relationship:
423
.. sourcecode:: python+sql
425
mapper(Node, nodes, properties={
426
'parent': relationship(Node, remote_side=[nodes.c.id])
429
And the bi-directional version combines both:
431
.. sourcecode:: python+sql
433
mapper(Node, nodes, properties={
434
'children': relationship(Node,
435
backref=backref('parent', remote_side=[nodes.c.id])
439
For comparison, the declarative version typically uses the inline ``id``
440
:class:`.Column` attribute to declare remote_side (note the list form is optional
441
when the collection is only one column)::
443
from sqlalchemy.ext.declarative import declarative_base
444
Base = declarative_base()
447
__tablename__ = 'nodes'
448
id = Column(Integer, primary_key=True)
449
parent_id = Column(Integer, ForeignKey('nodes.id'))
298
The :func:`.relationship` configuration here works in the
299
same way as a "normal" one-to-many relationship, with the
300
exception that the "direction", i.e. whether the relationship
301
is one-to-many or many-to-one, is assumed by default to
302
be one-to-many. To establish the relationship as many-to-one,
303
an extra directive is added known as ``remote_side``, which
304
is a :class:`.Column` or collection of :class:`.Column` objects
305
that indicate those which should be considered to be "remote"::
308
__tablename__ = 'node'
309
id = Column(Integer, primary_key=True)
310
parent_id = Column(Integer, ForeignKey('node.id'))
311
data = Column(String(50))
312
parent = relationship("Node", remote_side=[id])
314
Where above, the ``id`` column is applied as the ``remote_side``
315
of the ``parent`` :func:`.relationship`, thus establishing
316
``parent_id`` as the "local" side, and the relationship
317
then behaves as a many-to-one.
319
As always, both directions can be combined into a bidirectional
320
relationship using the :func:`.backref` function::
323
__tablename__ = 'node'
324
id = Column(Integer, primary_key=True)
325
parent_id = Column(Integer, ForeignKey('node.id'))
450
326
data = Column(String(50))
451
327
children = relationship("Node",
452
backref=backref('parent', remote_side=id)
328
backref=backref('parent', remote_side=[id])
455
331
There are several examples included with SQLAlchemy illustrating
456
332
self-referential strategies; these include :ref:`examples_adjacencylist` and
459
335
Self-Referential Query Strategies
460
336
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
463
Querying self-referential structures is done in the same way as any other
464
query in SQLAlchemy, such as below, we query for any node whose ``data``
465
attribute stores the value ``child2``:
467
.. sourcecode:: python+sql
338
Querying of self-referential structures works like any other query::
469
340
# get all nodes named 'child2'
470
341
session.query(Node).filter(Node.data=='child2')
472
On the subject of joins, i.e. those described in `datamapping_joins`,
473
self-referential structures require the usage of aliases so that the same
474
table can be referenced multiple times within the FROM clause of the query.
475
Aliasing can be done either manually using the ``nodes``
476
:class:`~sqlalchemy.schema.Table` object as a source of aliases:
478
.. sourcecode:: python+sql
480
# get all nodes named 'subchild1' with a parent named 'child2'
481
nodealias = nodes.alias()
482
{sql}session.query(Node).filter(Node.data=='subchild1').\
483
filter(and_(Node.parent_id==nodealias.c.id, nodealias.c.data=='child2')).all()
484
SELECT nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.data AS nodes_data
485
FROM nodes, nodes AS nodes_1
486
WHERE nodes.data = ? AND nodes.parent_id = nodes_1.id AND nodes_1.data = ?
487
['subchild1', 'child2']
489
or automatically, using ``join()`` with ``aliased=True``:
491
.. sourcecode:: python+sql
493
# get all nodes named 'subchild1' with a parent named 'child2'
494
{sql}session.query(Node).filter(Node.data=='subchild1').\
495
join('parent', aliased=True).filter(Node.data=='child2').all()
496
SELECT nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.data AS nodes_data
497
FROM nodes JOIN nodes AS nodes_1 ON nodes_1.id = nodes.parent_id
498
WHERE nodes.data = ? AND nodes_1.data = ?
499
['subchild1', 'child2']
501
To add criterion to multiple points along a longer join, use ``from_joinpoint=True``:
503
.. sourcecode:: python+sql
505
# get all nodes named 'subchild1' with a parent named 'child2' and a grandparent 'root'
506
{sql}session.query(Node).filter(Node.data=='subchild1').\
507
join('parent', aliased=True).filter(Node.data=='child2').\
508
join('parent', aliased=True, from_joinpoint=True).filter(Node.data=='root').all()
509
SELECT nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.data AS nodes_data
510
FROM nodes JOIN nodes AS nodes_1 ON nodes_1.id = nodes.parent_id JOIN nodes AS nodes_2 ON nodes_2.id = nodes_1.parent_id
511
WHERE nodes.data = ? AND nodes_1.data = ? AND nodes_2.data = ?
343
However extra care is needed when attempting to join along
344
the foreign key from one level of the tree to the next. In SQL,
345
a join from a table to itself requires that at least one side of the
346
expression be "aliased" so that it can be unambiguously referred to.
348
Recall from :ref:`ormtutorial_aliases` in the ORM tutorial that the
349
:class:`.orm.aliased` construct is normally used to provide an "alias" of
350
an ORM entity. Joining from ``Node`` to itself using this technique
353
.. sourcecode:: python+sql
355
from sqlalchemy.orm import aliased
357
nodealias = aliased(Node)
358
{sql}session.query(Node).filter(Node.data=='subchild1').\
359
join(nodealias, Node.parent).\
360
filter(nodealias.data=="child2").\
362
SELECT node.id AS node_id,
363
node.parent_id AS node_parent_id,
364
node.data AS node_data
365
FROM node JOIN node AS node_1
366
ON node.parent_id = node_1.id
369
['subchild1', 'child2']
371
:meth:`.Query.join` also includes a feature known as ``aliased=True`` that
372
can shorten the verbosity self-referential joins, at the expense
373
of query flexibility. This feature
374
performs a similar "aliasing" step to that above, without the need for an
375
explicit entity. Calls to :meth:`.Query.filter` and similar subsequent to
376
the aliased join will **adapt** the ``Node`` entity to be that of the alias:
378
.. sourcecode:: python+sql
380
{sql}session.query(Node).filter(Node.data=='subchild1').\
381
join(Node.parent, aliased=True).\
382
filter(Node.data=='child2').\
384
SELECT node.id AS node_id,
385
node.parent_id AS node_parent_id,
386
node.data AS node_data
388
JOIN node AS node_1 ON node_1.id = node.parent_id
389
WHERE node.data = ? AND node_1.data = ?
390
['subchild1', 'child2']
392
To add criterion to multiple points along a longer join, add ``from_joinpoint=True``
393
to the additional :meth:`~.Query.join` calls:
395
.. sourcecode:: python+sql
397
# get all nodes named 'subchild1' with a
398
# parent named 'child2' and a grandparent 'root'
399
{sql}session.query(Node).\
400
filter(Node.data=='subchild1').\
401
join(Node.parent, aliased=True).\
402
filter(Node.data=='child2').\
403
join(Node.parent, aliased=True, from_joinpoint=True).\
404
filter(Node.data=='root').\
406
SELECT node.id AS node_id,
407
node.parent_id AS node_parent_id,
408
node.data AS node_data
410
JOIN node AS node_1 ON node_1.id = node.parent_id
411
JOIN node AS node_2 ON node_2.id = node_1.parent_id
512
415
['subchild1', 'child2', 'root']
514
Configuring Eager Loading
515
~~~~~~~~~~~~~~~~~~~~~~~~~~
417
:meth:`.Query.reset_joinpoint` will also remove the "aliasing" from filtering
420
session.query(Node).\
421
join(Node.children, aliased=True).\
422
filter(Node.data == 'foo').\
424
filter(Node.data == 'bar')
426
For an example of using ``aliased=True`` to arbitrarily join along a chain of self-referential
427
nodes, see :ref:`examples_xmlpersistence`.
429
Configuring Self-Referential Eager Loading
430
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
517
432
Eager loading of relationships occurs using joins or outerjoins from parent to
518
433
child table during a normal query operation, such that the parent and its
519
child collection can be populated from a single SQL statement, or a second
520
statement for all collections at once. SQLAlchemy's joined and subquery eager
521
loading uses aliased tables in all cases when joining to related items, so it
522
is compatible with self-referential joining. However, to use eager loading
523
with a self-referential relationship, SQLAlchemy needs to be told how many
524
levels deep it should join; otherwise the eager load will not take place. This
525
depth setting is configured via ``join_depth``:
434
immediate child collection or reference can be populated from a single SQL
435
statement, or a second statement for all immediate child collections.
436
SQLAlchemy's joined and subquery eager loading use aliased tables in all cases
437
when joining to related items, so are compatible with self-referential
438
joining. However, to use eager loading with a self-referential relationship,
439
SQLAlchemy needs to be told how many levels deep it should join and/or query;
440
otherwise the eager load will not take place at all. This depth setting is
441
configured via ``join_depth``:
527
443
.. sourcecode:: python+sql
529
mapper(Node, nodes, properties={
530
'children': relationship(Node, lazy='joined', join_depth=2)
446
__tablename__ = 'node'
447
id = Column(Integer, primary_key=True)
448
parent_id = Column(Integer, ForeignKey('node.id'))
449
data = Column(String(50))
450
children = relationship("Node",
533
454
{sql}session.query(Node).all()
534
SELECT nodes_1.id AS nodes_1_id, nodes_1.parent_id AS nodes_1_parent_id, nodes_1.data AS nodes_1_data, nodes_2.id AS nodes_2_id, nodes_2.parent_id AS nodes_2_parent_id, nodes_2.data AS nodes_2_data, nodes.id AS nodes_id, nodes.parent_id AS nodes_parent_id, nodes.data AS nodes_data
535
FROM nodes LEFT OUTER JOIN nodes AS nodes_2 ON nodes.id = nodes_2.parent_id LEFT OUTER JOIN nodes AS nodes_1 ON nodes_2.id = nodes_1.parent_id
455
SELECT node_1.id AS node_1_id,
456
node_1.parent_id AS node_1_parent_id,
457
node_1.data AS node_1_data,
458
node_2.id AS node_2_id,
459
node_2.parent_id AS node_2_parent_id,
460
node_2.data AS node_2_data,
462
node.parent_id AS node_parent_id,
463
node.data AS node_data
465
LEFT OUTER JOIN node AS node_2
466
ON node.id = node_2.parent_id
467
LEFT OUTER JOIN node AS node_1
468
ON node_2.id = node_1.parent_id
538
Linking relationships with Backref
471
.. _relationships_backref:
473
Linking Relationships with Backref
539
474
----------------------------------
541
476
The ``backref`` keyword argument was first introduced in :ref:`ormtutorial_toplevel`, and has been