diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 1625a0e..f364b1d 100644
a
|
b
|
class SQLCompiler(object):
|
84 | 84 | if where: |
85 | 85 | result.append('WHERE %s' % where) |
86 | 86 | params.extend(w_params) |
87 | | if self.query.extra_where: |
88 | | if not where: |
89 | | result.append('WHERE') |
90 | | else: |
91 | | result.append('AND') |
92 | | result.append(' AND '.join(self.query.extra_where)) |
93 | 87 | |
94 | 88 | grouping, gb_params = self.get_grouping() |
95 | 89 | if grouping: |
… |
… |
class SQLCompiler(object):
|
124 | 118 | result.append('LIMIT %d' % val) |
125 | 119 | result.append('OFFSET %d' % self.query.low_mark) |
126 | 120 | |
127 | | params.extend(self.query.extra_params) |
128 | 121 | return ' '.join(result), tuple(params) |
129 | 122 | |
130 | 123 | def as_nested_sql(self): |
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index dde1494..2d3f610 100644
a
|
b
|
from django.db.models.sql import aggregates as base_aggregates_module
|
19 | 19 | from django.db.models.sql.constants import * |
20 | 20 | from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin |
21 | 21 | from django.db.models.sql.expressions import SQLEvaluator |
22 | | from django.db.models.sql.where import WhereNode, Constraint, EverythingNode, AND, OR |
| 22 | from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode, |
| 23 | ExtraWhere, AND, OR) |
23 | 24 | from django.core.exceptions import FieldError |
24 | 25 | |
25 | 26 | __all__ = ['Query', 'RawQuery'] |
… |
… |
class Query(object):
|
128 | 129 | self._extra_select_cache = None |
129 | 130 | |
130 | 131 | self.extra_tables = () |
131 | | self.extra_where = () |
132 | | self.extra_params = () |
133 | 132 | self.extra_order_by = () |
134 | 133 | |
135 | 134 | # A tuple that is a set of model field names and either True, if these |
… |
… |
class Query(object):
|
256 | 255 | else: |
257 | 256 | obj._extra_select_cache = self._extra_select_cache.copy() |
258 | 257 | obj.extra_tables = self.extra_tables |
259 | | obj.extra_where = self.extra_where |
260 | | obj.extra_params = self.extra_params |
261 | 258 | obj.extra_order_by = self.extra_order_by |
262 | 259 | obj.deferred_loading = deepcopy(self.deferred_loading) |
263 | 260 | if self.filter_is_sticky and self.used_aliases: |
… |
… |
class Query(object):
|
466 | 463 | if self.extra and rhs.extra: |
467 | 464 | raise ValueError("When merging querysets using 'or', you " |
468 | 465 | "cannot have extra(select=...) on both sides.") |
469 | | if self.extra_where and rhs.extra_where: |
470 | | raise ValueError("When merging querysets using 'or', you " |
471 | | "cannot have extra(where=...) on both sides.") |
472 | 466 | self.extra.update(rhs.extra) |
473 | 467 | extra_select_mask = set() |
474 | 468 | if self.extra_select_mask is not None: |
… |
… |
class Query(object):
|
478 | 472 | if extra_select_mask: |
479 | 473 | self.set_extra_mask(extra_select_mask) |
480 | 474 | self.extra_tables += rhs.extra_tables |
481 | | self.extra_where += rhs.extra_where |
482 | | self.extra_params += rhs.extra_params |
483 | 475 | |
484 | 476 | # Ordering uses the 'rhs' ordering, unless it has none, in which case |
485 | 477 | # the current ordering is used. |
… |
… |
class Query(object):
|
1611 | 1603 | select_pairs[name] = (entry, entry_params) |
1612 | 1604 | # This is order preserving, since self.extra_select is a SortedDict. |
1613 | 1605 | self.extra.update(select_pairs) |
1614 | | if where: |
1615 | | self.extra_where += tuple(where) |
1616 | | if params: |
1617 | | self.extra_params += tuple(params) |
| 1606 | if where or params: |
| 1607 | self.where.add(ExtraWhere(where, params), AND) |
1618 | 1608 | if tables: |
1619 | 1609 | self.extra_tables += tuple(tables) |
1620 | 1610 | if order_by: |
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 4aa2351..cf147c6 100644
a
|
b
|
class WhereNode(tree.Node):
|
220 | 220 | child.relabel_aliases(change_map) |
221 | 221 | elif isinstance(child, tree.Node): |
222 | 222 | self.relabel_aliases(change_map, child) |
223 | | else: |
| 223 | elif isinstance(child, (list, tuple)): |
224 | 224 | if isinstance(child[0], (list, tuple)): |
225 | 225 | elt = list(child[0]) |
226 | 226 | if elt[0] in change_map: |
… |
… |
class NothingNode(object):
|
254 | 254 | def relabel_aliases(self, change_map, node=None): |
255 | 255 | return |
256 | 256 | |
| 257 | class ExtraWhere(object): |
| 258 | def __init__(self, sqls, params): |
| 259 | self.sqls = sqls |
| 260 | self.params = params |
| 261 | |
| 262 | def as_sql(self, qn=None, connection=None): |
| 263 | return " AND ".join(self.sqls), tuple(self.params or ()) |
| 264 | |
257 | 265 | class Constraint(object): |
258 | 266 | """ |
259 | 267 | An object that can be passed to WhereNode.add() and knows how to |
diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py
index d4d7cb8..1e94de0 100644
a
|
b
|
True
|
210 | 210 | >>> TestObject.objects.filter(pk__in=TestObject.objects.values('pk').extra(select={'extra': 1})) |
211 | 211 | [<TestObject: TestObject: first,second,third>] |
212 | 212 | |
| 213 | >>> pk = TestObject.objects.get().pk |
| 214 | >>> TestObject.objects.filter(pk=pk) | TestObject.objects.extra(where=["id > %s"], params=[pk]) |
| 215 | [<TestObject: TestObject: first,second,third>] |
| 216 | |
213 | 217 | """} |