--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 6320
+
+ Fixed bug in new :func:`_orm.with_loader_criteria` feature where using a
+ mixin class with :func:`_orm.declared_attr` on an attribute that were
+ accessed inside the custom lambda would emit a warning regarding using an
+ unmapped declared attr, when the lambda callable were first initialized.
+ This warning is now prevented using special instrumentation for this
+ lambda initialization step.
+
return "aliased(%s)" % (self._target.__name__,)
+class _WrapUserEntity(object):
+ """A wrapper used within the loader_criteria lambda caller so that
+ we can bypass declared_attr descriptors on unmapped mixins, which
+ normally emit a warning for such use.
+
+ might also be useful for other per-lambda instrumentations should
+ the need arise.
+
+ """
+
+ def __init__(self, subject):
+ self.subject = subject
+
+ @util.preload_module("sqlalchemy.orm.decl_api")
+ def __getattribute__(self, name):
+ decl_api = util.preloaded.orm.decl_api
+
+ subject = object.__getattribute__(self, "subject")
+ if name in subject.__dict__ and isinstance(
+ subject.__dict__[name], decl_api.declared_attr
+ ):
+ return subject.__dict__[name].fget(subject)
+ else:
+ return getattr(subject, name)
+
+
class LoaderCriteriaOption(CriteriaOption):
"""Add additional WHERE criteria to the load for all occurrences of
a particular entity.
where_criteria,
roles.WhereHavingRole,
lambda_args=(
- self.root_entity
- if self.root_entity is not None
- else self.entity.entity,
+ _WrapUserEntity(
+ self.root_entity
+ if self.root_entity is not None
+ else self.entity.entity,
+ ),
),
opts=lambdas.LambdaOptions(
track_closure_variables=track_closure_variables
from sqlalchemy.orm import Session
from sqlalchemy.orm import subqueryload
from sqlalchemy.orm import with_loader_criteria
+from sqlalchemy.orm.decl_api import declared_attr
from sqlalchemy.testing import eq_
from sqlalchemy.testing.assertsql import CompiledSQL
from test.orm import _fixtures
)
return HasFoob, UserWFoob
+ @testing.fixture
+ def declattr_mixin_fixture(self):
+ users = self.tables.users
+
+ class HasFoob(object):
+ @declared_attr
+ def name(cls):
+ return Column(String)
+
+ class UserWFoob(HasFoob, self.Comparable):
+ pass
+
+ mapper(
+ UserWFoob,
+ users,
+ )
+ return HasFoob, UserWFoob
+
@testing.fixture
def multi_mixin_fixture(self):
orders, items = self.tables.orders, self.tables.items
[Order(description="order 3")],
)
+ def test_declared_attr_no_warning(self, declattr_mixin_fixture):
+ HasFoob, UserWFoob = declattr_mixin_fixture
+
+ statement = select(UserWFoob).filter(UserWFoob.id < 10)
+
+ def go(value):
+ return statement.options(
+ with_loader_criteria(
+ HasFoob,
+ lambda cls: cls.name == value,
+ include_aliases=True,
+ )
+ )
+
+ s = Session(testing.db, future=True)
+
+ for i in range(10):
+ name = random.choice(["ed", "fred", "jack"])
+ stmt = go(name)
+
+ eq_(s.execute(stmt).scalars().all(), [UserWFoob(name=name)])
+
def test_caching_and_binds_lambda_more_mixins(self, multi_mixin_fixture):
# By including non-mapped mixin HasBat in the middle of the
# hierarchy, we test issue #5766