You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1201 lines
				
				41 KiB
			
		
		
			
		
	
	
					1201 lines
				
				41 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# ext/hybrid.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								r"""Define attributes on ORM-mapped classes that have "hybrid" behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"hybrid" means the attribute has distinct behaviors defined at the
							 | 
						||
| 
								 | 
							
								class level and at the instance level.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :mod:`~sqlalchemy.ext.hybrid` extension provides a special form of
							 | 
						||
| 
								 | 
							
								method decorator, is around 50 lines of code and has almost no
							 | 
						||
| 
								 | 
							
								dependencies on the rest of SQLAlchemy.  It can, in theory, work with
							 | 
						||
| 
								 | 
							
								any descriptor-based expression system.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Consider a mapping ``Interval``, representing integer ``start`` and ``end``
							 | 
						||
| 
								 | 
							
								values. We can define higher level functions on mapped classes that produce SQL
							 | 
						||
| 
								 | 
							
								expressions at the class level, and Python expression evaluation at the
							 | 
						||
| 
								 | 
							
								instance level.  Below, each function decorated with :class:`.hybrid_method` or
							 | 
						||
| 
								 | 
							
								:class:`.hybrid_property` may receive ``self`` as an instance of the class, or
							 | 
						||
| 
								 | 
							
								as the class itself::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column, Integer
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import Session, aliased
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Interval(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'interval'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        start = Column(Integer, nullable=False)
							 | 
						||
| 
								 | 
							
								        end = Column(Integer, nullable=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, start, end):
							 | 
						||
| 
								 | 
							
								            self.start = start
							 | 
						||
| 
								 | 
							
								            self.end = end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def length(self):
							 | 
						||
| 
								 | 
							
								            return self.end - self.start
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_method
							 | 
						||
| 
								 | 
							
								        def contains(self, point):
							 | 
						||
| 
								 | 
							
								            return (self.start <= point) & (point <= self.end)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_method
							 | 
						||
| 
								 | 
							
								        def intersects(self, other):
							 | 
						||
| 
								 | 
							
								            return self.contains(other.start) | self.contains(other.end)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, the ``length`` property returns the difference between the
							 | 
						||
| 
								 | 
							
								``end`` and ``start`` attributes.  With an instance of ``Interval``,
							 | 
						||
| 
								 | 
							
								this subtraction occurs in Python, using normal Python descriptor
							 | 
						||
| 
								 | 
							
								mechanics::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> i1 = Interval(5, 10)
							 | 
						||
| 
								 | 
							
								    >>> i1.length
							 | 
						||
| 
								 | 
							
								    5
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When dealing with the ``Interval`` class itself, the :class:`.hybrid_property`
							 | 
						||
| 
								 | 
							
								descriptor evaluates the function body given the ``Interval`` class as
							 | 
						||
| 
								 | 
							
								the argument, which when evaluated with SQLAlchemy expression mechanics
							 | 
						||
| 
								 | 
							
								(here using the :attr:`.QueryableAttribute.expression` accessor)
							 | 
						||
| 
								 | 
							
								returns a new SQL expression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Interval.length.expression)
							 | 
						||
| 
								 | 
							
								    interval."end" - interval.start
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(Interval).filter(Interval.length > 10))
							 | 
						||
| 
								 | 
							
								    SELECT interval.id AS interval_id, interval.start AS interval_start,
							 | 
						||
| 
								 | 
							
								    interval."end" AS interval_end
							 | 
						||
| 
								 | 
							
								    FROM interval
							 | 
						||
| 
								 | 
							
								    WHERE interval."end" - interval.start > :param_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ORM methods such as :meth:`_query.Query.filter_by`
							 | 
						||
| 
								 | 
							
								generally use ``getattr()`` to
							 | 
						||
| 
								 | 
							
								locate attributes, so can also be used with hybrid attributes::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(Interval).filter_by(length=5))
							 | 
						||
| 
								 | 
							
								    SELECT interval.id AS interval_id, interval.start AS interval_start,
							 | 
						||
| 
								 | 
							
								    interval."end" AS interval_end
							 | 
						||
| 
								 | 
							
								    FROM interval
							 | 
						||
| 
								 | 
							
								    WHERE interval."end" - interval.start = :param_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``Interval`` class example also illustrates two methods,
							 | 
						||
| 
								 | 
							
								``contains()`` and ``intersects()``, decorated with
							 | 
						||
| 
								 | 
							
								:class:`.hybrid_method`. This decorator applies the same idea to
							 | 
						||
| 
								 | 
							
								methods that :class:`.hybrid_property` applies to attributes.   The
							 | 
						||
| 
								 | 
							
								methods return boolean values, and take advantage of the Python ``|``
							 | 
						||
| 
								 | 
							
								and ``&`` bitwise operators to produce equivalent instance-level and
							 | 
						||
| 
								 | 
							
								SQL expression-level boolean behavior::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> i1.contains(6)
							 | 
						||
| 
								 | 
							
								    True
							 | 
						||
| 
								 | 
							
								    >>> i1.contains(15)
							 | 
						||
| 
								 | 
							
								    False
							 | 
						||
| 
								 | 
							
								    >>> i1.intersects(Interval(7, 18))
							 | 
						||
| 
								 | 
							
								    True
							 | 
						||
| 
								 | 
							
								    >>> i1.intersects(Interval(25, 29))
							 | 
						||
| 
								 | 
							
								    False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(Interval).filter(Interval.contains(15)))
							 | 
						||
| 
								 | 
							
								    SELECT interval.id AS interval_id, interval.start AS interval_start,
							 | 
						||
| 
								 | 
							
								    interval."end" AS interval_end
							 | 
						||
| 
								 | 
							
								    FROM interval
							 | 
						||
| 
								 | 
							
								    WHERE interval.start <= :start_1 AND interval."end" > :end_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> ia = aliased(Interval)
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(Interval, ia).filter(Interval.intersects(ia)))
							 | 
						||
| 
								 | 
							
								    SELECT interval.id AS interval_id, interval.start AS interval_start,
							 | 
						||
| 
								 | 
							
								    interval."end" AS interval_end, interval_1.id AS interval_1_id,
							 | 
						||
| 
								 | 
							
								    interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end
							 | 
						||
| 
								 | 
							
								    FROM interval, interval AS interval_1
							 | 
						||
| 
								 | 
							
								    WHERE interval.start <= interval_1.start
							 | 
						||
| 
								 | 
							
								        AND interval."end" > interval_1.start
							 | 
						||
| 
								 | 
							
								        OR interval.start <= interval_1."end"
							 | 
						||
| 
								 | 
							
								        AND interval."end" > interval_1."end"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _hybrid_distinct_expression:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Defining Expression Behavior Distinct from Attribute Behavior
							 | 
						||
| 
								 | 
							
								--------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Our usage of the ``&`` and ``|`` bitwise operators above was
							 | 
						||
| 
								 | 
							
								fortunate, considering our functions operated on two boolean values to
							 | 
						||
| 
								 | 
							
								return a new one.   In many cases, the construction of an in-Python
							 | 
						||
| 
								 | 
							
								function and a SQLAlchemy SQL expression have enough differences that
							 | 
						||
| 
								 | 
							
								two separate Python expressions should be defined.  The
							 | 
						||
| 
								 | 
							
								:mod:`~sqlalchemy.ext.hybrid` decorators define the
							 | 
						||
| 
								 | 
							
								:meth:`.hybrid_property.expression` modifier for this purpose.   As an
							 | 
						||
| 
								 | 
							
								example we'll define the radius of the interval, which requires the
							 | 
						||
| 
								 | 
							
								usage of the absolute value function::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import func
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Interval(object):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def radius(self):
							 | 
						||
| 
								 | 
							
								            return abs(self.length) / 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @radius.expression
							 | 
						||
| 
								 | 
							
								        def radius(cls):
							 | 
						||
| 
								 | 
							
								            return func.abs(cls.length) / 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above the Python function ``abs()`` is used for instance-level
							 | 
						||
| 
								 | 
							
								operations, the SQL function ``ABS()`` is used via the :data:`.func`
							 | 
						||
| 
								 | 
							
								object for class-level expressions::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> i1.radius
							 | 
						||
| 
								 | 
							
								    2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(Interval).filter(Interval.radius > 5))
							 | 
						||
| 
								 | 
							
								    SELECT interval.id AS interval_id, interval.start AS interval_start,
							 | 
						||
| 
								 | 
							
								        interval."end" AS interval_end
							 | 
						||
| 
								 | 
							
								    FROM interval
							 | 
						||
| 
								 | 
							
								    WHERE abs(interval."end" - interval.start) / :abs_1 > :param_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note:: When defining an expression for a hybrid property or method, the
							 | 
						||
| 
								 | 
							
								   expression method **must** retain the name of the original hybrid, else
							 | 
						||
| 
								 | 
							
								   the new hybrid with the additional state will be attached to the class
							 | 
						||
| 
								 | 
							
								   with the non-matching name. To use the example above::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Interval(object):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def radius(self):
							 | 
						||
| 
								 | 
							
								            return abs(self.length) / 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # WRONG - the non-matching name will cause this function to be
							 | 
						||
| 
								 | 
							
								        # ignored
							 | 
						||
| 
								 | 
							
								        @radius.expression
							 | 
						||
| 
								 | 
							
								        def radius_expression(cls):
							 | 
						||
| 
								 | 
							
								            return func.abs(cls.length) / 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   This is also true for other mutator methods, such as
							 | 
						||
| 
								 | 
							
								   :meth:`.hybrid_property.update_expression`. This is the same behavior
							 | 
						||
| 
								 | 
							
								   as that of the ``@property`` construct that is part of standard Python.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Defining Setters
							 | 
						||
| 
								 | 
							
								----------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Hybrid properties can also define setter methods.  If we wanted
							 | 
						||
| 
								 | 
							
								``length`` above, when set, to modify the endpoint value::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Interval(object):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def length(self):
							 | 
						||
| 
								 | 
							
								            return self.end - self.start
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @length.setter
							 | 
						||
| 
								 | 
							
								        def length(self, value):
							 | 
						||
| 
								 | 
							
								            self.end = self.start + value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``length(self, value)`` method is now called upon set::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> i1 = Interval(5, 10)
							 | 
						||
| 
								 | 
							
								    >>> i1.length
							 | 
						||
| 
								 | 
							
								    5
							 | 
						||
| 
								 | 
							
								    >>> i1.length = 12
							 | 
						||
| 
								 | 
							
								    >>> i1.end
							 | 
						||
| 
								 | 
							
								    17
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _hybrid_bulk_update:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Allowing Bulk ORM Update
							 | 
						||
| 
								 | 
							
								------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A hybrid can define a custom "UPDATE" handler for when using the
							 | 
						||
| 
								 | 
							
								:meth:`_query.Query.update` method, allowing the hybrid to be used in the
							 | 
						||
| 
								 | 
							
								SET clause of the update.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Normally, when using a hybrid with :meth:`_query.Query.update`, the SQL
							 | 
						||
| 
								 | 
							
								expression is used as the column that's the target of the SET.  If our
							 | 
						||
| 
								 | 
							
								``Interval`` class had a hybrid ``start_point`` that linked to
							 | 
						||
| 
								 | 
							
								``Interval.start``, this could be substituted directly::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    session.query(Interval).update({Interval.start_point: 10})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								However, when using a composite hybrid like ``Interval.length``, this
							 | 
						||
| 
								 | 
							
								hybrid represents more than one column.   We can set up a handler that will
							 | 
						||
| 
								 | 
							
								accommodate a value passed to :meth:`_query.Query.update` which can affect
							 | 
						||
| 
								 | 
							
								this, using the :meth:`.hybrid_property.update_expression` decorator.
							 | 
						||
| 
								 | 
							
								A handler that works similarly to our setter would be::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Interval(object):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def length(self):
							 | 
						||
| 
								 | 
							
								            return self.end - self.start
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @length.setter
							 | 
						||
| 
								 | 
							
								        def length(self, value):
							 | 
						||
| 
								 | 
							
								            self.end = self.start + value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @length.update_expression
							 | 
						||
| 
								 | 
							
								        def length(cls, value):
							 | 
						||
| 
								 | 
							
								            return [
							 | 
						||
| 
								 | 
							
								                (cls.end, cls.start + value)
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, if we use ``Interval.length`` in an UPDATE expression as::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    session.query(Interval).update(
							 | 
						||
| 
								 | 
							
								        {Interval.length: 25}, synchronize_session='fetch')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We'll get an UPDATE statement along the lines of::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    UPDATE interval SET end=start + :value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								In some cases, the default "evaluate" strategy can't perform the SET
							 | 
						||
| 
								 | 
							
								expression in Python; while the addition operator we're using above
							 | 
						||
| 
								 | 
							
								is supported, for more complex SET expressions it will usually be necessary
							 | 
						||
| 
								 | 
							
								to use either the "fetch" or False synchronization strategy as illustrated
							 | 
						||
| 
								 | 
							
								above.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note:: For ORM bulk updates to work with hybrids, the function name
							 | 
						||
| 
								 | 
							
								   of the hybrid must match that of how it is accessed.    Something
							 | 
						||
| 
								 | 
							
								   like this wouldn't work::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class Interval(object):
							 | 
						||
| 
								 | 
							
								            # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def _get(self):
							 | 
						||
| 
								 | 
							
								                return self.end - self.start
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def _set(self, value):
							 | 
						||
| 
								 | 
							
								                self.end = self.start + value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def _update_expr(cls, value):
							 | 
						||
| 
								 | 
							
								                return [
							 | 
						||
| 
								 | 
							
								                    (cls.end, cls.start + value)
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            length = hybrid_property(
							 | 
						||
| 
								 | 
							
								                fget=_get, fset=_set, update_expr=_update_expr
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The Python descriptor protocol does not provide any reliable way for
							 | 
						||
| 
								 | 
							
								    a descriptor to know what attribute name it was accessed as, and
							 | 
						||
| 
								 | 
							
								    the UPDATE scheme currently relies upon being able to access the
							 | 
						||
| 
								 | 
							
								    attribute from an instance by name in order to perform the instance
							 | 
						||
| 
								 | 
							
								    synchronization step.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.2 added support for bulk updates to hybrid properties.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Working with Relationships
							 | 
						||
| 
								 | 
							
								--------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								There's no essential difference when creating hybrids that work with
							 | 
						||
| 
								 | 
							
								related objects as opposed to column-based data. The need for distinct
							 | 
						||
| 
								 | 
							
								expressions tends to be greater.  The two variants we'll illustrate
							 | 
						||
| 
								 | 
							
								are the "join-dependent" hybrid, and the "correlated subquery" hybrid.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Join-Dependent Relationship Hybrid
							 | 
						||
| 
								 | 
							
								^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Consider the following declarative
							 | 
						||
| 
								 | 
							
								mapping which relates a ``User`` to a ``SavingsAccount``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column, Integer, ForeignKey, Numeric, String
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import relationship
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import hybrid_property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class SavingsAccount(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'account'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
							 | 
						||
| 
								 | 
							
								        balance = Column(Numeric(15, 5))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class User(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'user'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        name = Column(String(100), nullable=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        accounts = relationship("SavingsAccount", backref="owner")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def balance(self):
							 | 
						||
| 
								 | 
							
								            if self.accounts:
							 | 
						||
| 
								 | 
							
								                return self.accounts[0].balance
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @balance.setter
							 | 
						||
| 
								 | 
							
								        def balance(self, value):
							 | 
						||
| 
								 | 
							
								            if not self.accounts:
							 | 
						||
| 
								 | 
							
								                account = Account(owner=self)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                account = self.accounts[0]
							 | 
						||
| 
								 | 
							
								            account.balance = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @balance.expression
							 | 
						||
| 
								 | 
							
								        def balance(cls):
							 | 
						||
| 
								 | 
							
								            return SavingsAccount.balance
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The above hybrid property ``balance`` works with the first
							 | 
						||
| 
								 | 
							
								``SavingsAccount`` entry in the list of accounts for this user.   The
							 | 
						||
| 
								 | 
							
								in-Python getter/setter methods can treat ``accounts`` as a Python
							 | 
						||
| 
								 | 
							
								list available on ``self``.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								However, at the expression level, it's expected that the ``User`` class will
							 | 
						||
| 
								 | 
							
								be used in an appropriate context such that an appropriate join to
							 | 
						||
| 
								 | 
							
								``SavingsAccount`` will be present::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(User, User.balance).
							 | 
						||
| 
								 | 
							
								    ...       join(User.accounts).filter(User.balance > 5000))
							 | 
						||
| 
								 | 
							
								    SELECT "user".id AS user_id, "user".name AS user_name,
							 | 
						||
| 
								 | 
							
								    account.balance AS account_balance
							 | 
						||
| 
								 | 
							
								    FROM "user" JOIN account ON "user".id = account.user_id
							 | 
						||
| 
								 | 
							
								    WHERE account.balance > :balance_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note however, that while the instance level accessors need to worry
							 | 
						||
| 
								 | 
							
								about whether ``self.accounts`` is even present, this issue expresses
							 | 
						||
| 
								 | 
							
								itself differently at the SQL expression level, where we basically
							 | 
						||
| 
								 | 
							
								would use an outer join::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> from sqlalchemy import or_
							 | 
						||
| 
								 | 
							
								    >>> print (Session().query(User, User.balance).outerjoin(User.accounts).
							 | 
						||
| 
								 | 
							
								    ...         filter(or_(User.balance < 5000, User.balance == None)))
							 | 
						||
| 
								 | 
							
								    SELECT "user".id AS user_id, "user".name AS user_name,
							 | 
						||
| 
								 | 
							
								    account.balance AS account_balance
							 | 
						||
| 
								 | 
							
								    FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id
							 | 
						||
| 
								 | 
							
								    WHERE account.balance <  :balance_1 OR account.balance IS NULL
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Correlated Subquery Relationship Hybrid
							 | 
						||
| 
								 | 
							
								^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We can, of course, forego being dependent on the enclosing query's usage
							 | 
						||
| 
								 | 
							
								of joins in favor of the correlated subquery, which can portably be packed
							 | 
						||
| 
								 | 
							
								into a single column expression. A correlated subquery is more portable, but
							 | 
						||
| 
								 | 
							
								often performs more poorly at the SQL level. Using the same technique
							 | 
						||
| 
								 | 
							
								illustrated at :ref:`mapper_column_property_sql_expressions`,
							 | 
						||
| 
								 | 
							
								we can adjust our ``SavingsAccount`` example to aggregate the balances for
							 | 
						||
| 
								 | 
							
								*all* accounts, and use a correlated subquery for the column expression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column, Integer, ForeignKey, Numeric, String
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import relationship
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import hybrid_property
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import select, func
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class SavingsAccount(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'account'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
							 | 
						||
| 
								 | 
							
								        balance = Column(Numeric(15, 5))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class User(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'user'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        name = Column(String(100), nullable=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        accounts = relationship("SavingsAccount", backref="owner")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def balance(self):
							 | 
						||
| 
								 | 
							
								            return sum(acc.balance for acc in self.accounts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @balance.expression
							 | 
						||
| 
								 | 
							
								        def balance(cls):
							 | 
						||
| 
								 | 
							
								            return select(func.sum(SavingsAccount.balance)).\
							 | 
						||
| 
								 | 
							
								                    where(SavingsAccount.user_id==cls.id).\
							 | 
						||
| 
								 | 
							
								                    label('total_balance')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The above recipe will give us the ``balance`` column which renders
							 | 
						||
| 
								 | 
							
								a correlated SELECT::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(s.query(User).filter(User.balance > 400))
							 | 
						||
| 
								 | 
							
								    SELECT "user".id AS user_id, "user".name AS user_name
							 | 
						||
| 
								 | 
							
								    FROM "user"
							 | 
						||
| 
								 | 
							
								    WHERE (SELECT sum(account.balance) AS sum_1
							 | 
						||
| 
								 | 
							
								    FROM account
							 | 
						||
| 
								 | 
							
								    WHERE account.user_id = "user".id) > :param_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _hybrid_custom_comparators:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Building Custom Comparators
							 | 
						||
| 
								 | 
							
								---------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The hybrid property also includes a helper that allows construction of
							 | 
						||
| 
								 | 
							
								custom comparators. A comparator object allows one to customize the
							 | 
						||
| 
								 | 
							
								behavior of each SQLAlchemy expression operator individually.  They
							 | 
						||
| 
								 | 
							
								are useful when creating custom types that have some highly
							 | 
						||
| 
								 | 
							
								idiosyncratic behavior on the SQL side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note::  The :meth:`.hybrid_property.comparator` decorator introduced
							 | 
						||
| 
								 | 
							
								   in this section **replaces** the use of the
							 | 
						||
| 
								 | 
							
								   :meth:`.hybrid_property.expression` decorator.
							 | 
						||
| 
								 | 
							
								   They cannot be used together.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The example class below allows case-insensitive comparisons on the attribute
							 | 
						||
| 
								 | 
							
								named ``word_insensitive``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import Comparator, hybrid_property
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import func, Column, Integer, String
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import Session
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class CaseInsensitiveComparator(Comparator):
							 | 
						||
| 
								 | 
							
								        def __eq__(self, other):
							 | 
						||
| 
								 | 
							
								            return func.lower(self.__clause_element__()) == func.lower(other)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class SearchWord(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'searchword'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        word = Column(String(255), nullable=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def word_insensitive(self):
							 | 
						||
| 
								 | 
							
								            return self.word.lower()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @word_insensitive.comparator
							 | 
						||
| 
								 | 
							
								        def word_insensitive(cls):
							 | 
						||
| 
								 | 
							
								            return CaseInsensitiveComparator(cls.word)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, SQL expressions against ``word_insensitive`` will apply the ``LOWER()``
							 | 
						||
| 
								 | 
							
								SQL function to both sides::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(SearchWord).filter_by(word_insensitive="Trucks"))
							 | 
						||
| 
								 | 
							
								    SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
							 | 
						||
| 
								 | 
							
								    FROM searchword
							 | 
						||
| 
								 | 
							
								    WHERE lower(searchword.word) = lower(:lower_1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``CaseInsensitiveComparator`` above implements part of the
							 | 
						||
| 
								 | 
							
								:class:`.ColumnOperators` interface.   A "coercion" operation like
							 | 
						||
| 
								 | 
							
								lowercasing can be applied to all comparison operations (i.e. ``eq``,
							 | 
						||
| 
								 | 
							
								``lt``, ``gt``, etc.) using :meth:`.Operators.operate`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class CaseInsensitiveComparator(Comparator):
							 | 
						||
| 
								 | 
							
								        def operate(self, op, other):
							 | 
						||
| 
								 | 
							
								            return op(func.lower(self.__clause_element__()), func.lower(other))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _hybrid_reuse_subclass:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Reusing Hybrid Properties across Subclasses
							 | 
						||
| 
								 | 
							
								-------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A hybrid can be referred to from a superclass, to allow modifying
							 | 
						||
| 
								 | 
							
								methods like :meth:`.hybrid_property.getter`, :meth:`.hybrid_property.setter`
							 | 
						||
| 
								 | 
							
								to be used to redefine those methods on a subclass.  This is similar to
							 | 
						||
| 
								 | 
							
								how the standard Python ``@property`` object works::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class FirstNameOnly(Base):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        first_name = Column(String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def name(self):
							 | 
						||
| 
								 | 
							
								            return self.first_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @name.setter
							 | 
						||
| 
								 | 
							
								        def name(self, value):
							 | 
						||
| 
								 | 
							
								            self.first_name = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class FirstNameLastName(FirstNameOnly):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        last_name = Column(String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @FirstNameOnly.name.getter
							 | 
						||
| 
								 | 
							
								        def name(self):
							 | 
						||
| 
								 | 
							
								            return self.first_name + ' ' + self.last_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @name.setter
							 | 
						||
| 
								 | 
							
								        def name(self, value):
							 | 
						||
| 
								 | 
							
								            self.first_name, self.last_name = value.split(' ', 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, the ``FirstNameLastName`` class refers to the hybrid from
							 | 
						||
| 
								 | 
							
								``FirstNameOnly.name`` to repurpose its getter and setter for the subclass.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When overriding :meth:`.hybrid_property.expression` and
							 | 
						||
| 
								 | 
							
								:meth:`.hybrid_property.comparator` alone as the first reference to the
							 | 
						||
| 
								 | 
							
								superclass, these names conflict with the same-named accessors on the class-
							 | 
						||
| 
								 | 
							
								level :class:`.QueryableAttribute` object returned at the class level.  To
							 | 
						||
| 
								 | 
							
								override these methods when referring directly to the parent class descriptor,
							 | 
						||
| 
								 | 
							
								add the special qualifier :attr:`.hybrid_property.overrides`, which will de-
							 | 
						||
| 
								 | 
							
								reference the instrumented attribute back to the hybrid object::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class FirstNameLastName(FirstNameOnly):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        last_name = Column(String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @FirstNameOnly.name.overrides.expression
							 | 
						||
| 
								 | 
							
								        def name(cls):
							 | 
						||
| 
								 | 
							
								            return func.concat(cls.first_name, ' ', cls.last_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.2 Added :meth:`.hybrid_property.getter` as well as the
							 | 
						||
| 
								 | 
							
								   ability to redefine accessors per-subclass.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Hybrid Value Objects
							 | 
						||
| 
								 | 
							
								--------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note in our previous example, if we were to compare the ``word_insensitive``
							 | 
						||
| 
								 | 
							
								attribute of a ``SearchWord`` instance to a plain Python string, the plain
							 | 
						||
| 
								 | 
							
								Python string would not be coerced to lower case - the
							 | 
						||
| 
								 | 
							
								``CaseInsensitiveComparator`` we built, being returned by
							 | 
						||
| 
								 | 
							
								``@word_insensitive.comparator``, only applies to the SQL side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A more comprehensive form of the custom comparator is to construct a *Hybrid
							 | 
						||
| 
								 | 
							
								Value Object*. This technique applies the target value or expression to a value
							 | 
						||
| 
								 | 
							
								object which is then returned by the accessor in all cases.   The value object
							 | 
						||
| 
								 | 
							
								allows control of all operations upon the value as well as how compared values
							 | 
						||
| 
								 | 
							
								are treated, both on the SQL expression side as well as the Python value side.
							 | 
						||
| 
								 | 
							
								Replacing the previous ``CaseInsensitiveComparator`` class with a new
							 | 
						||
| 
								 | 
							
								``CaseInsensitiveWord`` class::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class CaseInsensitiveWord(Comparator):
							 | 
						||
| 
								 | 
							
								        "Hybrid value representing a lower case representation of a word."
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, word):
							 | 
						||
| 
								 | 
							
								            if isinstance(word, basestring):
							 | 
						||
| 
								 | 
							
								                self.word = word.lower()
							 | 
						||
| 
								 | 
							
								            elif isinstance(word, CaseInsensitiveWord):
							 | 
						||
| 
								 | 
							
								                self.word = word.word
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.word = func.lower(word)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def operate(self, op, other):
							 | 
						||
| 
								 | 
							
								            if not isinstance(other, CaseInsensitiveWord):
							 | 
						||
| 
								 | 
							
								                other = CaseInsensitiveWord(other)
							 | 
						||
| 
								 | 
							
								            return op(self.word, other.word)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __clause_element__(self):
							 | 
						||
| 
								 | 
							
								            return self.word
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __str__(self):
							 | 
						||
| 
								 | 
							
								            return self.word
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        key = 'word'
							 | 
						||
| 
								 | 
							
								        "Label to apply to Query tuple results"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Above, the ``CaseInsensitiveWord`` object represents ``self.word``, which may
							 | 
						||
| 
								 | 
							
								be a SQL function, or may be a Python native.   By overriding ``operate()`` and
							 | 
						||
| 
								 | 
							
								``__clause_element__()`` to work in terms of ``self.word``, all comparison
							 | 
						||
| 
								 | 
							
								operations will work against the "converted" form of ``word``, whether it be
							 | 
						||
| 
								 | 
							
								SQL side or Python side. Our ``SearchWord`` class can now deliver the
							 | 
						||
| 
								 | 
							
								``CaseInsensitiveWord`` object unconditionally from a single hybrid call::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class SearchWord(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'searchword'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        word = Column(String(255), nullable=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def word_insensitive(self):
							 | 
						||
| 
								 | 
							
								            return CaseInsensitiveWord(self.word)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``word_insensitive`` attribute now has case-insensitive comparison behavior
							 | 
						||
| 
								 | 
							
								universally, including SQL expression vs. Python expression (note the Python
							 | 
						||
| 
								 | 
							
								value is converted to lower case on the Python side here)::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(SearchWord).filter_by(word_insensitive="Trucks"))
							 | 
						||
| 
								 | 
							
								    SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
							 | 
						||
| 
								 | 
							
								    FROM searchword
							 | 
						||
| 
								 | 
							
								    WHERE lower(searchword.word) = :lower_1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SQL expression versus SQL expression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> sw1 = aliased(SearchWord)
							 | 
						||
| 
								 | 
							
								    >>> sw2 = aliased(SearchWord)
							 | 
						||
| 
								 | 
							
								    >>> print(Session().query(
							 | 
						||
| 
								 | 
							
								    ...                    sw1.word_insensitive,
							 | 
						||
| 
								 | 
							
								    ...                    sw2.word_insensitive).\
							 | 
						||
| 
								 | 
							
								    ...                        filter(
							 | 
						||
| 
								 | 
							
								    ...                            sw1.word_insensitive > sw2.word_insensitive
							 | 
						||
| 
								 | 
							
								    ...                        ))
							 | 
						||
| 
								 | 
							
								    SELECT lower(searchword_1.word) AS lower_1,
							 | 
						||
| 
								 | 
							
								    lower(searchword_2.word) AS lower_2
							 | 
						||
| 
								 | 
							
								    FROM searchword AS searchword_1, searchword AS searchword_2
							 | 
						||
| 
								 | 
							
								    WHERE lower(searchword_1.word) > lower(searchword_2.word)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Python only expression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> ws1 = SearchWord(word="SomeWord")
							 | 
						||
| 
								 | 
							
								    >>> ws1.word_insensitive == "sOmEwOrD"
							 | 
						||
| 
								 | 
							
								    True
							 | 
						||
| 
								 | 
							
								    >>> ws1.word_insensitive == "XOmEwOrX"
							 | 
						||
| 
								 | 
							
								    False
							 | 
						||
| 
								 | 
							
								    >>> print(ws1.word_insensitive)
							 | 
						||
| 
								 | 
							
								    someword
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Hybrid Value pattern is very useful for any kind of value that may have
							 | 
						||
| 
								 | 
							
								multiple representations, such as timestamps, time deltas, units of
							 | 
						||
| 
								 | 
							
								measurement, currencies and encrypted passwords.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    `Hybrids and Value Agnostic Types
							 | 
						||
| 
								 | 
							
								    <https://techspot.zzzeek.org/2011/10/21/hybrids-and-value-agnostic-types/>`_
							 | 
						||
| 
								 | 
							
								    - on the techspot.zzzeek.org blog
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    `Value Agnostic Types, Part II
							 | 
						||
| 
								 | 
							
								    <https://techspot.zzzeek.org/2011/10/29/value-agnostic-types-part-ii/>`_ -
							 | 
						||
| 
								 | 
							
								    on the techspot.zzzeek.org blog
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _hybrid_transformers:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Building Transformers
							 | 
						||
| 
								 | 
							
								----------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A *transformer* is an object which can receive a :class:`_query.Query`
							 | 
						||
| 
								 | 
							
								object and
							 | 
						||
| 
								 | 
							
								return a new one.   The :class:`_query.Query` object includes a method
							 | 
						||
| 
								 | 
							
								:meth:`.with_transformation` that returns a new :class:`_query.Query`
							 | 
						||
| 
								 | 
							
								transformed by
							 | 
						||
| 
								 | 
							
								the given function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We can combine this with the :class:`.Comparator` class to produce one type
							 | 
						||
| 
								 | 
							
								of recipe which can both set up the FROM clause of a query as well as assign
							 | 
						||
| 
								 | 
							
								filtering criterion.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Consider a mapped class ``Node``, which assembles using adjacency list into a
							 | 
						||
| 
								 | 
							
								hierarchical tree pattern::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column, Integer, ForeignKey
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.orm import relationship
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.declarative import declarative_base
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Node(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'node'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        parent_id = Column(Integer, ForeignKey('node.id'))
							 | 
						||
| 
								 | 
							
								        parent = relationship("Node", remote_side=id)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Suppose we wanted to add an accessor ``grandparent``.  This would return the
							 | 
						||
| 
								 | 
							
								``parent`` of ``Node.parent``.  When we have an instance of ``Node``, this is
							 | 
						||
| 
								 | 
							
								simple::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import hybrid_property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Node(Base):
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def grandparent(self):
							 | 
						||
| 
								 | 
							
								            return self.parent.parent
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For the expression, things are not so clear.   We'd need to construct a
							 | 
						||
| 
								 | 
							
								:class:`_query.Query` where we :meth:`_query.Query.join` twice along
							 | 
						||
| 
								 | 
							
								``Node.parent`` to get to the ``grandparent``.   We can instead return a
							 | 
						||
| 
								 | 
							
								transforming callable that we'll combine with the :class:`.Comparator` class to
							 | 
						||
| 
								 | 
							
								receive any :class:`_query.Query` object, and return a new one that's joined to
							 | 
						||
| 
								 | 
							
								the ``Node.parent`` attribute and filtered based on the given criterion::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.ext.hybrid import Comparator
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class GrandparentTransformer(Comparator):
							 | 
						||
| 
								 | 
							
								        def operate(self, op, other):
							 | 
						||
| 
								 | 
							
								            def transform(q):
							 | 
						||
| 
								 | 
							
								                cls = self.__clause_element__()
							 | 
						||
| 
								 | 
							
								                parent_alias = aliased(cls)
							 | 
						||
| 
								 | 
							
								                return q.join(parent_alias, cls.parent).\
							 | 
						||
| 
								 | 
							
								                            filter(op(parent_alias.parent, other))
							 | 
						||
| 
								 | 
							
								            return transform
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Base = declarative_base()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Node(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'node'
							 | 
						||
| 
								 | 
							
								        id = Column(Integer, primary_key=True)
							 | 
						||
| 
								 | 
							
								        parent_id = Column(Integer, ForeignKey('node.id'))
							 | 
						||
| 
								 | 
							
								        parent = relationship("Node", remote_side=id)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @hybrid_property
							 | 
						||
| 
								 | 
							
								        def grandparent(self):
							 | 
						||
| 
								 | 
							
								            return self.parent.parent
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @grandparent.comparator
							 | 
						||
| 
								 | 
							
								        def grandparent(cls):
							 | 
						||
| 
								 | 
							
								            return GrandparentTransformer(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``GrandparentTransformer`` overrides the core :meth:`.Operators.operate`
							 | 
						||
| 
								 | 
							
								method at the base of the :class:`.Comparator` hierarchy to return a query-
							 | 
						||
| 
								 | 
							
								transforming callable, which then runs the given comparison operation in a
							 | 
						||
| 
								 | 
							
								particular context. Such as, in the example above, the ``operate`` method is
							 | 
						||
| 
								 | 
							
								called, given the :attr:`.Operators.eq` callable as well as the right side of
							 | 
						||
| 
								 | 
							
								the comparison ``Node(id=5)``.  A function ``transform`` is then returned which
							 | 
						||
| 
								 | 
							
								will transform a :class:`_query.Query` first to join to ``Node.parent``,
							 | 
						||
| 
								 | 
							
								then to
							 | 
						||
| 
								 | 
							
								compare ``parent_alias`` using :attr:`.Operators.eq` against the left and right
							 | 
						||
| 
								 | 
							
								sides, passing into :meth:`_query.Query.filter`:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. sourcecode:: pycon+sql
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    >>> from sqlalchemy.orm import Session
							 | 
						||
| 
								 | 
							
								    >>> session = Session()
							 | 
						||
| 
								 | 
							
								    {sql}>>> session.query(Node).\
							 | 
						||
| 
								 | 
							
								    ...        with_transformation(Node.grandparent==Node(id=5)).\
							 | 
						||
| 
								 | 
							
								    ...        all()
							 | 
						||
| 
								 | 
							
								    SELECT node.id AS node_id, node.parent_id AS node_parent_id
							 | 
						||
| 
								 | 
							
								    FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
							 | 
						||
| 
								 | 
							
								    WHERE :param_1 = node_1.parent_id
							 | 
						||
| 
								 | 
							
								    {stop}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We can modify the pattern to be more verbose but flexible by separating the
							 | 
						||
| 
								 | 
							
								"join" step from the "filter" step.  The tricky part here is ensuring that
							 | 
						||
| 
								 | 
							
								successive instances of ``GrandparentTransformer`` use the same
							 | 
						||
| 
								 | 
							
								:class:`.AliasedClass` object against ``Node``.  Below we use a simple
							 | 
						||
| 
								 | 
							
								memoizing approach that associates a ``GrandparentTransformer`` with each
							 | 
						||
| 
								 | 
							
								class::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class Node(Base):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @grandparent.comparator
							 | 
						||
| 
								 | 
							
								        def grandparent(cls):
							 | 
						||
| 
								 | 
							
								            # memoize a GrandparentTransformer
							 | 
						||
| 
								 | 
							
								            # per class
							 | 
						||
| 
								 | 
							
								            if '_gp' not in cls.__dict__:
							 | 
						||
| 
								 | 
							
								                cls._gp = GrandparentTransformer(cls)
							 | 
						||
| 
								 | 
							
								            return cls._gp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    class GrandparentTransformer(Comparator):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def __init__(self, cls):
							 | 
						||
| 
								 | 
							
								            self.parent_alias = aliased(cls)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @property
							 | 
						||
| 
								 | 
							
								        def join(self):
							 | 
						||
| 
								 | 
							
								            def go(q):
							 | 
						||
| 
								 | 
							
								                return q.join(self.parent_alias, Node.parent)
							 | 
						||
| 
								 | 
							
								            return go
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def operate(self, op, other):
							 | 
						||
| 
								 | 
							
								            return op(self.parent_alias.parent, other)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. sourcecode:: pycon+sql
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    {sql}>>> session.query(Node).\
							 | 
						||
| 
								 | 
							
								    ...            with_transformation(Node.grandparent.join).\
							 | 
						||
| 
								 | 
							
								    ...            filter(Node.grandparent==Node(id=5))
							 | 
						||
| 
								 | 
							
								    SELECT node.id AS node_id, node.parent_id AS node_parent_id
							 | 
						||
| 
								 | 
							
								    FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
							 | 
						||
| 
								 | 
							
								    WHERE :param_1 = node_1.parent_id
							 | 
						||
| 
								 | 
							
								    {stop}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The "transformer" pattern is an experimental pattern that starts to make usage
							 | 
						||
| 
								 | 
							
								of some functional programming paradigms. While it's only recommended for
							 | 
						||
| 
								 | 
							
								advanced and/or patient developers, there's probably a whole lot of amazing
							 | 
						||
| 
								 | 
							
								things it can be used for.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""  # noqa
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								from ..orm import attributes
							 | 
						||
| 
								 | 
							
								from ..orm import interfaces
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HYBRID_METHOD = util.symbol("HYBRID_METHOD")
							 | 
						||
| 
								 | 
							
								"""Symbol indicating an :class:`InspectionAttr` that's
							 | 
						||
| 
								 | 
							
								   of type :class:`.hybrid_method`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   Is assigned to the :attr:`.InspectionAttr.extension_type`
							 | 
						||
| 
								 | 
							
								   attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :attr:`_orm.Mapper.all_orm_attributes`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								HYBRID_PROPERTY = util.symbol("HYBRID_PROPERTY")
							 | 
						||
| 
								 | 
							
								"""Symbol indicating an :class:`InspectionAttr` that's
							 | 
						||
| 
								 | 
							
								    of type :class:`.hybrid_method`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   Is assigned to the :attr:`.InspectionAttr.extension_type`
							 | 
						||
| 
								 | 
							
								   attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :attr:`_orm.Mapper.all_orm_attributes`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class hybrid_method(interfaces.InspectionAttrInfo):
							 | 
						||
| 
								 | 
							
								    """A decorator which allows definition of a Python object method with both
							 | 
						||
| 
								 | 
							
								    instance-level and class-level behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_attribute = True
							 | 
						||
| 
								 | 
							
								    extension_type = HYBRID_METHOD
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, func, expr=None):
							 | 
						||
| 
								 | 
							
								        """Create a new :class:`.hybrid_method`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Usage is typically via decorator::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            from sqlalchemy.ext.hybrid import hybrid_method
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            class SomeClass(object):
							 | 
						||
| 
								 | 
							
								                @hybrid_method
							 | 
						||
| 
								 | 
							
								                def value(self, x, y):
							 | 
						||
| 
								 | 
							
								                    return self._value + x + y
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @value.expression
							 | 
						||
| 
								 | 
							
								                def value(self, x, y):
							 | 
						||
| 
								 | 
							
								                    return func.some_function(self._value, x, y)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.func = func
							 | 
						||
| 
								 | 
							
								        self.expression(expr or func)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __get__(self, instance, owner):
							 | 
						||
| 
								 | 
							
								        if instance is None:
							 | 
						||
| 
								 | 
							
								            return self.expr.__get__(owner, owner.__class__)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self.func.__get__(instance, owner)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def expression(self, expr):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a
							 | 
						||
| 
								 | 
							
								        SQL-expression producing method."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.expr = expr
							 | 
						||
| 
								 | 
							
								        if not self.expr.__doc__:
							 | 
						||
| 
								 | 
							
								            self.expr.__doc__ = self.func.__doc__
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class hybrid_property(interfaces.InspectionAttrInfo):
							 | 
						||
| 
								 | 
							
								    """A decorator which allows definition of a Python descriptor with both
							 | 
						||
| 
								 | 
							
								    instance-level and class-level behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_attribute = True
							 | 
						||
| 
								 | 
							
								    extension_type = HYBRID_PROPERTY
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        fget,
							 | 
						||
| 
								 | 
							
								        fset=None,
							 | 
						||
| 
								 | 
							
								        fdel=None,
							 | 
						||
| 
								 | 
							
								        expr=None,
							 | 
						||
| 
								 | 
							
								        custom_comparator=None,
							 | 
						||
| 
								 | 
							
								        update_expr=None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        """Create a new :class:`.hybrid_property`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Usage is typically via decorator::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            from sqlalchemy.ext.hybrid import hybrid_property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            class SomeClass(object):
							 | 
						||
| 
								 | 
							
								                @hybrid_property
							 | 
						||
| 
								 | 
							
								                def value(self):
							 | 
						||
| 
								 | 
							
								                    return self._value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @value.setter
							 | 
						||
| 
								 | 
							
								                def value(self, value):
							 | 
						||
| 
								 | 
							
								                    self._value = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.fget = fget
							 | 
						||
| 
								 | 
							
								        self.fset = fset
							 | 
						||
| 
								 | 
							
								        self.fdel = fdel
							 | 
						||
| 
								 | 
							
								        self.expr = expr
							 | 
						||
| 
								 | 
							
								        self.custom_comparator = custom_comparator
							 | 
						||
| 
								 | 
							
								        self.update_expr = update_expr
							 | 
						||
| 
								 | 
							
								        util.update_wrapper(self, fget)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __get__(self, instance, owner):
							 | 
						||
| 
								 | 
							
								        if instance is None:
							 | 
						||
| 
								 | 
							
								            return self._expr_comparator(owner)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self.fget(instance)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __set__(self, instance, value):
							 | 
						||
| 
								 | 
							
								        if self.fset is None:
							 | 
						||
| 
								 | 
							
								            raise AttributeError("can't set attribute")
							 | 
						||
| 
								 | 
							
								        self.fset(instance, value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __delete__(self, instance):
							 | 
						||
| 
								 | 
							
								        if self.fdel is None:
							 | 
						||
| 
								 | 
							
								            raise AttributeError("can't delete attribute")
							 | 
						||
| 
								 | 
							
								        self.fdel(instance)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _copy(self, **kw):
							 | 
						||
| 
								 | 
							
								        defaults = {
							 | 
						||
| 
								 | 
							
								            key: value
							 | 
						||
| 
								 | 
							
								            for key, value in self.__dict__.items()
							 | 
						||
| 
								 | 
							
								            if not key.startswith("_")
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        defaults.update(**kw)
							 | 
						||
| 
								 | 
							
								        return type(self)(**defaults)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def overrides(self):
							 | 
						||
| 
								 | 
							
								        """Prefix for a method that is overriding an existing attribute.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The :attr:`.hybrid_property.overrides` accessor just returns
							 | 
						||
| 
								 | 
							
								        this hybrid object, which when called at the class level from
							 | 
						||
| 
								 | 
							
								        a parent class, will de-reference the "instrumented attribute"
							 | 
						||
| 
								 | 
							
								        normally returned at this level, and allow modifying decorators
							 | 
						||
| 
								 | 
							
								        like :meth:`.hybrid_property.expression` and
							 | 
						||
| 
								 | 
							
								        :meth:`.hybrid_property.comparator`
							 | 
						||
| 
								 | 
							
								        to be used without conflicting with the same-named attributes
							 | 
						||
| 
								 | 
							
								        normally present on the :class:`.QueryableAttribute`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            class SuperClass(object):
							 | 
						||
| 
								 | 
							
								                # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @hybrid_property
							 | 
						||
| 
								 | 
							
								                def foobar(self):
							 | 
						||
| 
								 | 
							
								                    return self._foobar
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            class SubClass(SuperClass):
							 | 
						||
| 
								 | 
							
								                # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @SuperClass.foobar.overrides.expression
							 | 
						||
| 
								 | 
							
								                def foobar(cls):
							 | 
						||
| 
								 | 
							
								                    return func.subfoobar(self._foobar)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :ref:`hybrid_reuse_subclass`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getter(self, fget):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a getter method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._copy(fget=fget)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def setter(self, fset):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a setter method."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._copy(fset=fset)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def deleter(self, fdel):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a deletion method."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._copy(fdel=fdel)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def expression(self, expr):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a SQL-expression
							 | 
						||
| 
								 | 
							
								        producing method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        When a hybrid is invoked at the class level, the SQL expression given
							 | 
						||
| 
								 | 
							
								        here is wrapped inside of a specialized :class:`.QueryableAttribute`,
							 | 
						||
| 
								 | 
							
								        which is the same kind of object used by the ORM to represent other
							 | 
						||
| 
								 | 
							
								        mapped attributes.   The reason for this is so that other class-level
							 | 
						||
| 
								 | 
							
								        attributes such as docstrings and a reference to the hybrid itself may
							 | 
						||
| 
								 | 
							
								        be maintained within the structure that's returned, without any
							 | 
						||
| 
								 | 
							
								        modifications to the original SQL expression passed in.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								           When referring to a hybrid property  from an owning class (e.g.
							 | 
						||
| 
								 | 
							
								           ``SomeClass.some_hybrid``), an instance of
							 | 
						||
| 
								 | 
							
								           :class:`.QueryableAttribute` is returned, representing the
							 | 
						||
| 
								 | 
							
								           expression or comparator object as well as this  hybrid object.
							 | 
						||
| 
								 | 
							
								           However, that object itself has accessors called ``expression`` and
							 | 
						||
| 
								 | 
							
								           ``comparator``; so when attempting to override these decorators on a
							 | 
						||
| 
								 | 
							
								           subclass, it may be necessary to qualify it using the
							 | 
						||
| 
								 | 
							
								           :attr:`.hybrid_property.overrides` modifier first.  See that
							 | 
						||
| 
								 | 
							
								           modifier for details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            :ref:`hybrid_distinct_expression`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._copy(expr=expr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def comparator(self, comparator):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines a custom
							 | 
						||
| 
								 | 
							
								        comparator producing method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The return value of the decorated method should be an instance of
							 | 
						||
| 
								 | 
							
								        :class:`~.hybrid.Comparator`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. note::  The :meth:`.hybrid_property.comparator` decorator
							 | 
						||
| 
								 | 
							
								           **replaces** the use of the :meth:`.hybrid_property.expression`
							 | 
						||
| 
								 | 
							
								           decorator.  They cannot be used together.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        When a hybrid is invoked at the class level, the
							 | 
						||
| 
								 | 
							
								        :class:`~.hybrid.Comparator` object given here is wrapped inside of a
							 | 
						||
| 
								 | 
							
								        specialized :class:`.QueryableAttribute`, which is the same kind of
							 | 
						||
| 
								 | 
							
								        object used by the ORM to represent other mapped attributes.   The
							 | 
						||
| 
								 | 
							
								        reason for this is so that other class-level attributes such as
							 | 
						||
| 
								 | 
							
								        docstrings and a reference to the hybrid itself may be maintained
							 | 
						||
| 
								 | 
							
								        within the structure that's returned, without any modifications to the
							 | 
						||
| 
								 | 
							
								        original comparator object passed in.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. note::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								           When referring to a hybrid property  from an owning class (e.g.
							 | 
						||
| 
								 | 
							
								           ``SomeClass.some_hybrid``), an instance of
							 | 
						||
| 
								 | 
							
								           :class:`.QueryableAttribute` is returned, representing the
							 | 
						||
| 
								 | 
							
								           expression or comparator object as this  hybrid object.  However,
							 | 
						||
| 
								 | 
							
								           that object itself has accessors called ``expression`` and
							 | 
						||
| 
								 | 
							
								           ``comparator``; so when attempting to override these decorators on a
							 | 
						||
| 
								 | 
							
								           subclass, it may be necessary to qualify it using the
							 | 
						||
| 
								 | 
							
								           :attr:`.hybrid_property.overrides` modifier first.  See that
							 | 
						||
| 
								 | 
							
								           modifier for details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self._copy(custom_comparator=comparator)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def update_expression(self, meth):
							 | 
						||
| 
								 | 
							
								        """Provide a modifying decorator that defines an UPDATE tuple
							 | 
						||
| 
								 | 
							
								        producing method.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The method accepts a single value, which is the value to be
							 | 
						||
| 
								 | 
							
								        rendered into the SET clause of an UPDATE statement.  The method
							 | 
						||
| 
								 | 
							
								        should then process this value into individual column expressions
							 | 
						||
| 
								 | 
							
								        that fit into the ultimate SET clause, and return them as a
							 | 
						||
| 
								 | 
							
								        sequence of 2-tuples.  Each tuple
							 | 
						||
| 
								 | 
							
								        contains a column expression as the key and a value to be rendered.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        E.g.::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            class Person(Base):
							 | 
						||
| 
								 | 
							
								                # ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                first_name = Column(String)
							 | 
						||
| 
								 | 
							
								                last_name = Column(String)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @hybrid_property
							 | 
						||
| 
								 | 
							
								                def fullname(self):
							 | 
						||
| 
								 | 
							
								                    return first_name + " " + last_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                @fullname.update_expression
							 | 
						||
| 
								 | 
							
								                def fullname(cls, value):
							 | 
						||
| 
								 | 
							
								                    fname, lname = value.split(" ", 1)
							 | 
						||
| 
								 | 
							
								                    return [
							 | 
						||
| 
								 | 
							
								                        (cls.first_name, fname),
							 | 
						||
| 
								 | 
							
								                        (cls.last_name, lname)
							 | 
						||
| 
								 | 
							
								                    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 1.2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return self._copy(update_expr=meth)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @util.memoized_property
							 | 
						||
| 
								 | 
							
								    def _expr_comparator(self):
							 | 
						||
| 
								 | 
							
								        if self.custom_comparator is not None:
							 | 
						||
| 
								 | 
							
								            return self._get_comparator(self.custom_comparator)
							 | 
						||
| 
								 | 
							
								        elif self.expr is not None:
							 | 
						||
| 
								 | 
							
								            return self._get_expr(self.expr)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self._get_expr(self.fget)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_expr(self, expr):
							 | 
						||
| 
								 | 
							
								        def _expr(cls):
							 | 
						||
| 
								 | 
							
								            return ExprComparator(cls, expr(cls), self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        util.update_wrapper(_expr, expr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self._get_comparator(_expr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_comparator(self, comparator):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        proxy_attr = attributes.create_proxied_attribute(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def expr_comparator(owner):
							 | 
						||
| 
								 | 
							
								            # because this is the descriptor protocol, we don't really know
							 | 
						||
| 
								 | 
							
								            # what our attribute name is.  so search for it through the
							 | 
						||
| 
								 | 
							
								            # MRO.
							 | 
						||
| 
								 | 
							
								            for lookup in owner.__mro__:
							 | 
						||
| 
								 | 
							
								                if self.__name__ in lookup.__dict__:
							 | 
						||
| 
								 | 
							
								                    if lookup.__dict__[self.__name__] is self:
							 | 
						||
| 
								 | 
							
								                        name = self.__name__
							 | 
						||
| 
								 | 
							
								                        break
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                name = attributes.NO_KEY
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return proxy_attr(
							 | 
						||
| 
								 | 
							
								                owner,
							 | 
						||
| 
								 | 
							
								                name,
							 | 
						||
| 
								 | 
							
								                self,
							 | 
						||
| 
								 | 
							
								                comparator(owner),
							 | 
						||
| 
								 | 
							
								                doc=comparator.__doc__ or self.__doc__,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return expr_comparator
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Comparator(interfaces.PropComparator):
							 | 
						||
| 
								 | 
							
								    """A helper class that allows easy construction of custom
							 | 
						||
| 
								 | 
							
								    :class:`~.orm.interfaces.PropComparator`
							 | 
						||
| 
								 | 
							
								    classes for usage with hybrids."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    property = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, expression):
							 | 
						||
| 
								 | 
							
								        self.expression = expression
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __clause_element__(self):
							 | 
						||
| 
								 | 
							
								        expr = self.expression
							 | 
						||
| 
								 | 
							
								        if hasattr(expr, "__clause_element__"):
							 | 
						||
| 
								 | 
							
								            expr = expr.__clause_element__()
							 | 
						||
| 
								 | 
							
								        return expr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def adapt_to_entity(self, adapt_to_entity):
							 | 
						||
| 
								 | 
							
								        # interesting....
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ExprComparator(Comparator):
							 | 
						||
| 
								 | 
							
								    def __init__(self, cls, expression, hybrid):
							 | 
						||
| 
								 | 
							
								        self.cls = cls
							 | 
						||
| 
								 | 
							
								        self.expression = expression
							 | 
						||
| 
								 | 
							
								        self.hybrid = hybrid
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __getattr__(self, key):
							 | 
						||
| 
								 | 
							
								        return getattr(self.expression, key)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def info(self):
							 | 
						||
| 
								 | 
							
								        return self.hybrid.info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _bulk_update_tuples(self, value):
							 | 
						||
| 
								 | 
							
								        if isinstance(self.expression, attributes.QueryableAttribute):
							 | 
						||
| 
								 | 
							
								            return self.expression._bulk_update_tuples(value)
							 | 
						||
| 
								 | 
							
								        elif self.hybrid.update_expr is not None:
							 | 
						||
| 
								 | 
							
								            return self.hybrid.update_expr(self.cls, value)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return [(self.expression, value)]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def property(self):
							 | 
						||
| 
								 | 
							
								        return self.expression.property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def operate(self, op, *other, **kwargs):
							 | 
						||
| 
								 | 
							
								        return op(self.expression, *other, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def reverse_operate(self, op, other, **kwargs):
							 | 
						||
| 
								 | 
							
								        return op(other, self.expression, **kwargs)
							 |