def _visit_classdef(self, node): """Class visitor.""" if not node_is_subclass(node, 'django.db.models.base.Model', '.Model'): # we only care about models return for child in node.get_children(): if _is_meta_with_abstract(child): return if isinstance(child, Assign): grandchildren = list(child.get_children()) if not isinstance(grandchildren[0], AssignName): continue name = grandchildren[0].name if name != '__unicode__': continue grandchild = grandchildren[1] assigned = inferred(grandchild)()[0] if assigned.callable(): return self.add_message('E%s01' % BASE_ID, args=node.name, node=node) return if isinstance(child, FunctionDef) and child.name == '__unicode__': if PY3: self.add_message('W%s02' % BASE_ID, args=node.name, node=node) return # if we get here, then we have no __unicode__ method directly on the class itself if PY3: return # a different warning is emitted if a parent declares __unicode__ for method in node.methods(): if method.name == '__unicode__': # this happens if a parent declares the unicode method but # this node does not self.add_message('W%s03' % BASE_ID, args=node.name, node=node) return # if the Django compatibility decorator is used then we don't emit a warning # see https://github.com/landscapeio/pylint-django/issues/10 if node.decorators is not None: for decorator in node.decorators.nodes: if getattr(decorator, 'name', None) == 'python_2_unicode_compatible': return self.add_message('W%s01' % BASE_ID, args=node.name, node=node)
def foreign_key_sets(chain, node): """ When a Django model has a ForeignKey to another model, the target of the foreign key gets a '<modelname>_set' attribute for accessing a queryset of the model owning the foreign key - eg: class ModelA(models.Model): pass class ModelB(models.Model): a = models.ForeignKey(ModelA) Now, ModelA instances will have a modelb_set attribute. It's also possible to explicitly name the relationship using the related_name argument to the ForeignKey constructor. As it's impossible to know this without inspecting all models before processing, we'll instead do a "best guess" approach and see if the attribute being accessed goes on to be used as a queryset. This is via 'duck typing': if the method called on the attribute being accessed is something we might find in a queryset, we'll warn. """ quack = False if node.attrname in MANAGER_ATTRS or node.attrname.endswith('_set'): # if this is a X_set method, that's a pretty strong signal that this is the default # Django name, rather than one set by related_name quack = True else: # we will if isinstance(node.parent, Attribute): func_name = getattr(node.parent, 'attrname', None) if func_name in MANAGER_ATTRS: quack = True if quack: children = list(node.get_children()) for child in children: try: inferred_cls = inferred(child)() except InferenceError: pass else: for cls in inferred_cls: if (node_is_subclass(cls, 'django.db.models.manager.Manager', 'django.db.models.base.Model', '.Model', 'django.db.models.fields.related.ForeignObject')): # This means that we are looking at a subclass of models.Model # and something is trying to access a <something>_set attribute. # Since this could exist, we will return so as not to raise an # error. return chain()
def foreign_key_sets(chain, node): """ When a Django model has a ForeignKey to another model, the target of the foreign key gets a '<modelname>_set' attribute for accessing a queryset of the model owning the foreign key - eg: class ModelA(models.Model): pass class ModelB(models.Model): a = models.ForeignKey(ModelA) Now, ModelA instances will have a modelb_set attribute. It's also possible to explicitly name the relationship using the related_name argument to the ForeignKey constructor. As it's impossible to know this without inspecting all models before processing, we'll instead do a "best guess" approach and see if the attribute being accessed goes on to be used as a queryset. This is via 'duck typing': if the method called on the attribute being accessed is something we might find in a queryset, we'll warn. """ quack = False if node.attrname in MANAGER_ATTRS or node.attrname.endswith('_set'): # if this is a X_set method, that's a pretty strong signal that this is the default # Django name, rather than one set by related_name quack = True else: # we will if isinstance(node.parent, Attribute): func_name = getattr(node.parent, 'attrname', None) if func_name in MANAGER_ATTRS: quack = True if quack: children = list(node.get_children()) for child in children: try: inferred_cls = inferred(child)() except InferenceError: pass else: for cls in inferred_cls: if (node_is_subclass(cls, 'django.db.models.manager.Manager', 'django.db.models.base.Model', '.Model')): # This means that we are looking at a subclass of models.Model # and something is trying to access a <something>_set attribute. # Since this could exist, we will return so as not to raise an # error. return chain()
def _attribute_is_magic(node, attrs, parents): """Checks that node is an attribute used inside one of allowed parents""" if node.attrname not in attrs: return False if not node.last_child(): return False try: for cls in inferred(node.last_child())(): if isinstance(cls, Super): cls = cls._self_class if node_is_subclass(cls, *parents) or cls.qname() in parents: return True except InferenceError: pass return False
def is_model_field_display_method(node): """Accept model's fields with get_*_display names.""" if not node.attrname.endswith('_display'): return if not node.attrname.startswith('get_'): return if node.last_child(): # TODO: could validate the names of the fields on the model rather than # blindly accepting get_*_display try: for cls in inferred(node.last_child())(): if node_is_subclass(cls, 'django.db.models.base.Model', '.Model'): return True except InferenceError: return False return False
def node_is_subclass(cls, *subclass_names): """Checks if cls node has parent with subclass_name.""" if not isinstance(cls, (ClassDef, Instance)): return False if cls.bases == Uninferable: return False for base_cls in cls.bases: try: for inf in inferred(base_cls)(): if inf.qname() in subclass_names: return True if inf != cls and node_is_subclass(inf, *subclass_names): # check up the hierarchy in case we are a subclass of # a subclass of a subclass ... return True except InferenceError: continue return False
def foreign_key_sets(chain, node): """ When a Django model has a ForeignKey to another model, the target of the foreign key gets a '<modelname>_set' attribute for accessing a queryset of the model owning the foreign key - eg: class ModelA(models.Model): pass class ModelB(models.Model): a = models.ForeignKey(ModelA) Now, ModelA instances will have a modelb_set attribute. It's also possible to explicitly name the relationship using the related_name argument to the ForeignKey constructor. As it's impossible to know this without inspecting all models before processing, we'll instead do a "best guess" approach and see if the attribute being accessed goes on to be used as a queryset. This is via 'duck typing': if the method called on the attribute being accessed is something we might find in a queryset, we'll warn. """ quack = False # Note: it would have been nice to import the Manager object from Django and # get its attributes that way - and this used to be the method - but unfortunately # there's no guarantee that Django is properly configured at that stage, and importing # anything from the django.db package causes an ImproperlyConfigured exception. # Therefore we'll fall back on a hard-coded list of attributes which won't be as accurate, # but this is not 100% accurate anyway. manager_attrs = ( 'none', 'all', 'count', 'dates', 'distinct', 'extra', 'get', 'get_or_create', 'create', 'bulk_create', 'filter', 'aggregate', 'annotate', 'complex_filter', 'exclude', 'in_bulk', 'iterator', 'latest', 'order_by', 'select_for_update', 'select_related', 'prefetch_related', 'values', 'values_list', 'update', 'reverse', 'defer', 'only', 'using', 'exists', ) if node.attrname in manager_attrs or node.attrname.endswith('_set'): # if this is a X_set method, that's a pretty strong signal that this is the default # Django name, rather than one set by related_name quack = True else: # we will if isinstance(node.parent, Attribute): func_name = getattr(node.parent, 'attrname', None) if func_name in manager_attrs: quack = True if quack: children = list(node.get_children()) for child in children: try: inferred_cls = inferred(child)() except InferenceError: pass else: for cls in inferred_cls: if (node_is_subclass(cls, 'django.db.models.manager.Manager', 'django.db.models.base.Model', '.Model')): # This means that we are looking at a subclass of models.Model # and something is trying to access a <something>_set attribute. # Since this could exist, we will return so as not to raise an # error. return chain()