Example #1
0
    def visit_class(self, node):
        if is_model(node):
            self.model_names.append(node.name)
            self.prev_idx = None
            self.prev_node = None

        elif is_model(node.parent.frame()):
            # Nested class
            self._visit_django_attribute(node, is_method=False)
Example #2
0
    def leave_class(self, node):
        if not is_model(node):
            return

        if is_model(node, check_base_classes=False) and self.field_count == 0:
            self.add_message('W6003', node=node)
        elif self.field_count > self.config.max_model_fields:
            self.add_message('W6002', node=node,
                args=(self.field_count, self.config.max_model_fields))

        self.field_count = 0
Example #3
0
    def leave_class(self, node):
        if node.name == 'Meta' and is_model(node.parent.parent):
            # Annotate the model with information from the Meta class
            try:
                val = safe_infer(node.locals['abstract'][-1]).value
                if val is True:
                    node.parent.parent._django_abstract = True
            except KeyError:
                pass
            return

        if not is_model(node):
            return
    def visit_function(self, node):
        if not is_model(node.parent.frame()):
            return

        if node.name == '__str__':
            self.add_message('W8011', node=node)

        try:
            idx = [
                '__unicode__',
                '__str__',
                'save',
                'delete',
                'get_absolute_url',
            ].index(node.name)

            if self.prev_idx == -1:
                self.add_message('W8012', node=self.prev_node)

            elif idx < self.prev_idx:
                self.add_message('W8013', node=node, args=self.prev_node.name)

        except ValueError:
            idx = -1

        self.prev_idx = idx
        self.prev_node = node
    def visit_class(self, node):
        if not is_model(node):
            return

        self.model_names.append(node.name)
        self.prev_idx = None
        self.prev_name = None
Example #6
0
    def visit_class(self, node):
        if not is_model(node):
            return

        self.model_names.append(node.name)
        self.prev_idx = None
        self.prev_name = None
Example #7
0
    def visit_function(self, node):
        if not is_model(node.parent.frame()):
            return

        if node.name == '__str__':
            self.add_message('W8011', node=node)

        try:
            idx = [
                '__unicode__',
                '__str__',
                'save',
                'delete',
                'get_absolute_url',
            ].index(node.name)

            if self.prev_idx == -1:
                self.add_message('W8012', node=self.prev_node)

            elif idx < self.prev_idx:
                self.add_message('W8013', node=node, args=self.prev_node.name)

        except ValueError:
            idx = -1

        self.prev_idx = idx
        self.prev_node = node
Example #8
0
    def visit_assname(self, node):
        if not is_model(node.parent.frame()):
            return

        if self.prev_idx >= 0:
            self.add_message('W8013', node=node, args=(
                '%r assignment' % node.name, self.prev_node.name,
            ))
Example #9
0
    def visit_function(self, node):
        if not is_model(node.parent.frame()):
            return

        if node.name == '__str__':
            self.add_message('W8011', node=node)

        self._visit_django_attribute(node)
Example #10
0
    def visit_callfunc(self, node):
        if not is_model(node.frame()):
            # We only care about fields attached to models
            return

        val = safe_infer(node)
        if not val or not val.root().name.startswith(
                'django.db.models.fields'):
            # Not a field
            return

        assname = '(unknown name)'
        x = node.parent.get_children().next()
        if isinstance(x, astng.AssName):
            assname = x.name

        self.field_count += 1

        # Parse kwargs
        options = dict([(option, None) for option in (
            'null',
            'blank',
            'unique',
            'default',
            'auto_now',
            'primary_key',
            'auto_now_add',
            'verify_exists',
            'related_name',
            'max_length',
            'unique_for_date',
            'unique_for_month',
            'unique_for_year',
        )])

        for arg in node.args:
            if not isinstance(arg, astng.Keyword):
                continue

            for option in options.keys():
                if arg.arg == option:
                    try:
                        options[option] = safe_infer(arg.value).value
                    except AttributeError:
                        # Don't lint this field if we cannot infer everything
                        return

        if not val.name.lower().startswith('null'):
            for option in ('null', 'blank'):
                if options[option] is False:
                    self.add_message('W6015',
                                     node=node,
                                     args=(
                                         assname,
                                         option,
                                     ))

        # Field type specific checks
        if val.name in ('CharField', 'TextField'):
            if options['null']:
                self.add_message('W6000', node=node, args=(assname, ))

            if val.name == 'CharField' and \
                    options['max_length'] > self.config.max_charfield_length:
                self.add_message('W6007',
                                 node=node,
                                 args=(
                                     assname,
                                     options['max_length'],
                                     self.config.max_charfield_length,
                                 ))

        elif val.name == 'BooleanField':
            if options['default']:
                self.add_message('W6012', node=node, args=(assname, ))

        elif val.name == 'ForeignKey':
            val = safe_infer(node.args[0])
            if isinstance(val, astng.Const) and val.value == 'self':
                self.add_message('W6001', node=node, args=(assname, ))

            elif not options['related_name']:
                self.add_message('W6006', node=node, args=(assname, ))

            if options['primary_key'] and options['unique'] is False:
                self.add_message('W6014', node=node, args=(assname, ))
            elif options['primary_key'] or options['unique']:
                self.add_message('W6013', node=node, args=(assname, ))

        elif val.name == 'URLField':
            if options['verify_exists'] is None:
                self.add_message('W6011', node=node, args=(assname, ))

        elif val.name in ('PositiveSmallIntegerField', 'SmallIntegerField'):
            self.add_message('W6010', node=node, args=(assname, val.name))

        elif val.name == 'NullBooleanField':
            self.add_message('W6009', node=node, args=(assname, ))

        elif val.name == 'ManyToManyField':
            if options['null']:
                self.add_message('W6016', node=node, args=(assname, ))

        # Generic checks
        if options['null'] and not options['blank']:
            self.add_message('W6004', node=node, args=(assname, ))

        if options['auto_now'] or options['auto_now_add']:
            self.add_message('W6008', node=node, args=(assname, ))

        for suffix in ('date', 'month', 'year'):
            if options['unique_for_%s' % suffix]:
                self.add_message('W6005', node=node, args=(assname, suffix))
Example #11
0
    def visit_callfunc(self, node):
        if not is_model(node.frame()):
            # We only care about fields attached to models
            return

        val = safe_infer(node)
        if not val or not val.root().name.startswith('django.db.models.fields'):
            # Not a field
            return

        assname = '(unknown name)'
        x = node.parent.get_children().next()
        if isinstance(x, astng.AssName):
            assname = x.name

        self.field_count += 1

        # Parse kwargs
        options = dict([(option, None) for option in (
            'null',
            'blank',
            'unique',
            'default',
            'auto_now',
            'primary_key',
            'auto_now_add',
            'verify_exists',
            'related_name',
            'max_length',
            'unique_for_date',
            'unique_for_month',
            'unique_for_year',
        )])

        for arg in node.args:
            if not isinstance(arg, astng.Keyword):
                continue

            for option in options.keys():
                if arg.arg == option:
                    try:
                        options[option] = safe_infer(arg.value).value
                    except AttributeError:
                        # Don't lint this field if we cannot infer everything
                        return

        if not val.name.lower().startswith('null'):
            for option in ('null', 'blank'):
                if options[option] is False:
                    self.add_message('W6015', node=node, args=(assname, option,))

        # Field type specific checks
        if val.name in ('CharField', 'TextField'):
            if options['null']:
                self.add_message('W6000', node=node, args=(assname,))

            if val.name == 'CharField' and \
                    options['max_length'] > self.config.max_charfield_length:
                self.add_message('W6007', node=node, args=(
                    assname,
                    options['max_length'],
                    self.config.max_charfield_length,
                ))

        elif val.name == 'BooleanField':
            if options['default']:
                self.add_message('W6012', node=node, args=(assname,))

        elif val.name == 'ForeignKey':
            val = safe_infer(node.args[0])
            if isinstance(val, astng.Const) and val.value == 'self':
                self.add_message('W6001', node=node, args=(assname,))

            elif not options['related_name']:
                self.add_message('W6006', node=node, args=(assname,))

            if options['primary_key'] and options['unique'] is False:
                self.add_message('W6014', node=node, args=(assname,))
            elif options['primary_key'] or options['unique']:
                self.add_message('W6013', node=node, args=(assname,))

        elif val.name == 'URLField':
            if options['verify_exists'] is None:
                self.add_message('W6011', node=node, args=(assname,))

        elif val.name in ('PositiveSmallIntegerField', 'SmallIntegerField'):
            self.add_message('W6010', node=node, args=(assname, val.name))

        elif val.name == 'NullBooleanField':
            self.add_message('W6009', node=node, args=(assname,))

        elif val.name == 'ManyToManyField':
            if options['null']:
                self.add_message('W6016', node=node, args=(assname,))

        # Generic checks
        if options['null'] and not options['blank']:
            self.add_message('W6004', node=node, args=(assname,))

        if options['auto_now'] or options['auto_now_add']:
            self.add_message('W6008', node=node, args=(assname,))

        for suffix in ('date', 'month', 'year'):
            if options['unique_for_%s' % suffix]:
                self.add_message('W6005', node=node, args=(assname, suffix))