コード例 #1
0
    def test_resolve_reverse_related_lookups(self):
        """
        Check that lookups can be resolved for related fields
        in the reverse direction.
        """
        lookups = [
            'exact',
            'gte',
            'gt',
            'lte',
            'lt',
            'in',
            'isnull',
        ]

        # ManyToOneRel
        model_field = User._meta.get_field('article')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToOneRel)
            self.assertEqual(lookup, term)

        # ManyToManyRel
        model_field = Book._meta.get_field('lovers')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToManyRel)
            self.assertEqual(lookup, term)
コード例 #2
0
    def test_resolve_forward_related_lookups(self):
        """
        Check that lookups can be resolved for related fields
        in the forwards direction.
        """
        lookups = [
            'exact',
            'gte',
            'gt',
            'lte',
            'lt',
            'in',
            'isnull',
        ]

        # ForeignKey
        model_field = Article._meta.get_field('author')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ForeignKey)
            self.assertEqual(lookup, term)

        # ManyToManyField
        model_field = User._meta.get_field('favorite_books')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToManyField)
            self.assertEqual(lookup, term)
コード例 #3
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_invalid_transformed_lookup_expression(self):
        model_field = Article._meta.get_field('published')

        with self.assertRaises(FieldLookupError) as context:
            resolve_field(model_field, 'date__invalid_lookup')

        exc = str(context.exception)
        self.assertIn(str(model_field), exc)
        self.assertIn('date__invalid_lookup', exc)
コード例 #4
0
ファイル: filters.py プロジェクト: matt852/nautobot
    def _generate_lookup_expression_filters(cls, filter_name, filter_field):
        """
        For specific filter types, new filters are created based on defined lookup expressions in
        the form `<field_name>__<lookup_expr>`
        """
        magic_filters = {}
        if filter_field.method is not None or filter_field.lookup_expr not in ["exact", "in"]:
            return magic_filters

        # Choose the lookup expression map based on the filter type
        lookup_map = cls._get_filter_lookup_dict(filter_field)
        if lookup_map is None:
            # Do not augment this filter type with more lookup expressions
            return magic_filters

        # Get properties of the existing filter for later use
        field_name = filter_field.field_name
        field = get_model_field(cls._meta.model, field_name)

        # If there isn't a model field, return.
        if field is None:
            return magic_filters

        # Create new filters for each lookup expression in the map
        for lookup_name, lookup_expr in lookup_map.items():
            new_filter_name = "{}__{}".format(filter_name, lookup_name)

            try:
                if filter_name in cls.declared_filters:
                    # The filter field has been explicity defined on the filterset class so we must manually
                    # create the new filter with the same type because there is no guarantee the defined type
                    # is the same as the default type for the field
                    resolve_field(field, lookup_expr)  # Will raise FieldLookupError if the lookup is invalid
                    new_filter = type(filter_field)(
                        field_name=field_name,
                        lookup_expr=lookup_expr,
                        label=filter_field.label,
                        exclude=filter_field.exclude,
                        distinct=filter_field.distinct,
                        **filter_field.extra,
                    )
                else:
                    # The filter field is listed in Meta.fields so we can safely rely on default behaviour
                    # Will raise FieldLookupError if the lookup is invalid
                    new_filter = cls.filter_for_field(field, field_name, lookup_expr)
            except django_filters.exceptions.FieldLookupError:
                # The filter could not be created because the lookup expression is not supported on the field
                continue

            if lookup_name.startswith("n"):
                # This is a negation filter which requires a queryset.exclude() clause
                # Of course setting the negation of the existing filter's exclude attribute handles both cases
                new_filter.exclude = not filter_field.exclude

            magic_filters[new_filter_name] = new_filter

        return magic_filters
コード例 #5
0
    def test_invalid_transformed_lookup_expression(self):
        model_field = Article._meta.get_field('published')

        with self.assertRaises(FieldLookupError) as context:
            resolve_field(model_field, 'date__invalid_lookup')

        exc = str(context.exception)
        self.assertIn(str(model_field), exc)
        self.assertIn('date__invalid_lookup', exc)
コード例 #6
0
    def get_additional_lookups(cls, existing_filter_name, existing_filter):
        new_filters = {}

        # Skip nonstandard lookup expressions
        if existing_filter.method is not None or existing_filter.lookup_expr not in ['exact', 'in']:
            return {}

        # Choose the lookup expression map based on the filter type
        lookup_map = cls._get_filter_lookup_dict(existing_filter)
        if lookup_map is None:
            # Do not augment this filter type with more lookup expressions
            return {}

        # Get properties of the existing filter for later use
        field_name = existing_filter.field_name
        field = get_model_field(cls._meta.model, field_name)

        # Create new filters for each lookup expression in the map
        for lookup_name, lookup_expr in lookup_map.items():
            new_filter_name = f'{existing_filter_name}__{lookup_name}'

            try:
                if existing_filter_name in cls.declared_filters:
                    # The filter field has been explicitly defined on the filterset class so we must manually
                    # create the new filter with the same type because there is no guarantee the defined type
                    # is the same as the default type for the field
                    resolve_field(field, lookup_expr)  # Will raise FieldLookupError if the lookup is invalid
                    new_filter = type(existing_filter)(
                        field_name=field_name,
                        lookup_expr=lookup_expr,
                        label=existing_filter.label,
                        exclude=existing_filter.exclude,
                        distinct=existing_filter.distinct,
                        **existing_filter.extra
                    )
                elif hasattr(existing_filter, 'custom_field'):
                    # Filter is for a custom field
                    custom_field = existing_filter.custom_field
                    new_filter = custom_field.to_filter(lookup_expr=lookup_expr)
                else:
                    # The filter field is listed in Meta.fields so we can safely rely on default behaviour
                    # Will raise FieldLookupError if the lookup is invalid
                    new_filter = cls.filter_for_field(field, field_name, lookup_expr)
            except FieldLookupError:
                # The filter could not be created because the lookup expression is not supported on the field
                continue

            if lookup_name.startswith('n'):
                # This is a negation filter which requires a queryset.exclude() clause
                # Of course setting the negation of the existing filter's exclude attribute handles both cases
                new_filter.exclude = not existing_filter.exclude

            new_filters[new_filter_name] = new_filter

        return new_filters
コード例 #7
0
    def test_resolve_implicit_exact_lookup(self):
        # Use a DateTimeField, so we can check multiple transforms.
        # eg, date__year__gte
        model_field = Article._meta.get_field('published')

        field, lookup = resolve_field(model_field, 'date')
        self.assertIsInstance(field, models.DateField)
        self.assertEqual(lookup, 'exact')

        field, lookup = resolve_field(model_field, 'date__year')
        self.assertIsInstance(field, models.IntegerField)
        self.assertEqual(lookup, 'exact')
コード例 #8
0
    def test_resolve_transformed_lookups(self):
        """
        Check that chained field transforms are correctly resolved.
        eg, a 'date__year__gte' lookup on an article's 'published' timestamp.
        """
        # Use a DateTimeField, so we can check multiple transforms.
        # eg, date__year__gte
        model_field = Article._meta.get_field('published')

        standard_lookups = [
            'exact',
            'iexact',
            'gte',
            'gt',
            'lte',
            'lt',
        ]

        date_lookups = [
            'year',
            'month',
            'day',
            'week_day',
        ]

        datetime_lookups = date_lookups + [
            'hour',
            'minute',
            'second',
        ]

        # ex: 'date__gt'
        for lookup in standard_lookups:
            field, resolved_lookup = resolve_field(
                model_field, LOOKUP_SEP.join(['date', lookup]))
            self.assertIsInstance(field, models.DateField)
            self.assertEqual(resolved_lookup, lookup)

        # ex: 'year__iexact'
        for part in datetime_lookups:
            for lookup in standard_lookups:
                field, resolved_lookup = resolve_field(
                    model_field, LOOKUP_SEP.join([part, lookup]))
                self.assertIsInstance(field, models.IntegerField)
                self.assertEqual(resolved_lookup, lookup)

        # ex: 'date__year__lte'
        for part in date_lookups:
            for lookup in standard_lookups:
                field, resolved_lookup = resolve_field(
                    model_field, LOOKUP_SEP.join(['date', part, lookup]))
                self.assertIsInstance(field, models.IntegerField)
                self.assertEqual(resolved_lookup, lookup)
コード例 #9
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_resolve_implicit_exact_lookup(self):
        # Use a DateTimeField, so we can check multiple transforms.
        # eg, date__year__gte
        model_field = Article._meta.get_field('published')

        field, lookup = resolve_field(model_field, 'date')
        self.assertIsInstance(field, models.DateField)
        self.assertEqual(lookup, 'exact')

        field, lookup = resolve_field(model_field, 'date__year')
        self.assertIsInstance(field, models.IntegerField)
        self.assertEqual(lookup, 'exact')
コード例 #10
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_resolve_transformed_lookups(self):
        """
        Check that chained field transforms are correctly resolved.
        eg, a 'date__year__gte' lookup on an article's 'published' timestamp.
        """
        # Use a DateTimeField, so we can check multiple transforms.
        # eg, date__year__gte
        model_field = Article._meta.get_field('published')

        standard_lookups = [
            'exact',
            'iexact',
            'gte',
            'gt',
            'lte',
            'lt',
        ]

        date_lookups = [
            'year',
            'month',
            'day',
            'week_day',
        ]

        datetime_lookups = date_lookups + [
            'hour',
            'minute',
            'second',
        ]

        # ex: 'date__gt'
        for lookup in standard_lookups:
            field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join(['date', lookup]))
            self.assertIsInstance(field, models.DateField)
            self.assertEqual(resolved_lookup, lookup)

        # ex: 'year__iexact'
        for part in datetime_lookups:
            for lookup in standard_lookups:
                field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join([part, lookup]))
                self.assertIsInstance(field, models.IntegerField)
                self.assertEqual(resolved_lookup, lookup)

        # ex: 'date__year__lte'
        for part in date_lookups:
            for lookup in standard_lookups:
                field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join(['date', part, lookup]))
                self.assertIsInstance(field, models.IntegerField)
                self.assertEqual(resolved_lookup, lookup)
コード例 #11
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_resolve_forward_related_lookups(self):
        """
        Check that lookups can be resolved for related fields
        in the forwards direction.
        """
        lookups = ['exact', 'gte', 'gt', 'lte', 'lt', 'in', 'isnull', ]

        # ForeignKey
        model_field = Article._meta.get_field('author')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ForeignKey)
            self.assertEqual(lookup, term)

        # ManyToManyField
        model_field = User._meta.get_field('favorite_books')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToManyField)
            self.assertEqual(lookup, term)
コード例 #12
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_resolve_reverse_related_lookups(self):
        """
        Check that lookups can be resolved for related fields
        in the reverse direction.
        """
        lookups = ['exact', 'gte', 'gt', 'lte', 'lt', 'in', 'isnull', ]

        # ManyToOneRel
        model_field = User._meta.get_field('article')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToOneRel)
            self.assertEqual(lookup, term)

        # ManyToManyRel
        model_field = Book._meta.get_field('lovers')
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.ManyToManyRel)
            self.assertEqual(lookup, term)
コード例 #13
0
ファイル: test_utils.py プロジェクト: Elec/django-filter
    def test_resolve_plain_lookups(self):
        """
        Check that the standard query terms can be correctly resolved.
        eg, an 'EXACT' lookup on a user's username
        """
        model_field = User._meta.get_field('username')
        lookups = model_field.class_lookups.keys()

        # This is simple - the final ouput of an untransformed field is itself.
        # The lookups are the default lookups registered to the class.
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.CharField)
            self.assertEqual(lookup, term)
コード例 #14
0
    def test_resolve_plain_lookups(self):
        """
        Check that the standard query terms can be correctly resolved.
        eg, an 'EXACT' lookup on a user's username
        """
        model_field = User._meta.get_field('username')
        lookups = model_field.class_lookups.keys()

        # This is simple - the final ouput of an untransformed field is itself.
        # The lookups are the default lookups registered to the class.
        for term in lookups:
            field, lookup = resolve_field(model_field, term)
            self.assertIsInstance(field, models.CharField)
            self.assertEqual(lookup, term)
コード例 #15
0
    def filter_for_field(cls, f, name, lookup_expr='exact'):
        # Redefine método estático para ignorar filtro para
        # fields que não possuam lookup_expr informado
        f, lookup_type = resolve_field(f, lookup_expr)

        default = {
            'field_name': name,
            'label': capfirst(f.verbose_name),
            'lookup_expr': lookup_expr
        }

        filter_class, params = cls.filter_for_lookup(f, lookup_type)
        default.update(params)
        if filter_class is not None:
            return filter_class(**default)
        return None
コード例 #16
0
    def test_invalid_transformed_lookup_expression(self):
        model_field = Article._meta.get_field('published')

        with self.assertRaises(FieldError):
            field, lookup = resolve_field(model_field, 'date__invalid_lookup')
コード例 #17
0
ファイル: test_utils.py プロジェクト: DjangoBD/django-filter
    def test_invalid_transformed_lookup_expression(self):
        model_field = Article._meta.get_field('published')

        with self.assertRaises(FieldError):
            field, lookup = resolve_field(model_field, 'date__invalid_lookup')
コード例 #18
0
ファイル: filters.py プロジェクト: whitej6/nautobot
    def get_filters(cls):
        """
        Override filter generation to support dynamic lookup expressions for certain filter types.

        For specific filter types, new filters are created based on defined lookup expressions in
        the form `<field_name>__<lookup_expr>`
        """
        filters = super().get_filters()

        new_filters = {}
        for existing_filter_name, existing_filter in filters.items():
            # Loop over existing filters to extract metadata by which to create new filters

            # If the filter makes use of a custom filter method or lookup expression skip it
            # as we cannot sanely handle these cases in a generic mannor
            if existing_filter.method is not None or existing_filter.lookup_expr not in ["exact", "in"]:
                continue

            # Choose the lookup expression map based on the filter type
            lookup_map = cls._get_filter_lookup_dict(existing_filter)
            if lookup_map is None:
                # Do not augment this filter type with more lookup expressions
                continue

            # Get properties of the existing filter for later use
            field_name = existing_filter.field_name
            field = get_model_field(cls._meta.model, field_name)

            # Create new filters for each lookup expression in the map
            for lookup_name, lookup_expr in lookup_map.items():
                new_filter_name = "{}__{}".format(existing_filter_name, lookup_name)

                try:
                    if existing_filter_name in cls.declared_filters:
                        # The filter field has been explicity defined on the filterset class so we must manually
                        # create the new filter with the same type because there is no guarantee the defined type
                        # is the same as the default type for the field
                        resolve_field(field, lookup_expr)  # Will raise FieldLookupError if the lookup is invalid
                        new_filter = type(existing_filter)(
                            field_name=field_name,
                            lookup_expr=lookup_expr,
                            label=existing_filter.label,
                            exclude=existing_filter.exclude,
                            distinct=existing_filter.distinct,
                            **existing_filter.extra,
                        )
                    else:
                        # The filter field is listed in Meta.fields so we can safely rely on default behaviour
                        # Will raise FieldLookupError if the lookup is invalid
                        new_filter = cls.filter_for_field(field, field_name, lookup_expr)
                except django_filters.exceptions.FieldLookupError:
                    # The filter could not be created because the lookup expression is not supported on the field
                    continue

                if lookup_name.startswith("n"):
                    # This is a negation filter which requires a queryset.exclude() clause
                    # Of course setting the negation of the existing filter's exclude attribute handles both cases
                    new_filter.exclude = not existing_filter.exclude

                new_filters[new_filter_name] = new_filter

        filters.update(new_filters)
        return filters