Another use case for relationships is the use of custom operators, such
as PostgreSQL's "is contained within" ``<<`` operator when joining with
types such as :class:`_postgresql.INET` and :class:`_postgresql.CIDR`.
-For custom operators we use the :meth:`.Operators.op` function::
+For custom boolean operators we use the :meth:`.Operators.bool_op` function::
- inet_column.op("<<")(cidr_column)
+ inet_column.bool_op("<<")(cidr_column)
-However, if we construct a :paramref:`_orm.relationship.primaryjoin` using this
-operator, :func:`_orm.relationship` will still need more information. This is because
-when it examines our primaryjoin condition, it specifically looks for operators
-used for **comparisons**, and this is typically a fixed list containing known
-comparison operators such as ``==``, ``<``, etc. So for our custom operator
-to participate in this system, we need it to register as a comparison operator
-using the :paramref:`~.Operators.op.is_comparison` parameter::
-
- inet_column.op("<<", is_comparison=True)(cidr_column)
-
-A complete example::
+A comparison like the above may be used directly with
+:paramref:`_orm.relationship.primaryjoin` when constructing
+a :func:`_orm.relationship`::
class IPA(Base):
__tablename__ = 'ip_address'
v4address = Column(INET)
network = relationship("Network",
- primaryjoin="IPA.v4address.op('<<', is_comparison=True)"
+ primaryjoin="IPA.v4address.bool_op('<<')"
"(foreign(Network.v4representation))",
viewonly=True
)
SELECT ip_address.id AS ip_address_id, ip_address.v4address AS ip_address_v4address
FROM ip_address JOIN network ON ip_address.v4address << network.v4representation
-.. versionadded:: 0.9.2 - Added the :paramref:`.Operators.op.is_comparison`
- flag to assist in the creation of :func:`_orm.relationship` constructs using
- custom operators.
+.. versionadded:: 2.0 Added :meth:`.Operators.bool_op` to generate operators
+ that are inherently "boolean" when used in expressions and ORM join
+ conditions.
.. _relationship_custom_operator_sql_function:
A value of 100 will be higher or equal to all operators, and -100
will be lower than or equal to all operators.
- :param is_comparison: if True, the operator will be considered as a
- "comparison" operator, that is which evaluates to a boolean
- true/false value, like ``==``, ``>``, etc. This flag should be set
+ :param is_comparison: legacy; if True, the operator will be considered
+ as a "comparison" operator, that is which evaluates to a boolean
+ true/false value, like ``==``, ``>``, etc. This flag is provided
so that ORM relationships can establish that the operator is a
comparison operator when used in a custom join condition.
- .. versionadded:: 0.9.2 - added the
- :paramref:`.Operators.op.is_comparison` flag.
+ Using the ``is_comparison`` parameter is superseded by using the
+ :meth:`.Operators.bool_op` method instead; this more succinct
+ operator sets this parameter automatically, but also provides
+ correct :pep:`484` typing support as the returned object will
+ express a "boolean" datatype, i.e. ``BinaryExpression[bool]``.
:param return_type: a :class:`.TypeEngine` class or object that will
force the return type of an expression produced by this operator
.. seealso::
+ :meth:`.Operators.bool_op`
+
:ref:`types_operators`
:ref:`relationship_custom_operator`
This method is shorthand for calling
:meth:`.Operators.op` and passing the
:paramref:`.Operators.op.is_comparison`
- flag with True.
+ flag with True. A key advantage to using :meth:`.Operators.bool_op`
+ is that when using column constructs, the "boolean" nature of the
+ returned expression will be present for :pep:`484` purposes.
.. seealso::
Column("foo", String(50)),
)
- def test_join_on_custom_op(self):
+ def test_join_on_custom_op_legacy_is_comparison(self):
class A(fixtures.BasicEntity):
pass
"FROM a JOIN b ON a.foo &* b.foo",
)
+ def test_join_on_custom_bool_op(self):
+ class A(fixtures.BasicEntity):
+ pass
+
+ class B(fixtures.BasicEntity):
+ pass
+
+ self.mapper_registry.map_imperatively(
+ A,
+ self.tables.a,
+ properties={
+ "bs": relationship(
+ B,
+ primaryjoin=self.tables.a.c.foo.bool_op("&*")(
+ foreign(self.tables.b.c.foo)
+ ),
+ viewonly=True,
+ )
+ },
+ )
+ self.mapper_registry.map_imperatively(B, self.tables.b)
+ self.assert_compile(
+ fixture_session().query(A).join(A.bs),
+ "SELECT a.id AS a_id, a.foo AS a_foo "
+ "FROM a JOIN b ON a.foo &* b.foo",
+ )
+
class ViewOnlyHistoryTest(fixtures.MappedTest):
@classmethod