from sqlalchemy.orm.util import (
AliasedClass, ORMAdapter, _entity_descriptor, _entity_info,
_is_aliased_class, _is_mapped_class, _orm_columns, _orm_selectable,
- join as orm_join,
+ join as orm_join,with_parent
)
self._populate_existing = True
def with_parent(self, instance, property=None):
- """Add filtering criterion that relates this query's primary entity
- to the given related instance, using established :func:`.relationship()`
+ """Add filtering criterion that relates the given instance
+ to a child object or collection, using its attribute state
+ as well as an established :func:`.relationship()`
configuration.
- The SQL rendered is the same as that rendered when a lazy loader
- would fire off from the given parent on that attribute, meaning
- that the appropriate state is taken from the parent object in
- Python without the need to render joins to the parent table
- in the rendered statement.
-
- As of 0.6.4, this method accepts parent instances in all
- persistence states, including transient, persistent, and detached.
- Only the requisite primary key/foreign key attributes need to
- be populated. Previous versions didn't work with transient
- instances.
-
- :param instance:
- An instance which is related to the class represented by
- this query via some :func:`.relationship`, that also
- contains the appropriate attribute state that identifies
- the child object or collection.
-
- :param property:
- String property name, or class-bound attribute, which indicates
- what relationship should be used to reconcile the parent/child
- relationship. If None, the method will use the first relationship
- that links them together - note that this is not deterministic
- in the case of multiple relationships linking parent/child,
- so using None is not recommended.
-
+ The method uses the :func:`.with_parent` function to generate
+ the clause, the result of which is passed to :meth:`.Query.filter`.
+
+ Parameters are the same as :func:`.with_parent`, with the exception
+ that the given property can be None, in which case a search is
+ performed against this :class:`.Query` object's target mapper.
+
"""
- from sqlalchemy.orm import properties
- mapper = object_mapper(instance)
+
if property is None:
+ from sqlalchemy.orm import properties
+ mapper = object_mapper(instance)
+
for prop in mapper.iterate_properties:
if isinstance(prop, properties.PropertyLoader) and \
prop.mapper is self._mapper_zero():
+ property = prop
break
else:
raise sa_exc.InvalidRequestError(
self._mapper_zero().class_.__name__,
instance.__class__.__name__)
)
- else:
- prop = mapper.get_property(property, resolve_synonyms=True)
- return self.filter(prop.compare(
- operators.eq,
- instance, value_is_parent=True,
- detect_transient_pending=True))
+
+ return self.filter(with_parent(instance, property))
@_generative()
def add_entity(self, entity, alias=None):
return _ORMJoin(left, right, onclause, True, join_to_left)
def with_parent(instance, prop):
- """Return criterion which selects instances with a given parent.
-
- :param instance: a parent instance, which should be persistent
- or detached.
-
- :param property: a class-attached descriptor, MapperProperty or
- string property name
- attached to the parent instance.
-
- :param \**kwargs: all extra keyword arguments are propagated
- to the constructor of Query.
-
+ """Create filtering criterion that relates this query's primary entity
+ to the given related instance, using established :func:`.relationship()`
+ configuration.
+
+ The SQL rendered is the same as that rendered when a lazy loader
+ would fire off from the given parent on that attribute, meaning
+ that the appropriate state is taken from the parent object in
+ Python without the need to render joins to the parent table
+ in the rendered statement.
+
+ As of 0.6.4, this method accepts parent instances in all
+ persistence states, including transient, persistent, and detached.
+ Only the requisite primary key/foreign key attributes need to
+ be populated. Previous versions didn't work with transient
+ instances.
+
+ :param instance:
+ An instance which has some :func:`.relationship`.
+
+ :param property:
+ String property name, or class-bound attribute, which indicates
+ what relationship from the instance should be used to reconcile the
+ parent/child relationship.
+
"""
if isinstance(prop, basestring):
mapper = object_mapper(instance)
elif isinstance(prop, attributes.QueryableAttribute):
prop = prop.property
- return prop.compare(operators.eq, instance, value_is_parent=True)
+ return prop.compare(operators.eq,
+ instance,
+ value_is_parent=True,
+ detect_transient_pending=True)
def _entity_info(entity, compile=True):
o = sess.query(Order).with_parent(u1, property='orders').all()
assert [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o
+ o = sess.query(Order).with_parent(u1, property=User.orders).all()
+ assert [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o
+
o = sess.query(Order).filter(with_parent(u1, User.orders)).all()
assert [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o
[Order(description="order 1"), Order(description="order 3"), Order(description="order 5")],
o.all()
)
+
+ o = sess.query(Order).filter(with_parent(utrans, 'orders'))
+ eq_(
+ [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")],
+ o.all()
+ )
def test_with_pending_autoflush(self):
sess = Session()
sess.query(User).with_parent(opending, 'user').one(),
User(id=o1.user_id)
)
+ eq_(
+ sess.query(User).filter(with_parent(opending, 'user')).one(),
+ User(id=o1.user_id)
+ )
def test_with_pending_no_autoflush(self):
sess = Session(autoflush=False)