Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

queryset-refactor: Added the ability to apply parameters to the select

fragments in QuerySet.extra(). Refs #2902


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6603 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f951d97d9937370a602753f0d530e5b2bbdac69d 1 parent abcb70e
Malcolm Tredinnick authored October 24, 2007
9  django/db/models/query.py
@@ -88,7 +88,6 @@ def iterator(self):
88 88
         max_depth = self.query.max_depth
89 89
         index_end = len(self.model._meta.fields)
90 90
         extra_select = self.query.extra_select.keys()
91  
-        extra_select.sort()
92 91
         for row in self.query.results_iter():
93 92
             if fill_cache:
94 93
                 obj, index_end = get_cached_row(klass=self.model, row=row,
@@ -378,11 +377,7 @@ def __init__(self, *args, **kwargs):
378 377
         # names of the model fields to select.
379 378
 
380 379
     def iterator(self):
381  
-        extra_select = self.query.extra_select.keys()
382  
-        extra_select.sort()
383  
-        if extra_select:
384  
-            self.field_names.extend([f for f in extra_select])
385  
-
  380
+        self.field_names.extend([f for f in self.query.extra_select.keys()])
386 381
         for row in self.query.results_iter():
387 382
             yield dict(zip(self.field_names, row))
388 383
 
@@ -409,7 +404,7 @@ def _setup_query(self):
409 404
                 except KeyError, e:
410 405
                     raise FieldDoesNotExist('%s has no field named %r'
411 406
                                 % (opts.object_name, e.args[0]))
412  
-                field_names = self._fields
  407
+                field_names = list(self._fields)
413 408
             else:
414 409
                 fields = []
415 410
                 field_names = []
15  django/db/models/sql/query.py
@@ -10,7 +10,8 @@
10 10
 import copy
11 11
 import re
12 12
 
13  
-from django.utils import tree
  13
+from django.utils.tree import Node
  14
+from django.utils.datastructures import SortedDict
14 15
 from django.db.models.sql.where import WhereNode, AND, OR
15 16
 from django.db.models.sql.datastructures import Count, Date
16 17
 from django.db.models.fields import FieldDoesNotExist, Field
@@ -94,7 +95,7 @@ def __init__(self, model, connection):
94 95
 
95 96
         # These are for extensions. The contents are more or less appended
96 97
         # verbatim to the appropriate clause.
97  
-        self.extra_select = {}  # Maps col_alias -> col_sql.
  98
+        self.extra_select = SortedDict()  # Maps col_alias -> col_sql.
98 99
         self.extra_tables = []
99 100
         self.extra_where = []
100 101
         self.extra_params = []
@@ -364,12 +365,8 @@ def get_columns(self):
364 365
                     for f in self.model._meta.fields]
365 366
             aliases = result[:]
366 367
 
367  
-        # We sort extra_select so that the result columns are in a well-defined
368  
-        # order (and thus QuerySet.iterator can extract them correctly).
369  
-        extra_select = self.extra_select.items()
370  
-        extra_select.sort()
371 368
         result.extend(['(%s) AS %s' % (col, alias)
372  
-                for alias, col in extra_select])
  369
+                for alias, col in self.extra_select.items()])
373 370
         aliases.extend(self.extra_select.keys())
374 371
 
375 372
         self._select_aliases = dict.fromkeys(aliases)
@@ -761,7 +758,7 @@ def add_q(self, q_object):
761 758
             return
762 759
 
763 760
         for child in q_object.children:
764  
-            if isinstance(child, tree.Node):
  761
+            if isinstance(child, Node):
765 762
                 self.where.start_subtree(q_object.connection)
766 763
                 self.add_q(child)
767 764
                 self.where.end_subtree()
@@ -937,7 +934,7 @@ def add_count_column(self):
937 934
             # level.
938 935
             self.distinct = False
939 936
         self.select = [select]
940  
-        self.extra_select = {}
  937
+        self.extra_select = SortedDict()
941 938
 
942 939
     def execute_sql(self, result_type=MULTI):
943 940
         """
28  docs/db-api.txt
@@ -820,6 +820,34 @@ of the arguments is required, but you should use at least one of them.
820 820
     some database backends, such as some MySQL versions, don't support
821 821
     subqueries.
822 822
 
  823
+    **New in Django development version**
  824
+    In some rare cases, you might wish to pass parameters to the SQL fragments
  825
+    in ``extra(select=...)```. Since the ``params`` attribute is a sequence
  826
+    and the ``select`` attribute is a dictionary, some care is required so
  827
+    that the parameters are matched up correctly with the extra select pieces.
  828
+    Firstly, in this situation, you should use a
  829
+    ``django.utils.datastructures.SortedDict`` for the ``select`` value, not
  830
+    just a normal Python dictionary. Secondly, make sure that your parameters
  831
+    for the ``select`` come first in the list and that you have not passed any
  832
+    parameters to an earlier ``extra()`` call for this queryset.
  833
+
  834
+    This will work::
  835
+
  836
+        Blog.objects.extra(
  837
+            select=SortedDict(('a', '%s'), ('b', '%s')),
  838
+            params=('one', 'two'))
  839
+
  840
+    ... while this won't::
  841
+
  842
+        # Will not work!
  843
+        Blog.objects.extra(where=['foo=%s'], params=('bar',)).extra(
  844
+            select=SortedDict(('a', '%s'), ('b', '%s')),
  845
+            params=('one', 'two'))
  846
+
  847
+    In the second example, the earlier ``params`` usage will mess up the later
  848
+    one. So always put your extra select pieces in the first ``extra()`` call
  849
+    if you need to use parameters in them.
  850
+
823 851
 ``where`` / ``tables``
824 852
     You can define explicit SQL ``WHERE`` clauses -- perhaps to perform
825 853
     non-explicit joins -- by using ``where``. You can manually add tables to
15  tests/regressiontests/queries/models.py
@@ -379,5 +379,20 @@ class Y(models.Model):
379 379
 >>> q1 = Item.objects.order_by('name')
380 380
 >>> id(q1) == id(q1.all())
381 381
 False
  382
+
  383
+Bug #2902
  384
+Parameters can be given to extra_select, *if* you use a SortedDict.
  385
+
  386
+(First we need to know which order the keys fall in "naturally" on your system,
  387
+so we can put things in the wrong way around from normal. A normal dict would
  388
+thus fail.)
  389
+>>> from django.utils.datastructures import SortedDict
  390
+>>> s = [('a', '%s'), ('b', '%s')]
  391
+>>> params = ['one', 'two']
  392
+>>> if {'a': 1, 'b': 2}.keys() == ['a', 'b']:
  393
+...     s.reverse()
  394
+...     params.reverse()
  395
+>>> Item.objects.extra(select=SortedDict(s), params=params).values('a','b')[0]
  396
+{'a': u'one', 'b': u'two'}
382 397
 """}
383 398
 

0 notes on commit f951d97

Please sign in to comment.
Something went wrong with that request. Please try again.