From bf01994a5ccc07cbe4b011b53b644eba04da052e Mon Sep 17 00:00:00 2001 From: Abhinav Patil Date: Sat, 29 Sep 2018 03:22:27 -0400 Subject: [PATCH] Fixed #29804 -- Added 'did you mean' suggestions for unsupported lookup error. --- AUTHORS | 1 + django/db/models/sql/query.py | 11 +++++++++-- tests/lookup/tests.py | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index bc2e6bac5fe..76d9040fd53 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 beb57f8ecdc..7c46c7a237e 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 45360963b22..d759bbadff6 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'