Default severity: warning · Applicability: suggestion · Category: correctness
Reports a segment of a lookup path inside filter(), exclude(), get() and their siblings that is both unrecognised and close in spelling to a name that is recognised. Post.objects.filter(titel="x") is flagged with title as the suggestion; a QuickFix replaces just that segment, leaving the rest of the path and the value alone.
Django raises FieldError for a bad lookup name, but only when the queryset is evaluated — which for a filter buried in a branch may be in production rather than in a test.
“I do not recognise this name” is a low-precision claim. Annotations, custom lookups registered with register_lookup, fields from a mixin the parser could not read, GenericForeignKey — all legal, all absent from the parsed schema. A rule built on that claim underlines correct code, and a warning that is wrong even occasionally teaches people to turn the extension off.
“This looks like a misspelling of that” is a high-precision claim, and it arrives with something actionable. So a name close to nothing at all — zzzqqq, or an annotation alias — is left alone, and only a near miss is reported.
The distance allowed scales with length: one edit for names of four characters or fewer, two for longer ones. The first character must match, because that is the letter people get right even when they fumble the rest — without that constraint short names pair off with each other across an entire model.
The check asks the completion resolver for the names valid at that position and reports only what is missing from that list. It is not a second, weaker copy of the resolution logic; it is the same one. Everything completion understands, this rule stays quiet about, automatically:
related_name, or the lowercased model namepkicontains on a CharField, year on a DateTimeField)That property is why the rule waited. Reverse accessors landed in v0.15.0, inherited fields in v0.16.0, cross-file bases in v0.17.0. Shipped before those, it would have underlined Author.objects.filter(posts__title=...) — ordinary Django.
.annotate(), .alias() or .extra(). Those introduce names that are valid downstream and exist in no model.Q(...), F(...), a subquery — where a bare name need not be a field.Post.objects.filter(titel="django") # DOL008: did you mean title?
Post.objects.filter(author__emial="a@b.c") # DOL008: did you mean email?
Post.objects.filter(title__icontans="x") # DOL008: did you mean icontains?
Post.objects.filter(title="django")
Post.objects.filter(author__email="a@b.c")
Post.objects.filter(title__icontains="x")
Post.objects.filter(titel="x") # noqa: DOL008
Set djangoOrmLens.rules.DOL008 to "off" to disable the rule, or djangoOrmLens.codeFixes.enabled to false to silence the whole rule set.