Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
80 lines (68 sloc) 3.17 KB
from astroid import MANAGER, nodes, InferenceError, inference_tip, UseInferenceDefault
from astroid.nodes import ClassDef, Attribute
from pylint_django.utils import node_is_subclass
def is_foreignkey_in_class(node):
# is this of the form field = models.ForeignKey
if not isinstance(node.parent, nodes.Assign):
return False
if not isinstance(node.parent.parent, ClassDef):
return False
if isinstance(node.func, Attribute):
attr = node.func.attrname
elif isinstance(node.func, nodes.Name):
attr = node.func.name
else:
return False
return attr in ('OneToOneField', 'ForeignKey')
def infer_key_classes(node, context=None):
for arg in node.args:
# typically the class of the foreign key will
# be the first argument, so we'll go from left to right
if isinstance(arg, (nodes.Name, nodes.Attribute)):
try:
key_cls = None
for inferred in arg.infer(context=context):
key_cls = inferred
break
except InferenceError:
continue
else:
if key_cls is not None:
break
elif isinstance(arg, nodes.Const):
try:
# can be 'Model' or 'app.Model'
module_name, _, model_name = arg.value.rpartition('.')
except AttributeError:
break
# when ForeignKey is specified only by class name we assume that
# this class must be found in the current module
if not module_name:
current_module = node.frame()
while not isinstance(current_module, nodes.Module):
current_module = current_module.parent.frame()
module_name = current_module.name
elif not module_name.endswith('models'):
# otherwise Django allows specifying an app name first, e.g.
# ForeignKey('auth.User') so we try to convert that to
# 'auth.models', 'User' which works nicely with the `endswith()`
# comparison below
module_name += '.models'
for module in MANAGER.astroid_cache.values():
# only load model classes from modules which match the module in
# which *we think* they are defined. This will prevent infering
# other models of the same name which are found elsewhere!
if model_name in module.locals and module.name.endswith(module_name):
class_defs = [
module_node for module_node in module.lookup(model_name)[1]
if isinstance(module_node, nodes.ClassDef)
and node_is_subclass(module_node, 'django.db.models.base.Model')
]
if class_defs:
return iter([class_defs[0].instantiate_class()])
else:
raise UseInferenceDefault
return iter([key_cls.instantiate_class()])
def add_transform(manager):
manager.register_transform(nodes.Call, inference_tip(infer_key_classes),
is_foreignkey_in_class)