]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added a `name` argument to `Query.subquery()`, to allow
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 30 Jan 2011 18:39:08 +0000 (13:39 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 30 Jan 2011 18:39:08 +0000 (13:39 -0500)
a fixed name to be assigned to the alias object.
[ticket:2030]
- changed the kw name 'alias' to 'name' on the alias() standalone
function.
- fixed up some alias/join docstrings

CHANGES
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/expression.py
test/orm/test_query.py

diff --git a/CHANGES b/CHANGES
index 210bfbc519ac348cb553ec4b8efd411de8937995..bd8294b5f549d6a58e563c1b7b0fc94c0b0eeb20 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -176,6 +176,11 @@ CHANGES
 
 0.6.7
 =====
+- orm
+  - Added a `name` argument to `Query.subquery()`, to allow
+    a fixed name to be assigned to the alias object.
+    [ticket:2030]
+
 - sql
   - Column.copy(), as used in table.tometadata(), copies the 
     'doc' attribute.  [ticket:2028]
index 42dd60a2fcfbcba9e94f5199a0fd005dba9e0b59..79b0809277d6dbdf5d1481b91f45dec8d813f654 100644 (file)
@@ -420,18 +420,25 @@ class Query(object):
             stmt = stmt.params(self._params)
         return stmt._annotate({'_halt_adapt': True})
 
-    def subquery(self):
+    def subquery(self, name=None):
         """return the full SELECT statement represented by this :class:`.Query`, 
         embedded within an :class:`.Alias`.
 
         Eager JOIN generation within the query is disabled.
 
-        The statement by default will not have disambiguating labels
-        applied to the construct unless with_labels(True) is called
-        first.
+        The statement will not have disambiguating labels
+        applied to the list of selected columns unless the 
+        :meth:`.Query.with_labels` method is used to generate a new
+        :class:`.Query` with the option enabled.
+
+        :param name: string name to be assigned as the alias; 
+            this is passed through to :meth:`.FromClause.alias`.
+            If ``None``, a name will be deterministically generated
+            at compile time.
+        
 
         """
-        return self.enable_eagerloads(False).statement.alias()
+        return self.enable_eagerloads(False).statement.alias(name=name)
 
     def label(self, name):
         """Return the full SELECT statement represented by this :class:`.Query`, converted 
index 64274359b15f6df56967f9cb06ae5b860f6bebdc..10041940a1141ae78519e02a3ecfc84ce07f6f70 100644 (file)
@@ -636,23 +636,38 @@ def intersect_all(*selects, **kwargs):
     """
     return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects, **kwargs)
 
-def alias(selectable, alias=None):
-    """Return an :class:`Alias` object.
+def alias(selectable, name=None):
+    """Return an :class:`.Alias` object.
 
-    An :class:`Alias` represents any :class:`FromClause`
+    An :class:`.Alias` represents any :class:`.FromClause`
     with an alternate name assigned within SQL, typically using the ``AS``
     clause when generated, e.g. ``SELECT * FROM table AS aliasname``.
 
-    Similar functionality is available via the :func:`alias()` method
-    available on all :class:`FromClause` subclasses.
+    Similar functionality is available via the 
+    :meth:`~.FromClause.alias` method
+    available on all :class:`.FromClause` subclasses.
 
-      selectable
-        any :class:`FromClause` subclass, such as a table, select
-        statement, etc..
+    When an :class:`.Alias` is created from a :class:`.Table` object,
+    this has the effect of the table being rendered
+    as ``tablename AS aliasname`` in a SELECT statement.
+    
+    For :func:`.select` objects, the effect is that of creating a named
+    subquery, i.e. ``(select ...) AS aliasname``.
 
-      alias
-        string name to be assigned as the alias.  If ``None``, a
-        random name will be generated.
+    The ``name`` parameter is optional, and provides the name
+    to use in the rendered SQL.  If blank, an "anonymous" name
+    will be deterministically generated at compile time.  
+    Deterministic means the name is guaranteed to be unique against
+    other constructs used in the same statement, and will also be the
+    same name for each successive compilation of the same statement
+    object.
+
+    :param selectable: any :class:`.FromClause` subclass,
+        such as a table, select statement, etc.
+
+    :param name: string name to be assigned as the alias.
+        If ``None``, a name will be deterministically generated
+        at compile time.
 
     """
     return Alias(selectable, alias=alias)
@@ -2232,19 +2247,14 @@ class FromClause(Selectable):
         return Join(self, right, onclause, True)
 
     def alias(self, name=None):
-        """return an alias of this :class:`FromClause`.
-
-        For table objects, this has the effect of the table being rendered
-        as ``tablename AS aliasname`` in a SELECT statement.
-        For select objects, the effect is that of creating a named
-        subquery, i.e. ``(select ...) AS aliasname``.
-        The :func:`alias()` method is the general way to create
-        a "subquery" out of an existing SELECT.
-
-        The ``name`` parameter is optional, and if left blank an 
-        "anonymous" name will be generated at compile time, guaranteed
-        to be unique against other anonymous constructs used in the
-        same statement.
+        """return an alias of this :class:`.FromClause`.
+        
+        This is shorthand for calling::
+        
+            from sqlalchemy import alias
+            a = alias(self, name=name)
+            
+        See :func:`~.expression.alias` for details.
 
         """
 
@@ -3190,6 +3200,13 @@ class Join(FromClause):
     __visit_name__ = 'join'
 
     def __init__(self, left, right, onclause=None, isouter=False):
+        """Construct a new :class:`.Join`.
+        
+        The usual entrypoint here is the :func:`~.expression.join`
+        function or the :meth:`.FromClause.join` method of any
+        :class:`.FromClause` object.
+        
+        """
         self.left = _literal_as_text(left)
         self.right = _literal_as_text(right).self_group()
 
@@ -3246,12 +3263,20 @@ class Join(FromClause):
 
     def select(self, whereclause=None, fold_equivalents=False, **kwargs):
         """Create a :class:`Select` from this :class:`Join`.
-
+        
+        The equivalent long-hand form, given a :class:`.Join` object
+        ``j``, is::
+        
+            from sqlalchemy import select
+            j = select([j.left, j.right], **kw).\\
+                        where(whereclause).\\
+                        select_from(j)
+            
         :param whereclause: the WHERE criterion that will be sent to 
           the :func:`select()` function
 
         :param fold_equivalents: based on the join criterion of this 
-          :class:`Join`, do not include
+          :class:`.Join`, do not include
           repeat column names in the column list of the resulting
           select, for columns that are calculated to be "equivalent"
           based on the join criterion of this :class:`Join`. This will
@@ -3274,11 +3299,32 @@ class Join(FromClause):
         return self.left.bind or self.right.bind
 
     def alias(self, name=None):
-        """Create a :class:`Select` out of this :class:`Join` clause and
-        return an :class:`Alias` of it.
-
-        The :class:`Select` is not correlating.
+        """return an alias of this :class:`.Join`.
+        
+        Used against a :class:`.Join` object,
+        :meth:`~.Join.alias` calls the :meth:`~.Join.select`
+        method first so that a subquery against a 
+        :func:`.select` construct is generated.
+        the :func:`~expression.select` construct also has the 
+        ``correlate`` flag set to ``False`` and will not
+        auto-correlate inside an enclosing :func:`~expression.select`
+        construct.
+        
+        The equivalent long-hand form, given a :class:`.Join` object
+        ``j``, is::
+        
+            from sqlalchemy import select, alias
+            j = alias(
+                select([j.left, j.right]).\\
+                    select_from(j).\\
+                    with_labels(True).\\
+                    correlate(False),
+                name=name
+            )
 
+        See :func:`~.expression.alias` for further details on 
+        aliases.
+        
         """
         return self.select(use_labels=True, correlate=False).alias(name)
 
@@ -3301,9 +3347,9 @@ class Alias(FromClause):
     sub-select within a SQL statement using the ``AS`` keyword (or
     without the keyword on certain databases such as Oracle).
 
-    This object is constructed from the :func:`alias()` module level
-    function as well as the :func:`alias()` method available on all
-    :class:`FromClause` subclasses.
+    This object is constructed from the :func:`~.expression.alias` module level
+    function as well as the :meth:`.FromClause.alias` method available on all
+    :class:`.FromClause` subclasses.
 
     """
 
index 948d04620ec73c0966073d725f978029ae662ff6..8e94f56c933af29964d85e18cfb68d5b7a7a7c27 100644 (file)
@@ -656,6 +656,17 @@ class ExpressionTest(QueryTest, AssertsCompiledSQL):
 
         eq_(User(id=7), q.one())
 
+    def test_named_subquery(self):
+        session = create_session()
+        a1 = session.query(User.id).filter(User.id==7).subquery('foo1')
+        a2 = session.query(User.id).filter(User.id==7).subquery(name='foo2')
+        a3 = session.query(User.id).filter(User.id==7).subquery()
+
+        eq_(a1.name, 'foo1')
+        eq_(a2.name, 'foo2')
+        eq_(a3.name, '%%(%d anon)s' % id(a3))
+
+
     def test_label(self):
         session = create_session()