return ColumnProperty(deferred=True, *columns, **kw)
-def query_expression():
+def query_expression(default_expr=_sql.null()):
"""Indicate an attribute that populates from a query-time SQL expression.
+ :param default_expr: Optional SQL expression object that will be used in
+ all cases if not assigned later with :func:`_orm.with_expression`.
+ E.g.::
+
+ from sqlalchemy.sql import literal
+
+ class C(Base):
+ #...
+ my_expr = query_expression(literal(1))
+
+ .. versionadded:: 1.3.18
+
+
.. versionadded:: 1.2
.. seealso::
:ref:`mapper_querytime_expression`
"""
- prop = ColumnProperty(_sql.null())
+ prop = ColumnProperty(default_expr)
prop.strategy_key = (("query_expression", True),)
return prop
def __init__(self, parent, strategy_key):
super(ExpressionColumnLoader, self).__init__(parent, strategy_key)
+ null = sql.null()
+ self._have_default_expression = any(
+ not c.compare(null) for c in self.parent_property.columns
+ )
+
def setup_query(
self,
compile_state,
memoized_populators,
**kwargs
):
-
+ columns = None
if loadopt and "expression" in loadopt.local_opts:
columns = [loadopt.local_opts["expression"]]
+ elif self._have_default_expression:
+ columns = self.parent_property.columns
- for c in columns:
- if adapter:
- c = adapter.columns[c]
- column_collection.append(c)
+ if columns is None:
+ return
- fetch = columns[0]
+ for c in columns:
if adapter:
- fetch = adapter.columns[fetch]
- memoized_populators[self.parent_property] = fetch
+ c = adapter.columns[c]
+ column_collection.append(c)
+
+ fetch = columns[0]
+ if adapter:
+ fetch = adapter.columns[fetch]
+ memoized_populators[self.parent_property] = fetch
def create_row_processor(
self, context, path, loadopt, mapper, result, adapter, populators
from sqlalchemy.orm import undefer_group
from sqlalchemy.orm import with_expression
from sqlalchemy.orm import with_polymorphic
+from sqlalchemy.sql import literal
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
b_expr = query_expression()
+ class C(fixtures.ComparableEntity, Base):
+ __tablename__ = "c"
+ id = Column(Integer, primary_key=True)
+ x = Column(Integer)
+
+ c_expr = query_expression(literal(1))
+
@classmethod
def insert_data(cls, connection):
- A, B = cls.classes("A", "B")
+ A, B, C = cls.classes("A", "B", "C")
s = Session(connection)
s.add_all(
A(id=2, x=2, y=3),
A(id=3, x=5, y=10, bs=[B(id=3, p=5, q=0)]),
A(id=4, x=2, y=10, bs=[B(id=4, p=19, q=8), B(id=5, p=5, q=5)]),
+ C(id=1, x=1),
+ C(id=2, x=2),
]
)
eq_(a1.all(), [A(my_expr=5), A(my_expr=15), A(my_expr=12)])
+ def test_expr_default_value(self):
+ A = self.classes.A
+ C = self.classes.C
+ s = Session()
+
+ a1 = s.query(A).order_by(A.id).filter(A.x > 1)
+ eq_(a1.all(), [A(my_expr=None), A(my_expr=None), A(my_expr=None)])
+
+ c1 = s.query(C).order_by(C.id)
+ eq_(c1.all(), [C(c_expr=1), C(c_expr=1)])
+
+ c2 = (
+ s.query(C)
+ .options(with_expression(C.c_expr, C.x * 2))
+ .filter(C.x > 1)
+ .order_by(C.id)
+ )
+ eq_(c2.all(), [C(c_expr=4)])
+
def test_reuse_expr(self):
A = self.classes.A