Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 31 additions & 17 deletions src/cl_sii/extras/dj_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
except ImportError as exc: # pragma: no cover
raise ImportError("Package 'django-filter' is required to use this module.") from exc

from collections.abc import Sequence
from copy import deepcopy
from typing import ClassVar, Mapping, Tuple, Type
from typing import Any, ClassVar, Mapping, Type

import django.db.models
import django.forms
Expand All @@ -37,9 +38,37 @@ class RutFilter(django_filters.filters.CharFilter):
- https://github.com/carltongibson/django-filter/blob/24.2/docs/ref/filters.txt
"""

field_class: ClassVar[Type[django.forms.Field]]
field_class: Type[django.forms.Field]
field_class = cl_sii.extras.dj_form_fields.RutField

field_class_for_substrings: Type[django.forms.Field]
field_class_for_substrings = django_filters.filters.CharFilter.field_class

lookup_expressions_for_substrings: Sequence[str] = [
'contains',
'icontains',
'startswith',
'istartswith',
'endswith',
'iendswith',
]

def __init__(
self,
field_name: Any = None,
lookup_expr: Any = None,
*args: Any,
**kwargs: Any,
) -> None:
if lookup_expr in self.lookup_expressions_for_substrings:
# Lookups that can be used to search for substrings will not always
# work with the default field class because some substrings cannot
# be converted to instances of class `Rut`. For example,
# `…__contains="803"` fails because `Rut("803")` raises a `ValueError`.
self.field_class = self.field_class_for_substrings

super().__init__(field_name, lookup_expr, *args, **kwargs)


FILTER_FOR_DBFIELD_DEFAULTS = {
**FILTER_FOR_DBFIELD_DEFAULTS,
Expand All @@ -62,18 +91,3 @@ class SiiFilterSet(django_filters.filterset.FilterSet):

FILTER_DEFAULTS: ClassVar[Mapping[Type[django.db.models.Field], Mapping[str, object]]]
FILTER_DEFAULTS = FILTER_FOR_DBFIELD_DEFAULTS

@classmethod
def filter_for_lookup(
cls, field: django.db.models.Field, lookup_type: str
) -> Tuple[Type[django_filters.filters.Filter], Mapping[str, object]]:
filter_class, params = super().filter_for_lookup(field, lookup_type)

# Override RUT containment lookups.
if isinstance(field, cl_sii.extras.dj_model_fields.RutField) and lookup_type in (
'contains',
'icontains',
):
filter_class, params = django_filters.filters.CharFilter, {}

return filter_class, params
68 changes: 68 additions & 0 deletions src/tests/test_extras_dj_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import django_filters

from cl_sii.extras import dj_form_fields, dj_model_fields
from cl_sii.extras.dj_filters import RutFilter, SiiFilterSet


Expand All @@ -22,6 +23,34 @@ def test_new_instance(self) -> None:
self.assertIsInstance(filter, RutFilter)
self.assertIsInstance(filter, django_filters.filters.Filter)

def test_filter_class_lookup_expressions(self) -> None:
expected_field_class = dj_form_fields.RutField
for lookup_expr in [
'exact',
'iexact',
'in',
'gt',
'gte',
'lt',
'lte',
]:
with self.subTest(field_class=expected_field_class, lookup_expr=lookup_expr):
filter_instance = RutFilter(lookup_expr=lookup_expr)
self.assertIs(filter_instance.field_class, expected_field_class)

expected_field_class = django_filters.CharFilter.field_class
for lookup_expr in [
'contains',
'icontains',
'startswith',
'istartswith',
'endswith',
'iendswith',
]:
with self.subTest(field_class=expected_field_class, lookup_expr=lookup_expr):
filter_instance = RutFilter(lookup_expr=lookup_expr)
self.assertIs(filter_instance.field_class, expected_field_class)

# TODO: Add tests.


Expand All @@ -34,4 +63,43 @@ class SiiFilterSetTest(unittest.TestCase):
def test_filter_for_lookup(self) -> None:
assert SiiFilterSet.filter_for_lookup()

def test_filter_for_lookup_types(self) -> None:
field = dj_model_fields.RutField()

expected_field_class = dj_form_fields.RutField
for lookup_type in [
'exact',
'iexact',
'gt',
'gte',
'lt',
'lte',
]:
with self.subTest(field_class=expected_field_class, lookup_type=lookup_type):
filter_class, params = SiiFilterSet.filter_for_lookup(field, lookup_type)
filter_instance = filter_class(**{'lookup_expr': lookup_type, **params})
self.assertIs(filter_instance.field_class, expected_field_class)

for lookup_type in [
'in',
]:
with self.subTest(field_class=expected_field_class, lookup_type=lookup_type):
filter_class, params = SiiFilterSet.filter_for_lookup(field, lookup_type)
filter_instance = filter_class(**{'lookup_expr': lookup_type, **params})
self.assertTrue(issubclass(filter_instance.field_class, expected_field_class))

expected_field_class = django_filters.CharFilter.field_class
for lookup_type in [
'contains',
'icontains',
'startswith',
'istartswith',
'endswith',
'iendswith',
]:
with self.subTest(field_class=expected_field_class, lookup_type=lookup_type):
filter_class, params = SiiFilterSet.filter_for_lookup(field, lookup_type)
filter_instance = filter_class(**{'lookup_expr': lookup_type, **params})
self.assertIs(filter_instance.field_class, expected_field_class)

# TODO: Add tests.
Loading