.. _relationships_backref:
-Linking Relationships with Backref
-----------------------------------
+Using the legacy 'backref' relationship parameter
+--------------------------------------------------
-The :paramref:`_orm.relationship.backref` keyword argument was first introduced in :ref:`ormtutorial_toplevel`, and has been
-mentioned throughout many of the examples here. What does it actually do ? Let's start
-with the canonical ``User`` and ``Address`` scenario::
+.. 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 declarative_base, relationship
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.
-
-In fact, the :paramref:`_orm.relationship.backref` keyword is only a common shortcut for placing a second
-:func:`_orm.relationship` onto the ``Address`` mapping, including the establishment
-of an event listener on both sides which will mirror attribute operations
-in both directions. The above configuration is equivalent to::
+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 declarative_base, relationship
user = relationship("User", back_populates="addresses")
-Above, we add a ``.user`` relationship to ``Address`` explicitly. On
-both relationships, the :paramref:`_orm.relationship.back_populates` directive tells each relationship
-about the other one, indicating that they should establish "bidirectional"
-behavior between each other. The primary effect of this configuration
-is that the relationship adds event handlers to both attributes
-which have the behavior of "when an append or set event occurs here, set ourselves
-onto the incoming attribute using this particular attribute name".
-The behavior is illustrated as follows. Start with a ``User`` and an ``Address``
-instance. The ``.addresses`` collection is empty, and the ``.user`` attribute
-is ``None``::
-
- >>> u1 = User()
- >>> a1 = Address()
- >>> u1.addresses
- []
- >>> print(a1.user)
- None
-
-However, once the ``Address`` is appended to the ``u1.addresses`` collection,
-both the collection and the scalar attribute have been populated::
-
- >>> u1.addresses.append(a1)
- >>> u1.addresses
- [<__main__.Address object at 0x12a6ed0>]
- >>> a1.user
- <__main__.User object at 0x12a6590>
-
-This behavior of course works in reverse for removal operations as well, as well
-as for equivalent operations on both sides. Such as
-when ``.user`` is set again to ``None``, the ``Address`` object is removed
-from the reverse collection::
-
- >>> a1.user = None
- >>> u1.addresses
- []
-
-The manipulation of the ``.addresses`` collection and the ``.user`` attribute
-occurs entirely in Python without any interaction with the SQL database.
-Without this behavior, the proper state would be apparent on both sides once the
-data has been flushed to the database, and later reloaded after a commit or
-expiration operation occurs. The :paramref:`_orm.relationship.backref`/:paramref:`_orm.relationship.back_populates` behavior has the advantage
-that common bidirectional operations can reflect the correct state without requiring
-a database round trip.
-
-Remember, when the :paramref:`_orm.relationship.backref` keyword is used on a single relationship, it's
-exactly the same as if the above two relationships were created individually
-using :paramref:`_orm.relationship.back_populates` on each.
-
-Backref Arguments
-~~~~~~~~~~~~~~~~~
-
-We've established that the :paramref:`_orm.relationship.backref` keyword is merely a shortcut for building
-two individual :func:`_orm.relationship` constructs that refer to each other. Part of
-the behavior of this shortcut is that certain configurational arguments applied to
-the :func:`_orm.relationship`
-will also be applied to the other direction - namely those arguments that describe
-the relationship at a schema level, and are unlikely to be different in the reverse
-direction. The usual case
-here is a many-to-many :func:`_orm.relationship` that has a :paramref:`_orm.relationship.secondary` argument,
-or a one-to-many or many-to-one which has a :paramref:`_orm.relationship.primaryjoin` argument (the
-:paramref:`_orm.relationship.primaryjoin` argument is discussed in :ref:`relationship_primaryjoin`). Such
-as if we limited the list of ``Address`` objects to those which start with "tony"::
+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 declarative_base, relationship
email = Column(String)
user_id = Column(Integer, ForeignKey("user.id"))
-We can observe, by inspecting the resulting property, that both sides
-of the relationship have this join condition applied::
+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 || '%%'
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>
-This reuse of arguments should pretty much do the "right thing" - it
-uses only arguments that are applicable, and in the case of a many-to-
-many relationship, will reverse the usage of
+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` to correspond to the other
-direction (see the example in :ref:`self_referential_many_to_many` for
-this).
+: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
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-It's very often the case however that we'd like to specify arguments
-that are specific to just the side where we happened to place the
-"backref". This includes :func:`_orm.relationship` arguments like
+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::
+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
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.
-
-Cascade behavior for backrefs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-It's important to note that even though a bi-directional relationship
-may be manipulated from either direction, having the same end effect on the
-object structure produced, there is a significant difference in how the
-:ref:`save-update cascade <cascade_save_update>` behaves for two objects
-where one is attached and the other is unattached to a :class:`_orm.Session`,
-depending on the direction in which the relationships are manipulated.
-
-The ``save-update`` cascade will only take effect **uni-directionally**
-in the direction from a parent object that is already associated with a
-:class:`_orm.Session`, towards an object that is being associated with that
-parent directly via an attribute or collection on that parent. It won't
-take effect if the parent object is instead assigned to an attribute or
-collection on the child, in which case the unattached parent object should be
-added to the :class:`_orm.Session` explicitly using :meth:`_orm.Session.add`.
-
-For a complete example of how this looks in practice, see the section
-:ref:`backref_cascade`.
-
-
-One Way Backrefs
-~~~~~~~~~~~~~~~~
-
-An unusual case is that of the "one way backref". This is where the
-"back-populating" behavior of the backref is only desirable in one
-direction. An example of this is a collection which contains a
-filtering :paramref:`_orm.relationship.primaryjoin` condition. We'd
-like to append items to this collection as needed, and have them
-populate the "parent" object on the incoming object. However, we'd
-also like to have items that are not part of the collection, but still
-have the same "parent" association - these items should never be in
-the collection.
-
-Taking our previous example, where we established a
-:paramref:`_orm.relationship.primaryjoin` that limited the collection
-only to ``Address`` objects whose email address started with the word
-``tony``, the usual backref behavior is that all items populate in
-both directions. We wouldn't want this behavior for a case like the
-following::
-
- >>> u1 = User()
- >>> a1 = Address(email='mary')
- >>> a1.user = u1
- >>> u1.addresses
- [<__main__.Address object at 0x1411910>]
-
-Above, the ``Address`` object that doesn't match the criterion of "starts with 'tony'"
-is present in the ``addresses`` collection of ``u1``. After these objects are flushed,
-the transaction committed and their attributes expired for a re-load, the ``addresses``
-collection will hit the database on next access and no longer have this ``Address`` object
-present, due to the filtering condition. But we can do away with this unwanted side
-of the "backref" behavior on the Python side by using two separate :func:`_orm.relationship` constructs,
-placing :paramref:`_orm.relationship.back_populates` only on one side::
-
- from sqlalchemy import Column, ForeignKey, Integer, String
- from sqlalchemy.orm import declarative_base, relationship
-
- Base = declarative_base()
-
-
- class User(Base):
- __tablename__ = "user"
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
- addresses = relationship(
- "Address",
- primaryjoin="and_(User.id==Address.user_id, "
- "Address.email.startswith('tony'))",
- back_populates="user",
- )
-
-
- class Address(Base):
- __tablename__ = "address"
- id = Column(Integer, primary_key=True)
- email = Column(String)
- user_id = Column(Integer, ForeignKey("user.id"))
-
- user = relationship("User")
-
-With the above scenario, appending an ``Address`` object to the ``.addresses``
-collection of a ``User`` will always establish the ``.user`` attribute on that
-``Address``::
-
- >>> u1 = User()
- >>> a1 = Address(email='tony')
- >>> u1.addresses.append(a1)
- >>> a1.user
- <__main__.User object at 0x1411850>
-
-However, applying a ``User`` to the ``.user`` attribute of an ``Address``,
-will not append the ``Address`` object to the collection::
-
- >>> a2 = Address(email='mary')
- >>> a2.user = u1
- >>> a2 in u1.addresses
- False
-
-Of course, we've disabled some of the usefulness of
-:paramref:`_orm.relationship.backref` here, in that when we do append an
-``Address`` that corresponds to the criteria of
-``email.startswith('tony')``, it won't show up in the
-``User.addresses`` collection until the session is flushed, and the
-attributes reloaded after a commit or expire operation. While we
-could consider an attribute event that checks this criterion in
-Python, this starts to cross the line of duplicating too much SQL
-behavior in Python. The backref behavior itself is only a slight
-transgression of this philosophy - SQLAlchemy tries to keep these to a
-minimum overall.
the "previous" value of the attribute.
:param backref:
- Indicates the string name of a property to be placed on the related
- mapper's class that will handle this relationship in the other
- direction. The other property will be created automatically
- when the mappers are configured. Can also be passed as a
- :func:`.backref` object to control the configuration of the
- new relationship.
+ A reference to a string relationship name, or a :func:`_orm.backref`
+ construct, which will be used to automatically generate a new
+ :func:`_orm.relationship` on the related class, which then refers to this
+ one using a bi-directional :paramref:`_orm.relationship.back_populates`
+ configuration.
+
+ In modern Python, explicit use of :func:`_orm.relationship`
+ with :paramref:`_orm.relationship.back_populates` should be preferred,
+ as it is more robust in terms of mapper configuration as well as
+ more conceptually straightforward. It also integrates with
+ new :pep:`484` typing features introduced in SQLAlchemy 2.0 which
+ is not possible with dynamically generated attributes.
.. seealso::
- :ref:`relationships_backref` - Introductory documentation and
- examples.
+ :ref:`relationships_backref` - notes on using
+ :paramref:`_orm.relationship.backref`
- :paramref:`_orm.relationship.back_populates` - alternative form
- of backref specification.
+ :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`
:func:`.backref` - allows control over :func:`_orm.relationship`
configuration when using :paramref:`_orm.relationship.backref`.
:param back_populates:
- Takes a string name and has the same meaning as
- :paramref:`_orm.relationship.backref`, except the complementing
- property is **not** created automatically, and instead must be
- configured explicitly on the other mapper. The complementing
- property should also indicate
- :paramref:`_orm.relationship.back_populates` to this relationship to
- ensure proper functioning.
+ Indicates the name of a :func:`_orm.relationship` on the related
+ class that will be synchronized with this one. It is usually
+ expected that the :func:`_orm.relationship` on the related class
+ also refer to this one. This allows objects on both sides of
+ each :func:`_orm.relationship` to synchronize in-Python state
+ changes and also provides directives to the :term:`unit of work`
+ flush process how changes along these relationships should
+ be persisted.
.. seealso::
- :ref:`relationships_backref` - Introductory documentation and
- examples.
+ :ref:`tutorial_orm_related_objects` - in the :ref:`unified_tutorial`,
+ presents an overview of bi-directional relationship configuration
+ and behaviors.
- :paramref:`_orm.relationship.backref` - alternative form
- of backref specification.
+ :ref:`relationship_patterns` - includes many examples of
+ :paramref:`_orm.relationship.back_populates`.
:param overlaps:
A string name or comma-delimited set of names of other relationships
def backref(name: str, **kwargs: Any) -> _ORMBackrefArgument:
- """Create a back reference with explicit keyword arguments, which are the
- same arguments one can send to :func:`relationship`.
+ """When using the :paramref:`_orm.relationship.backref` parameter,
+ provides specific parameters to be used when the new
+ :func:`_orm.relationship` is generated.
- Used with the ``backref`` keyword argument to :func:`relationship` in
- place of a string argument, e.g.::
+ E.g.::
'items':relationship(
SomeItem, backref=backref('parent', lazy='subquery'))
+ The :paramref:`_orm.relationship.backref` parameter is generally
+ considered to be legacy; for modern applications, using
+ explicit :func:`_orm.relationship` constructs linked together using
+ the :paramref:`_orm.relationship.back_populates` parameter should be
+ preferred.
+
.. seealso::
- :ref:`relationships_backref`
+ :ref:`relationships_backref` - background on backrefs
"""