Skip to content

Commit

Permalink
Fixed #30668 -- Made QuerySet.filter() raise NotSupportedError if any…
Browse files Browse the repository at this point in the history
… of source expressions is not filterable.
  • Loading branch information
gizmondo authored and felixxm committed Aug 2, 2019
1 parent 194d1df commit 4edad1d
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 9 deletions.
2 changes: 0 additions & 2 deletions django/db/models/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,6 @@ def as_sql(self, compiler, connection):
@deconstructible
class F(Combinable):
"""An object capable of resolving references to existing query objects."""
# Can the expression be used in a WHERE clause?
filterable = True

def __init__(self, name):
"""
Expand Down
19 changes: 14 additions & 5 deletions django/db/models/sql/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,17 @@ def check_related_objects(self, field, value, opts):
for v in value:
self.check_query_object_type(v, opts, field)

def check_filterable(self, expression):
"""Raise an error if expression cannot be used in a WHERE clause."""
if not getattr(expression, 'filterable', 'True'):
raise NotSupportedError(
expression.__class__.__name__ + ' is disallowed in the filter '
'clause.'
)
if hasattr(expression, 'get_source_expressions'):
for expr in expression.get_source_expressions():
self.check_filterable(expr)

def build_lookup(self, lookups, lhs, rhs):
"""
Try to extract transforms and lookup from given lhs.
Expand Down Expand Up @@ -1217,11 +1228,7 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
raise FieldError("Cannot parse keyword query %r" % arg)
lookups, parts, reffed_expression = self.solve_lookup_type(arg)

if not getattr(reffed_expression, 'filterable', True):
raise NotSupportedError(
reffed_expression.__class__.__name__ + ' is disallowed in '
'the filter clause.'
)
self.check_filterable(reffed_expression)

if not allow_joins and len(parts) > 1:
raise FieldError("Joined field references are not permitted in this query")
Expand All @@ -1230,6 +1237,8 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
value = self.resolve_lookup_value(value, can_reuse, allow_joins, simple_col)
used_joins = {k for k, v in self.alias_refcount.items() if v > pre_joins.get(k, 0)}

self.check_filterable(value)

clause = self.where_class()
if reffed_expression:
condition = self.build_lookup(lookups, reffed_expression, value)
Expand Down
14 changes: 12 additions & 2 deletions tests/expressions_window/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from django.core.exceptions import FieldError
from django.db import NotSupportedError, connection
from django.db.models import (
F, OuterRef, RowRange, Subquery, Value, ValueRange, Window, WindowFrame,
F, Func, OuterRef, Q, RowRange, Subquery, Value, ValueRange, Window,
WindowFrame,
)
from django.db.models.aggregates import Avg, Max, Min, Sum
from django.db.models.functions import (
Expand Down Expand Up @@ -833,8 +834,17 @@ def test_frame_window_frame_notimplemented(self):

def test_invalid_filter(self):
msg = 'Window is disallowed in the filter clause'
qs = Employee.objects.annotate(dense_rank=Window(expression=DenseRank()))
with self.assertRaisesMessage(NotSupportedError, msg):
Employee.objects.annotate(dense_rank=Window(expression=DenseRank())).filter(dense_rank__gte=1)
qs.filter(dense_rank__gte=1)
with self.assertRaisesMessage(NotSupportedError, msg):
qs.annotate(inc_rank=F('dense_rank') + Value(1)).filter(inc_rank__gte=1)
with self.assertRaisesMessage(NotSupportedError, msg):
qs.filter(id=F('dense_rank'))
with self.assertRaisesMessage(NotSupportedError, msg):
qs.filter(id=Func('dense_rank', 2, function='div'))
with self.assertRaisesMessage(NotSupportedError, msg):
qs.annotate(total=Sum('dense_rank', filter=Q(name='Jones'))).filter(total=1)

def test_invalid_order_by(self):
msg = 'order_by must be either an Expression or a sequence of expressions'
Expand Down

0 comments on commit 4edad1d

Please sign in to comment.