]> 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:45:48 +0000 (13:45 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 30 Jan 2011 18:45:48 +0000 (13:45 -0500)
a fixed name to be assigned to the alias object.
[ticket:2030]

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

diff --git a/CHANGES b/CHANGES
index 8c96a79ad4d486e1d73770caad4d0171f1cebe38..f1a9c35a5ec3fbcfb7dcdec45493ef962f21c8b9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,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 040938feb5d45cfac4ec7b26e4e0c33c54462509..1da9aad1ef938aa892f0f6035515ebf006c913e4 100644 (file)
@@ -424,18 +424,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 122f90e6c19076ea9e3b4fda915285f5312a7dcf..42c7a29990c67bcd24a827396b8be0353568d4bf 100644 (file)
@@ -617,22 +617,39 @@ def intersect_all(*selects, **kwargs):
     return CompoundSelect(CompoundSelect.INTERSECT_ALL, *selects, **kwargs)
 
 def alias(selectable, alias=None):
-    """Return an :class:`Alias` object.
+    """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.
-
-      selectable
-        any :class:`FromClause` subclass, such as a table, select
-        statement, etc..
-
-      alias
-        string name to be assigned as the alias.  If ``None``, a
-        random name will be generated.
+    Similar functionality is available via the 
+    :meth:`~.FromClause.alias` method
+    available on all :class:`.FromClause` subclasses.
+
+    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``.
+
+    The ``alias`` 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 alias: string name to be assigned as the alias.
+        If ``None``, a name will be deterministically generated
+        at compile time.
+        **Deprecated** - this argument is called ``name`` in 
+        0.7 for consistency with the other aliasing methods.
 
     """
     return Alias(selectable, alias=alias)
@@ -2141,19 +2158,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)
+            
+        See :func:`~.expression.alias` for details.
 
         """
 
@@ -3080,6 +3092,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()
 
@@ -3136,12 +3155,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
@@ -3164,11 +3191,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
+            )
 
+        See :func:`~.expression.alias` for further details on 
+        aliases.
+        
         """
         return self.select(use_labels=True, correlate=False).alias(name)
 
@@ -3191,9 +3239,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 bb512b3b2ae3000e0fc952a545019974ae7bad20..120984e95a1aec7d434868fd7ea71b4dfffba86e 100644 (file)
@@ -682,6 +682,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()