File: mixins.py

package info (click to toggle)
drf-haystack 1.9.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 564 kB
  • sloc: python: 2,608; makefile: 147
file content (122 lines) | stat: -rw-r--r-- 4,500 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# -*- coding: utf-8 -*-

from __future__ import absolute_import, unicode_literals

from rest_framework.decorators import action
from rest_framework.response import Response

from drf_haystack.filters import HaystackFacetFilter


class MoreLikeThisMixin(object):
    """
    Mixin class for supporting "more like this" on an API View.
    """

    @action(detail=True, methods=["get"], url_path="more-like-this")
    def more_like_this(self, request, pk=None):
        """
        Sets up a detail route for ``more-like-this`` results.
        Note that you'll need backend support in order to take advantage of this.

        This will add ie. ^search/{pk}/more-like-this/$ to your existing ^search pattern.
        """
        obj = self.get_object().object
        queryset = self.filter_queryset(self.get_queryset()).more_like_this(obj)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class FacetMixin(object):
    """
    Mixin class for supporting faceting on an API View.
    """

    facet_filter_backends = [HaystackFacetFilter]
    facet_serializer_class = None
    facet_objects_serializer_class = None
    facet_query_params_text = 'selected_facets'

    @action(detail=False, methods=["get"], url_path="facets")
    def facets(self, request):
        """
        Sets up a list route for ``faceted`` results.
        This will add ie ^search/facets/$ to your existing ^search pattern.
        """
        queryset = self.filter_facet_queryset(self.get_queryset())

        for facet in request.query_params.getlist(self.facet_query_params_text):

            if ":" not in facet:
                continue

            field, value = facet.split(":", 1)
            if value:
                queryset = queryset.narrow('%s:"%s"' % (field, queryset.query.clean(value)))

        serializer = self.get_facet_serializer(queryset.facet_counts(), objects=queryset, many=False)
        return Response(serializer.data)

    def filter_facet_queryset(self, queryset):
        """
        Given a search queryset, filter it with whichever facet filter backends
        in use.
        """
        for backend in list(self.facet_filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)

        if self.load_all:
            queryset = queryset.load_all()

        return queryset

    def get_facet_serializer(self, *args, **kwargs):
        """
        Return the facet serializer instance that should be used for
        serializing faceted output.
        """
        assert "objects" in kwargs, "`objects` is a required argument to `get_facet_serializer()`"

        facet_serializer_class = self.get_facet_serializer_class()
        kwargs["context"] = self.get_serializer_context()
        kwargs["context"].update({
            "objects": kwargs.pop("objects"),
            "facet_query_params_text": self.facet_query_params_text,
        })
        return facet_serializer_class(*args, **kwargs)

    def get_facet_serializer_class(self):
        """
        Return the class to use for serializing facets.
        Defaults to using ``self.facet_serializer_class``.
        """
        if self.facet_serializer_class is None:
            raise AttributeError(
                "%(cls)s should either include a `facet_serializer_class` attribute, "
                "or override %(cls)s.get_facet_serializer_class() method." %
                {"cls": self.__class__.__name__}
            )
        return self.facet_serializer_class

    def get_facet_objects_serializer(self, *args, **kwargs):
        """
        Return the serializer instance which should be used for
        serializing faceted objects.
        """
        facet_objects_serializer_class = self.get_facet_objects_serializer_class()
        kwargs["context"] = self.get_serializer_context()
        return facet_objects_serializer_class(*args, **kwargs)

    def get_facet_objects_serializer_class(self):
        """
        Return the class to use for serializing faceted objects.
        Defaults to using the views ``self.serializer_class`` if not
        ``self.facet_objects_serializer_class`` is set.
        """
        return self.facet_objects_serializer_class or super(FacetMixin, self).get_serializer_class()