summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES62
-rw-r--r--lib/sqlalchemy/orm/query.py13
-rw-r--r--test/orm/query.py16
3 files changed, 86 insertions, 5 deletions
diff --git a/CHANGES b/CHANGES
index 5dea65f02..3ffa58203 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,18 +13,72 @@ CHANGES
- the functionality of query.with_polymorphic() has
been added to mapper() as a configuration option.
+
It's set via several forms:
with_polymorphic='*'
- with_polymorphic=(mappers, selectable)
with_polymorphic=[mappers]
+ with_polymorphic=('*', selectable)
+ with_polymorphic=([mappers], selectable)
+
+ This controls the default polymorphic loading strategy
+ for inherited mappers. When a selectable is not given,
+ outer joins are created for all joined-table inheriting
+ mappers requested. Note that the auto-create of joins
+ is not compatible with concrete table inheritance.
- The existing select_table flag to mapper() is now
+ The existing select_table flag on mapper() is now
deprecated and is synonymous with
with_polymorphic('*', select_table). Note that the
underlying "guts" of select_table have been
completely removed and replaced with the newer,
- more flexible approach.
-
+ more flexible approach.
+
+ The new approach also automatically allows eager loads
+ to work for subclasses, if they are present, for
+ example
+ sess.query(Company).options(
+ eagerload_all(
+ [Company.employees.of_type(Engineer), 'machines']
+ ))
+ to load Company objects, their employees, and the
+ 'machines' collection of employees who happen to be
+ Engineers. A "with_polymorphic" Query option should be
+ introduced soon as well which would allow per-Query
+ control of with_polymorphic() on relations.
+
+ - added two "experimental" features to Query,
+ "experimental" in that their specific name/behavior
+ is not carved in stone just yet: _values() and
+ _from_self(). We'd like feedback on these.
+
+ - _values(*columns) is given a list of column
+ expressions, and returns a new Query that only
+ returns those columns. When evaluated, the return
+ value is a list of tuples just like when using
+ add_column() or add_entity(), the only difference is
+ that "entity zero", i.e. the mapped class, is not
+ included in the results. This means it finally makes
+ sense to use group_by() and having() on Query, which
+ have been sitting around uselessly until now.
+
+ A future change to this method may include that its
+ ability to join, filter and allow other options not
+ related to a "resultset" are removed, so the feedback
+ we're looking for is how people want to use
+ _values()...i.e. at the very end, or do people prefer
+ to continue generating after it's called.
+
+ - _from_self() compiles the SELECT statement for the
+ Query (minus any eager loaders), and returns a new
+ Query that selects from that SELECT. So basically you
+ can query from a Query without needing to extract the
+ SELECT statement manually. This gives meaning to
+ operations like query[3:5]._from_self().filter(some
+ criterion). There's not much controversial here
+ except that you can quickly create highly nested
+ queries that are less efficient, and we want feedback
+ on the naming choice.
+
- query.order_by() and query.group_by() will accept
multiple arguments using *args (like select()
already does).
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 1d4b24e0c..9751cc9ba 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -360,7 +360,18 @@ class Query(object):
q._entities = q._entities + [_MapperEntity(mapper=entity, alias=alias, id=id)]
return q
-
+
+ def _from_self(self):
+ """return a Query that selects from this Query's SELECT statement.
+
+ The API for this method hasn't been decided yet and is subject to change.
+ """
+
+ q = self._clone()
+ q._eager_loaders = util.Set()
+ fromclause = q.compile()
+ return Query(self.mapper, self.session).select_from(fromclause)
+
def _values(self, *columns):
"""Turn this query into a 'columns only' query.
diff --git a/test/orm/query.py b/test/orm/query.py
index a4dbb938d..b985202b4 100644
--- a/test/orm/query.py
+++ b/test/orm/query.py
@@ -408,6 +408,22 @@ class FilterTest(QueryTest):
# o2m
self.assertEquals([User(id=10)], sess.query(User).filter(User.addresses==None).all())
self.assertEquals([User(id=7),User(id=8),User(id=9)], sess.query(User).filter(User.addresses!=None).all())
+
+class FromSelfTest(QueryTest):
+ def test_filter(self):
+
+ assert [User(id=8), User(id=9)] == create_session().query(User).filter(User.id.in_([8,9]))._from_self().all()
+
+ assert [User(id=8), User(id=9)] == create_session().query(User)[1:3]._from_self().all()
+ assert [User(id=8)] == list(create_session().query(User).filter(User.id.in_([8,9]))._from_self()[0:1])
+
+ def test_join(self):
+ assert [
+ (User(id=8), Address(id=2)),
+ (User(id=8), Address(id=3)),
+ (User(id=8), Address(id=4)),
+ (User(id=9), Address(id=5))
+ ] == create_session().query(User).filter(User.id.in_([8,9]))._from_self().join('addresses').add_entity(Address).all()
class AggregateTest(QueryTest):
def test_sum(self):