Project Versions

“hybrid” means the attribute has distinct behaviors defined at the
class level and at the instance level.

The 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 hybrid_method or
hybrid_property may receive self as an instance of the class, or
as the class itself:

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.length5

When dealing with the Interval class itself, the hybrid_property
descriptor evaluates the function body given the Interval class as
the argument, which when evaluated with SQLAlchemy expression mechanics
returns a new SQL expression:

The Interval class example also illustrates two methods, contains() and intersects(),
decorated with hybrid_method.
This decorator applies the same idea to methods that 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:

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 hybrid decorators
define the 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:

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. Two variants of we’ll illustrate
are the “join-dependent” hybrid, and the “correlated subquery” hybrid.

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:

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:

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 colunn expression.
A correlated subquery is more portable, but often performs more poorly
at the SQL level.
Using the same technique illustrated at Using column_property,
we can adjust our SavingsAccount example to aggregate the balances for
all accounts, and use a correlated subquery for the column expression:

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.

The example class below allows case-insensitive comparisons on the attribute
named word_insensitive:

The CaseInsensitiveComparator above implements part of the ColumnOperators
interface. A “coercion” operation like lowercasing can be applied to all comparison operations
(i.e. eq, lt, gt, etc.) using Operators.operate():

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:

classCaseInsensitiveWord(Comparator):"Hybrid value representing a lower case representation of a word."def__init__(self,word):ifisinstance(word,basestring):self.word=word.lower()elifisinstance(word,CaseInsensitiveWord):self.word=word.wordelse:self.word=func.lower(word)defoperate(self,op,other):ifnotisinstance(other,CaseInsensitiveWord):other=CaseInsensitiveWord(other)returnop(self.word,other.word)def__clause_element__(self):returnself.worddef__str__(self):returnself.wordkey='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:

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):

A transformer is an object which can receive a Query object and return a
new one. The Query object includes a method with_transformation()
that simply returns a new Query transformed by the given function.

We can combine this with the 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:

For the expression, things are not so clear. We’d need to construct a Query where we
join() twice along Node.parent to get to the grandparent. We can instead
return a transforming callable that we’ll combine with the Comparator class
to receive any Query object, and return a new one that’s joined to the Node.parent
attribute and filtered based on the given criterion:

The GrandparentTransformer overrides the core Operators.operate() method
at the base of the 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
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 Query first to join to Node.parent, then to compare parent_alias
using Operators.eq against the left and right sides, passing into
Query.filter:

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
AliasedClass object against Node. Below we use a simple
memoizing approach that associates a GrandparentTransformer
with each class:

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.