diff --git a/AUTHORS b/AUTHORS
index bc2e6bac5f..76d9040fd5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,6 +9,7 @@ answer newbie questions, and generally made Django that much better:
Aaron Swartz
Aaron T. Myers
Abeer Upadhyay
+ Abhinav Patil
Abhishek Gautam
Adam BogdaĆ
Adam Donaghy
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index beb57f8ecd..7c46c7a237 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -6,6 +6,7 @@ themselves do not have to (and could be backed by things other than SQL
databases). The abstraction barrier only works one way: this module has to know
all about the internals of models in order to get the information it needs.
"""
+import difflib
import functools
from collections import Counter, OrderedDict, namedtuple
from collections.abc import Iterator, Mapping
@@ -1140,10 +1141,16 @@ class Query:
if transform_class:
return transform_class(lhs)
else:
+ output_field = lhs.output_field.__class__
+ suggested_lookups = difflib.get_close_matches(name, output_field.get_lookups())
+ if suggested_lookups:
+ suggestion = ', perhaps you meant %s?' % ' or '.join(suggested_lookups)
+ else:
+ suggestion = '.'
raise FieldError(
"Unsupported lookup '%s' for %s or join on the field not "
- "permitted." %
- (name, lhs.output_field.__class__.__name__))
+ "permitted%s" % (name, output_field.__name__, suggestion)
+ )
def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
can_reuse=None, allow_joins=True, split_subq=True,
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index 45360963b2..d759bbadff 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -569,13 +569,28 @@ class LookupTests(TestCase):
):
Article.objects.filter(pub_date_year='2005').count()
+ def test_unsupported_lookups(self):
with self.assertRaisesMessage(
FieldError,
"Unsupported lookup 'starts' for CharField or join on the field "
- "not permitted."
+ "not permitted, perhaps you meant startswith or istartswith?"
):
Article.objects.filter(headline__starts='Article')
+ with self.assertRaisesMessage(
+ FieldError,
+ "Unsupported lookup 'is_null' for DateTimeField or join on the field "
+ "not permitted, perhaps you meant isnull?"
+ ):
+ Article.objects.filter(pub_date__is_null=True)
+
+ with self.assertRaisesMessage(
+ FieldError,
+ "Unsupported lookup 'gobbledygook' for DateTimeField or join on the field "
+ "not permitted."
+ ):
+ Article.objects.filter(pub_date__gobbledygook='blahblah')
+
def test_relation_nested_lookup_error(self):
# An invalid nested lookup on a related field raises a useful error.
msg = 'Related Field got invalid lookup: editor'