Skip to content

Commit 7a77887

Browse files
committed
Be more strict when infering ForeignKey models specified as string
The failing `func_noerror_foreignkeys` was caused by pylint_django trying to infer a ForeignKey('Author') field by looking at the astroid cache. In this case there was an Author model already defined in `func_noerror_duplicate_except_doesnotexist` which was inferred and of course this model didn't have the `author_name` field hence we got a no-member error. This commit tries to restrict where we load these models from and also takes into account the quirk that Django allows specifying 'appname.Model' instead of 'path.to.python.module.models.Model'.
1 parent e1d4332 commit 7a77887

File tree

1 file changed

+21
-2
lines changed

1 file changed

+21
-2
lines changed

pylint_django/transforms/foreignkey.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,31 @@ def infer_key_classes(node, context=None):
3737
break
3838
elif isinstance(arg, nodes.Const):
3939
try:
40-
model_name = arg.value.split('.')[-1] # can be 'Model' or 'app.Model'
40+
# can be 'Model' or 'app.Model'
41+
module_name, _, model_name = arg.value.rpartition('.')
4142
except AttributeError:
4243
break
4344

45+
# when ForeignKey is specified only by class name we assume that
46+
# this class must be found in the current module
47+
if not module_name:
48+
current_module = node.frame()
49+
while not isinstance(current_module, nodes.Module):
50+
current_module = current_module.parent.frame()
51+
52+
module_name = current_module.name
53+
elif not module_name.endswith('models'):
54+
# otherwise Django allows specifying an app name first, e.g.
55+
# ForeignKey('auth.User') so we try to convert that to
56+
# 'auth.models', 'User' which works nicely with the `endswith()`
57+
# comparison below
58+
module_name += '.models'
59+
4460
for module in MANAGER.astroid_cache.values():
45-
if model_name in module.locals:
61+
# only load model classes from modules which match the module in
62+
# which *we think* they are defined. This will prevent infering
63+
# other models of the same name which are found elsewhere!
64+
if model_name in module.locals and module.name.endswith(module_name):
4665
class_defs = [
4766
module_node for module_node in module.lookup(model_name)[1]
4867
if isinstance(module_node, nodes.ClassDef)

0 commit comments

Comments
 (0)