From: Mike Bayer Date: Tue, 7 Apr 2020 21:37:14 +0000 (-0400) Subject: Backport documentation warnings for relationship eval() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9873b5e6c285c42acbb5485ad367da0f0c89c6d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Backport documentation warnings for relationship eval() Documentation that is taken from #5238. Change-Id: Id802f403190adfab0ca034afe2214ba10fd9cfbb (cherry picked from commit 11b2b645078ffb46217d493ef9f5aebdf79b8bfc) (cherry picked from commit e2fb6c97826c3b3f6a2d7bf6d2d57a7a192787c8) (cherry picked from commit 68f18033245387c98383c91e7a199e7951e6ef3a) --- diff --git a/doc/build/orm/basic_relationships.rst b/doc/build/orm/basic_relationships.rst index fe9aa04da5..2b03387878 100644 --- a/doc/build/orm/basic_relationships.rst +++ b/doc/build/orm/basic_relationships.rst @@ -240,6 +240,13 @@ is accepted as well, matching the name of the table as stored in ``Base.metadata secondary="association", backref="parents") +.. warning:: When passed as a Python-evaluable string, the + :paramref:`.relationship.secondary` argument is interpreted using Python's + ``eval()`` function. **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. See + :ref:`declarative_relationship_eval` for details on declarative + evaluation of :func:`.relationship` arguments. + + .. _relationships_many_to_many_deletion: Deleting Rows from the Many to Many Table diff --git a/doc/build/orm/extensions/declarative/relationships.rst b/doc/build/orm/extensions/declarative/relationships.rst index 1763344c76..d47e56c0c8 100644 --- a/doc/build/orm/extensions/declarative/relationships.rst +++ b/doc/build/orm/extensions/declarative/relationships.rst @@ -44,13 +44,21 @@ class using them:: user_id = Column(Integer, ForeignKey('users.id')) user = relationship(User, primaryjoin=user_id == User.id) +.. _declarative_relationship_eval: + +Evaluation of relationship arguments +===================================== + In addition to the main argument for :func:`~sqlalchemy.orm.relationship`, -other arguments which depend upon the columns present on an as-yet -undefined class may also be specified as strings. These strings are -evaluated as Python expressions. The full namespace available within -this evaluation includes all classes mapped for this declarative base, -as well as the contents of the ``sqlalchemy`` package, including -expression functions like :func:`~sqlalchemy.sql.expression.desc` and +other arguments which depend upon the columns present on an as-yet undefined +class may also be specified as strings. For all of these arguments +**including** the main argument prior to SQLAlchemy 1.3.16, these strings are +**evaluated as Python expressions using Python's built-in eval() function.** + +The full namespace available within this evaluation includes all classes mapped +for this declarative base, as well as the contents of the ``sqlalchemy`` +package, including expression functions like +:func:`~sqlalchemy.sql.expression.desc` and :attr:`~sqlalchemy.sql.expression.func`:: class User(Base): @@ -59,6 +67,31 @@ expression functions like :func:`~sqlalchemy.sql.expression.desc` and order_by="desc(Address.email)", primaryjoin="Address.user_id==User.id") +.. warning:: + + The strings accepted by the following parameters: + + :paramref:`.relationship.order_by` + + :paramref:`.relationship.primaryjoin` + + :paramref:`.relationship.secondaryjoin` + + :paramref:`.relationship.secondary` + + :paramref:`.relationship.remote_side` + + :paramref:`.relationship.foreign_keys` + + :paramref:`.relationship._user_defined_foreign_keys` + + Are **evaluated as Python code expressions using eval(). DO NOT PASS + UNTRUSTED INPUT TO THESE ARGUMENTS.** + + In addition, prior to version 1.3.16 of SQLAlchemy, the main + "argument" to :func:`.relationship` is also evaluated as Python + code. **DO NOT PASS UNTRUSTED INPUT TO THIS ARGUMENT.** + For the case where more than one module contains a class of the same name, string class names can also be specified as module-qualified paths within any of these string expressions:: diff --git a/doc/build/orm/join_conditions.rst b/doc/build/orm/join_conditions.rst index c14a927c26..95c5367340 100644 --- a/doc/build/orm/join_conditions.rst +++ b/doc/build/orm/join_conditions.rst @@ -96,6 +96,13 @@ one :class:`.Column` we need:: billing_address = relationship("Address", foreign_keys="Customer.billing_address_id") +.. warning:: When passed as a Python-evaluable string, the + :paramref:`.relationship.foreign_keys` argument is interpreted using Python's + ``eval()`` function. **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. See + :ref:`declarative_relationship_eval` for details on declarative + evaluation of :func:`.relationship` arguments. + + .. _relationship_primaryjoin: Specifying Alternate Join Conditions @@ -138,12 +145,21 @@ load those ``Address`` objects which specify a city of "Boston":: state = Column(String) zip = Column(String) -Within this string SQL expression, we made use of the :func:`.and_` conjunction construct to establish -two distinct predicates for the join condition - joining both the ``User.id`` and -``Address.user_id`` columns to each other, as well as limiting rows in ``Address`` -to just ``city='Boston'``. When using Declarative, rudimentary SQL functions like -:func:`.and_` are automatically available in the evaluated namespace of a string -:func:`.relationship` argument. +Within this string SQL expression, we made use of the :func:`.and_` conjunction +construct to establish two distinct predicates for the join condition - joining +both the ``User.id`` and ``Address.user_id`` columns to each other, as well as +limiting rows in ``Address`` to just ``city='Boston'``. When using +Declarative, rudimentary SQL functions like :func:`.and_` are automatically +available in the evaluated namespace of a string :func:`.relationship` +argument. + +.. warning:: When passed as a Python-evaluable string, the + :paramref:`.relationship.primaryjoin` argument is interpreted using + Python's + ``eval()`` function. **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. See + :ref:`declarative_relationship_eval` for details on declarative + evaluation of :func:`.relationship` arguments. + The custom criteria we use in a :paramref:`~.relationship.primaryjoin` is generally only significant when SQLAlchemy is rendering SQL in @@ -518,6 +534,14 @@ use the string name of the table as it is present in the :class:`.MetaData`:: backref="left_nodes" ) +.. warning:: When passed as a Python-evaluable string, the + :paramref:`.relationship.primaryjoin` and + :paramref:`.relationship.secondaryjoin` arguments are interpreted using + Python's ``eval()`` function. **DO NOT PASS UNTRUSTED INPUT TO THESE + STRINGS**. See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + + A classical mapping situation here is similar, where ``node_to_node`` can be joined to ``node.c.id``:: diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index c4908c7ff5..6de66f0f2b 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -169,7 +169,13 @@ class RelationshipProperty(StrategizedProperty): :paramref:`~.relationship.argument` may also be passed as a callable function which is evaluated at mapper initialization time, and may - be passed as a Python-evaluable string when using Declarative. + be passed as a string name when using Declarative. + + .. warning:: Prior to SQLAlchemy 1.3.16, this value is interpreted + using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. .. seealso:: @@ -189,6 +195,12 @@ class RelationshipProperty(StrategizedProperty): present in the :class:`.MetaData` collection associated with the parent-mapped :class:`.Table`. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + The :paramref:`~.relationship.secondary` keyword argument is typically applied in the case where the intermediary :class:`.Table` is not otherwise expressed in any direct class mapping. If the @@ -443,6 +455,12 @@ class RelationshipProperty(StrategizedProperty): and may be passed as a Python-evaluable string when using Declarative. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + .. seealso:: :ref:`relationship_foreign_keys` @@ -595,6 +613,12 @@ class RelationshipProperty(StrategizedProperty): function which is evaluated at mapper initialization time, and may be passed as a Python-evaluable string when using Declarative. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + :param passive_deletes=False: Indicates loading behavior during delete operations. @@ -687,6 +711,12 @@ class RelationshipProperty(StrategizedProperty): and may be passed as a Python-evaluable string when using Declarative. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + .. seealso:: :ref:`relationship_primaryjoin` @@ -700,6 +730,12 @@ class RelationshipProperty(StrategizedProperty): and may be passed as a Python-evaluable string when using Declarative. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + .. seealso:: :ref:`self_referential` - in-depth explanation of how @@ -734,6 +770,12 @@ class RelationshipProperty(StrategizedProperty): and may be passed as a Python-evaluable string when using Declarative. + .. warning:: When passed as a Python-evaluable string, the + argument is interpreted using Python's ``eval()`` function. + **DO NOT PASS UNTRUSTED INPUT TO THIS STRING**. + See :ref:`declarative_relationship_eval` for details on + declarative evaluation of :func:`.relationship` arguments. + .. seealso:: :ref:`relationship_primaryjoin`