summaryrefslogtreecommitdiff
path: root/doc/build/orm/backref.rst
blob: 01f4c90736d5564c1550cafda5ba48b69f4ee204 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
.. _relationships_backref:

Using the legacy 'backref' relationship parameter
--------------------------------------------------

.. note:: The :paramref:`_orm.relationship.backref` keyword should be considered
   legacy, and use of :paramref:`_orm.relationship.back_populates` with explicit
   :func:`_orm.relationship` constructs should be preferred.  Using
   individual :func:`_orm.relationship` constructs provides advantages
   including that both ORM mapped classes will include their attributes
   up front as the class is constructed, rather than as a deferred step,
   and configuration is more straightforward as all arguments are explicit.
   New :pep:`484` features in SQLAlchemy 2.0 also take advantage of
   attributes being explicitly present in source code rather than
   using dynamic attribute generation.

.. seealso::

    For general information about bidirectional relationships, see the
    following sections:

    :ref:`tutorial_orm_related_objects` - in the :ref:`unified_tutorial`,
    presents an overview of bi-directional relationship configuration
    and behaviors using :paramref:`_orm.relationship.back_populates`

    :ref:`back_populates_cascade` - notes on bi-directional :func:`_orm.relationship`
    behavior regarding :class:`_orm.Session` cascade behaviors.

    :paramref:`_orm.relationship.back_populates`


The :paramref:`_orm.relationship.backref` keyword argument on the
:func:`_orm.relationship` construct allows the
automatic generation of a new :func:`_orm.relationship` that will be automatically
be added to the ORM mapping for the related class.  It will then be
placed into a :paramref:`_orm.relationship.back_populates` configuration
against the current :func:`_orm.relationship` being configured, with both
:func:`_orm.relationship` constructs referring to each other.

Starting with the following example::

    from sqlalchemy import Column, ForeignKey, Integer, String
    from sqlalchemy.orm import DeclarativeBase, relationship


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"
        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String)

        addresses = relationship("Address", backref="user")


    class Address(Base):
        __tablename__ = "address"
        id = mapped_column(Integer, primary_key=True)
        email = mapped_column(String)
        user_id = mapped_column(Integer, ForeignKey("user.id"))

The above configuration establishes a collection of ``Address`` objects on ``User`` called
``User.addresses``.   It also establishes a ``.user`` attribute on ``Address`` which will
refer to the parent ``User`` object.   Using :paramref:`_orm.relationship.back_populates`
it's equivalent to the following::

    from sqlalchemy import Column, ForeignKey, Integer, String
    from sqlalchemy.orm import DeclarativeBase, relationship


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"
        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String)

        addresses = relationship("Address", back_populates="user")


    class Address(Base):
        __tablename__ = "address"
        id = mapped_column(Integer, primary_key=True)
        email = mapped_column(String)
        user_id = mapped_column(Integer, ForeignKey("user.id"))

        user = relationship("User", back_populates="addresses")

The behavior of the ``User.addresses`` and ``Address.user`` relationships
is that they now behave in a **bi-directional** way, indicating that
changes on one side of the relationship impact the other.   An example
and discussion of this behavior is in the :ref:`unified_tutorial`
at :ref:`tutorial_orm_related_objects`.


Backref Default Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~

Since :paramref:`_orm.relationship.backref` generates a whole new
:func:`_orm.relationship`, the generation process by default
will attempt to include corresponding arguments in the new
:func:`_orm.relationship` that correspond to the original arguments.
As an example, below is a :func:`_orm.relationship` that includes a
:ref:`custom join condition <relationship_configure_joins>`
which also includes the :paramref:`_orm.relationship.backref` keyword::

    from sqlalchemy import Column, ForeignKey, Integer, String
    from sqlalchemy.orm import DeclarativeBase, relationship


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"
        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String)

        addresses = relationship(
            "Address",
            primaryjoin=(
                "and_(User.id==Address.user_id, Address.email.startswith('tony'))"
            ),
            backref="user",
        )


    class Address(Base):
        __tablename__ = "address"
        id = mapped_column(Integer, primary_key=True)
        email = mapped_column(String)
        user_id = mapped_column(Integer, ForeignKey("user.id"))

When the "backref" is generated, the :paramref:`_orm.relationship.primaryjoin`
condition is copied to the new :func:`_orm.relationship` as well::

    >>> print(User.addresses.property.primaryjoin)
    "user".id = address.user_id AND address.email LIKE :email_1 || '%%'
    >>>
    >>> print(Address.user.property.primaryjoin)
    "user".id = address.user_id AND address.email LIKE :email_1 || '%%'
    >>>

Other arguments that are transferrable include the
:paramref:`_orm.relationship.secondary` parameter that refers to a
many-to-many association table, as well as the "join" arguments
:paramref:`_orm.relationship.primaryjoin` and
:paramref:`_orm.relationship.secondaryjoin`; "backref" is smart enough to know
that these two arguments should also be "reversed" when generating
the opposite side.

Specifying Backref Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lots of other arguments for a "backref" are not implicit, and
include arguments like
:paramref:`_orm.relationship.lazy`,
:paramref:`_orm.relationship.remote_side`,
:paramref:`_orm.relationship.cascade` and
:paramref:`_orm.relationship.cascade_backrefs`.   For this case we use
the :func:`.backref` function in place of a string; this will store
a specific set of arguments that will be transferred to the new
:func:`_orm.relationship` when generated::

    # <other imports>
    from sqlalchemy.orm import backref


    class User(Base):
        __tablename__ = "user"
        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String)

        addresses = relationship(
            "Address",
            backref=backref("user", lazy="joined"),
        )

Where above, we placed a ``lazy="joined"`` directive only on the ``Address.user``
side, indicating that when a query against ``Address`` is made, a join to the ``User``
entity should be made automatically which will populate the ``.user`` attribute of each
returned ``Address``.   The :func:`.backref` function formatted the arguments we gave
it into a form that is interpreted by the receiving :func:`_orm.relationship` as additional
arguments to be applied to the new relationship it creates.