Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…

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) |