Skip to content

Search

SearchFilterBackend

Bases: SearchFilter

Custom SearchFilter for better support of openapi.

Source code in saritasa_drf_tools/filters/search.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class SearchFilterBackend(filters.SearchFilter):
    """Custom SearchFilter for better support of openapi."""

    def get_schema_operation_parameters(
        self,
        view: views.APIView,
    ) -> list[dict[str, typing.Any]]:
        """Prepare parameters for openapi schema.

        Check that view has `search_fields`.

        Check that `search_fields` contains valid set of fields. Actually,
        this check may perform some SQL queries during spec generation. Also,
        spec generation is not the best place for checking of source code
        (comparing to linters/django system checks/tests), but DRF doesn't
        validate `search_fields` for views while backend running.

        Extend view description with list of `search_fields`.

        """
        operation_parameters = super().get_schema_operation_parameters(
            view=view,
        )
        # Not using get_search_fields since it is requires additional params
        search_fields: collections.abc.Sequence[str] | None = getattr(
            view,
            "search_fields",
            None,
        )
        if not search_fields:  # pragma: no cover
            from drf_spectacular import drainage

            drainage.warn(
                f"`search_fields` are not set up for {view.__class__}",
            )
            return operation_parameters

        self._validate_search_fields(view)

        formatted_fields = ", ".join(f"`{field}`" for field in search_fields)
        operation_parameters[0]["description"] = (
            f"A search term.\n\nPerformed on this fields: {formatted_fields}."
        )
        return operation_parameters

    def _validate_search_fields(
        self,
        view: views.APIView,
    ) -> None:
        """Validate `search_fields` in view."""
        queryset = view.get_queryset()  # type: ignore
        try:
            search_dict = {}
            for search_field in view.search_fields:  # type: ignore
                search_arg = self.construct_search(
                    str(search_field),
                    queryset=queryset,
                )
                search_dict[search_arg] = "test"
            queryset.filter(**search_dict)
        except exceptions.FieldError as error:  # pragma: no cover
            from drf_spectacular import drainage

            view_action = getattr(view, "action", "list")
            drainage.warn(
                "`search_fields` contains non-existent or"
                f" non-related fields for action {view_action}."
                f" {error}",
            )

get_schema_operation_parameters(view)

Prepare parameters for openapi schema.

Check that view has search_fields.

Check that search_fields contains valid set of fields. Actually, this check may perform some SQL queries during spec generation. Also, spec generation is not the best place for checking of source code (comparing to linters/django system checks/tests), but DRF doesn't validate search_fields for views while backend running.

Extend view description with list of search_fields.

Source code in saritasa_drf_tools/filters/search.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def get_schema_operation_parameters(
    self,
    view: views.APIView,
) -> list[dict[str, typing.Any]]:
    """Prepare parameters for openapi schema.

    Check that view has `search_fields`.

    Check that `search_fields` contains valid set of fields. Actually,
    this check may perform some SQL queries during spec generation. Also,
    spec generation is not the best place for checking of source code
    (comparing to linters/django system checks/tests), but DRF doesn't
    validate `search_fields` for views while backend running.

    Extend view description with list of `search_fields`.

    """
    operation_parameters = super().get_schema_operation_parameters(
        view=view,
    )
    # Not using get_search_fields since it is requires additional params
    search_fields: collections.abc.Sequence[str] | None = getattr(
        view,
        "search_fields",
        None,
    )
    if not search_fields:  # pragma: no cover
        from drf_spectacular import drainage

        drainage.warn(
            f"`search_fields` are not set up for {view.__class__}",
        )
        return operation_parameters

    self._validate_search_fields(view)

    formatted_fields = ", ".join(f"`{field}`" for field in search_fields)
    operation_parameters[0]["description"] = (
        f"A search term.\n\nPerformed on this fields: {formatted_fields}."
    )
    return operation_parameters