Browse Source

Fixed #29804 -- Added 'did you mean' suggestions for unsupported lookup error.

Abhinav Patil 6 years ago
parent
commit
bf01994a5c
3 changed files with 26 additions and 3 deletions
  1. 1 0
      AUTHORS
  2. 9 2
      django/db/models/sql/query.py
  3. 16 1
      tests/lookup/tests.py

+ 1 - 0
AUTHORS

@@ -9,6 +9,7 @@ answer newbie questions, and generally made Django that much better:
     Aaron Swartz <http://www.aaronsw.com/>
     Aaron T. Myers <atmyers@gmail.com>
     Abeer Upadhyay <ab.esquarer@gmail.com>
+    Abhinav Patil <https://github.com/ubadub/>
     Abhishek Gautam <abhishekg1128@yahoo.com>
     Adam Bogdał <adam@bogdal.pl>
     Adam Donaghy

+ 9 - 2
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,

+ 16 - 1
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'