Author: mtredinnick
Date: 2007-10-23 23:22:23 -0500 (Tue, 23 Oct 2007)
New Revision: 6603

Modified:
   django/branches/queryset-refactor/django/db/models/query.py
   django/branches/queryset-refactor/django/db/models/sql/query.py
   django/branches/queryset-refactor/docs/db-api.txt
   django/branches/queryset-refactor/tests/regressiontests/queries/models.py
Log:
queryset-refactor: Added the ability to apply parameters to the select
fragments in QuerySet.extra(). Refs #2902


Modified: django/branches/queryset-refactor/django/db/models/query.py
===================================================================
--- django/branches/queryset-refactor/django/db/models/query.py 2007-10-23 
19:11:15 UTC (rev 6602)
+++ django/branches/queryset-refactor/django/db/models/query.py 2007-10-24 
04:22:23 UTC (rev 6603)
@@ -88,7 +88,6 @@
         max_depth = self.query.max_depth
         index_end = len(self.model._meta.fields)
         extra_select = self.query.extra_select.keys()
-        extra_select.sort()
         for row in self.query.results_iter():
             if fill_cache:
                 obj, index_end = get_cached_row(klass=self.model, row=row,
@@ -378,11 +377,7 @@
         # names of the model fields to select.
 
     def iterator(self):
-        extra_select = self.query.extra_select.keys()
-        extra_select.sort()
-        if extra_select:
-            self.field_names.extend([f for f in extra_select])
-
+        self.field_names.extend([f for f in self.query.extra_select.keys()])
         for row in self.query.results_iter():
             yield dict(zip(self.field_names, row))
 
@@ -409,7 +404,7 @@
                 except KeyError, e:
                     raise FieldDoesNotExist('%s has no field named %r'
                                 % (opts.object_name, e.args[0]))
-                field_names = self._fields
+                field_names = list(self._fields)
             else:
                 fields = []
                 field_names = []

Modified: django/branches/queryset-refactor/django/db/models/sql/query.py
===================================================================
--- django/branches/queryset-refactor/django/db/models/sql/query.py     
2007-10-23 19:11:15 UTC (rev 6602)
+++ django/branches/queryset-refactor/django/db/models/sql/query.py     
2007-10-24 04:22:23 UTC (rev 6603)
@@ -10,7 +10,8 @@
 import copy
 import re
 
-from django.utils import tree
+from django.utils.tree import Node
+from django.utils.datastructures import SortedDict
 from django.db.models.sql.where import WhereNode, AND, OR
 from django.db.models.sql.datastructures import Count, Date
 from django.db.models.fields import FieldDoesNotExist, Field
@@ -94,7 +95,7 @@
 
         # These are for extensions. The contents are more or less appended
         # verbatim to the appropriate clause.
-        self.extra_select = {}  # Maps col_alias -> col_sql.
+        self.extra_select = SortedDict()  # Maps col_alias -> col_sql.
         self.extra_tables = []
         self.extra_where = []
         self.extra_params = []
@@ -364,12 +365,8 @@
                     for f in self.model._meta.fields]
             aliases = result[:]
 
-        # We sort extra_select so that the result columns are in a well-defined
-        # order (and thus QuerySet.iterator can extract them correctly).
-        extra_select = self.extra_select.items()
-        extra_select.sort()
         result.extend(['(%s) AS %s' % (col, alias)
-                for alias, col in extra_select])
+                for alias, col in self.extra_select.items()])
         aliases.extend(self.extra_select.keys())
 
         self._select_aliases = dict.fromkeys(aliases)
@@ -761,7 +758,7 @@
             return
 
         for child in q_object.children:
-            if isinstance(child, tree.Node):
+            if isinstance(child, Node):
                 self.where.start_subtree(q_object.connection)
                 self.add_q(child)
                 self.where.end_subtree()
@@ -937,7 +934,7 @@
             # level.
             self.distinct = False
         self.select = [select]
-        self.extra_select = {}
+        self.extra_select = SortedDict()
 
     def execute_sql(self, result_type=MULTI):
         """

Modified: django/branches/queryset-refactor/docs/db-api.txt
===================================================================
--- django/branches/queryset-refactor/docs/db-api.txt   2007-10-23 19:11:15 UTC 
(rev 6602)
+++ django/branches/queryset-refactor/docs/db-api.txt   2007-10-24 04:22:23 UTC 
(rev 6603)
@@ -820,6 +820,34 @@
     some database backends, such as some MySQL versions, don't support
     subqueries.
 
+    **New in Django development version**
+    In some rare cases, you might wish to pass parameters to the SQL fragments
+    in ``extra(select=...)```. Since the ``params`` attribute is a sequence
+    and the ``select`` attribute is a dictionary, some care is required so
+    that the parameters are matched up correctly with the extra select pieces.
+    Firstly, in this situation, you should use a
+    ``django.utils.datastructures.SortedDict`` for the ``select`` value, not
+    just a normal Python dictionary. Secondly, make sure that your parameters
+    for the ``select`` come first in the list and that you have not passed any
+    parameters to an earlier ``extra()`` call for this queryset.
+
+    This will work::
+
+        Blog.objects.extra(
+            select=SortedDict(('a', '%s'), ('b', '%s')),
+            params=('one', 'two'))
+
+    ... while this won't::
+
+        # Will not work!
+        Blog.objects.extra(where=['foo=%s'], params=('bar',)).extra(
+            select=SortedDict(('a', '%s'), ('b', '%s')),
+            params=('one', 'two'))
+
+    In the second example, the earlier ``params`` usage will mess up the later
+    one. So always put your extra select pieces in the first ``extra()`` call
+    if you need to use parameters in them.
+
 ``where`` / ``tables``
     You can define explicit SQL ``WHERE`` clauses -- perhaps to perform
     non-explicit joins -- by using ``where``. You can manually add tables to

Modified: 
django/branches/queryset-refactor/tests/regressiontests/queries/models.py
===================================================================
--- django/branches/queryset-refactor/tests/regressiontests/queries/models.py   
2007-10-23 19:11:15 UTC (rev 6602)
+++ django/branches/queryset-refactor/tests/regressiontests/queries/models.py   
2007-10-24 04:22:23 UTC (rev 6603)
@@ -379,5 +379,20 @@
 >>> q1 = Item.objects.order_by('name')
 >>> id(q1) == id(q1.all())
 False
+
+Bug #2902
+Parameters can be given to extra_select, *if* you use a SortedDict.
+
+(First we need to know which order the keys fall in "naturally" on your system,
+so we can put things in the wrong way around from normal. A normal dict would
+thus fail.)
+>>> from django.utils.datastructures import SortedDict
+>>> s = [('a', '%s'), ('b', '%s')]
+>>> params = ['one', 'two']
+>>> if {'a': 1, 'b': 2}.keys() == ['a', 'b']:
+...     s.reverse()
+...     params.reverse()
+>>> Item.objects.extra(select=SortedDict(s), params=params).values('a','b')[0]
+{'a': u'one', 'b': u'two'}
 """}
 


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to