diff --git a/django/db/models/query.py b/django/db/models/query.py index aa2138643e..8fbb942da2 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -10,8 +10,20 @@ import re if not hasattr(__builtins__, 'set'): from sets import Set as set +# The string constant used to separate query parts LOOKUP_SEPARATOR = '__' +# The list of valid query types +QUERY_TERMS=( + 'exact', 'iexact', + 'contains', 'icontains', + 'gt', 'gte', 'lt', 'lte', + 'in', + 'startswith', 'istartswith', 'endswith', 'iendswith', + 'range', 'year', 'month', 'day', + 'isnull' +) + # Size of each "chunk" for get_iterator calls. # Larger values are slightly faster at the expense of more storage space. GET_ITERATOR_CHUNK_SIZE = 100 @@ -710,12 +722,13 @@ def parse_lookup(kwarg_items, opts): # if we find "pk", make the clause "exact', and insert # a dummy name of None, which we will replace when # we know which table column to grab as the primary key. - # 2) If there is only one part, assume it to be an __exact + # 2) If there is only one part, or the last part is not a query + # term, assume that the query is an __exact clause = path.pop() if clause == 'pk': clause = 'exact' path.append(None) - elif len(path) == 0: + elif len(path) == 0 or clause not in QUERY_TERMS: path.append(clause) clause = 'exact' diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py index b7d27e2ed3..d202975128 100644 --- a/tests/modeltests/many_to_one/models.py +++ b/tests/modeltests/many_to_one/models.py @@ -136,6 +136,10 @@ False >>> Article.objects.filter(reporter__first_name__exact='John') [, ] +# Check that implied __exact also works +>>> Article.objects.filter(reporter__first_name='John') +[, ] + # Query twice over the related field. >>> Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith') [, ] @@ -237,6 +241,10 @@ TypeError: Cannot resolve keyword 'reporter_id' into field >>> Reporter.objects.filter(article__reporter__exact=r).distinct() [] +# Check that implied __exact also works +>>> Reporter.objects.filter(article__reporter=r).distinct() +[] + # If you delete a reporter, his articles will be deleted. >>> Article.objects.all() [, , , , ]