From 8c5cb163a1a81b8de0a56680f5b09aea91fa1dc8 Mon Sep 17 00:00:00 2001 From: Daniel Quinn Date: Sat, 12 Mar 2016 20:45:13 +0000 Subject: [PATCH] The API now supports filtering docs by tag & correspondent, as well as ordering --- docs/api.rst | 2 +- src/documents/filters.py | 135 +++++++++++++++++++++++++++------------ src/documents/views.py | 24 ++++--- 3 files changed, 112 insertions(+), 49 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 15ca9bc44..d08826a33 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5,7 +5,7 @@ The REST API Paperless makes use of the `Django REST Framework`_ standard API interface because of its inherent awesomeness. Conveniently, the system is also -self-documenting, so learn more about the access points, schema, what's +self-documenting, so to learn more about the access points, schema, what's accepted and what isn't, you need only visit ``/api`` on your local Paperless installation. diff --git a/src/documents/filters.py b/src/documents/filters.py index f2a5ea69a..a7c069d33 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -4,45 +4,31 @@ from rest_framework import filters from .models import Document, Correspondent, Tag - -class DocumentFilter(filters.FilterSet): - - title__startswith = django_filters.CharFilter( - name="title", lookup_type="startswith", - label="Title starts with (case sensitive)" - ) - title__istartswith = django_filters.CharFilter( - name="title", lookup_type="istartswith", - label="Title starts with (case insensitive)" - ) - title__endswith = django_filters.CharFilter( - name="title", lookup_type="endswith", - label="Title ends with (case sensitive)" - ) - title__iendswith = django_filters.CharFilter( - name="title", lookup_type="endswith", - label="Title ends with (case insensitive)" - ) - title__contains = django_filters.CharFilter( - name="title", lookup_type="contains", - label="Title contains (case sensitive)" - ) - title__icontains = django_filters.CharFilter( - name="title", lookup_type="icontains", - label="Title contains (case insensitive)" - ) - - content__contains = django_filters.CharFilter( - name="content", lookup_type="contains") - content__icontains = django_filters.CharFilter( - name="content", lookup_type="icontains") - - class Meta(object): - model = Document - fields = ["title"] +# +# I hate how copy/pastey this file is. Recommendations are welcome. +# -class SluggableFilter(filters.FilterSet): +# Filters + + +class RelatedFilter(django_filters.MethodFilter): + + def __init__(self, *args, **kwargs): + self.key = kwargs.pop("key") + self.lookup_type = kwargs.get("lookup_type") + django_filters.MethodFilter.__init__(self, *args, **kwargs) + + def filter(self, qs, value): + if not value: + return qs + return qs.filter(**{"tags__{}".format(self.key): value}) + + +# FilterSets + + +class SluggableFilterSet(filters.FilterSet): name__startswith = django_filters.CharFilter( name="name", lookup_type="startswith", @@ -83,15 +69,84 @@ class SluggableFilter(filters.FilterSet): ) -class CorrespondentFilter(SluggableFilter): +class CorrespondentFilterSet(SluggableFilterSet): class Meta(object): model = Correspondent fields = ["name"] -class TagFilter(SluggableFilter): +class TagFilterSet(SluggableFilterSet): class Meta(object): model = Tag - fields = ["name"] + fields = ["name", "slug"] + + +class DocumentFilterSet(filters.FilterSet): + + title__startswith = django_filters.CharFilter( + name="title", lookup_type="startswith", + label="Title starts with (case sensitive)" + ) + title__istartswith = django_filters.CharFilter( + name="title", lookup_type="istartswith", + label="Title starts with (case insensitive)" + ) + title__endswith = django_filters.CharFilter( + name="title", lookup_type="endswith", + label="Title ends with (case sensitive)" + ) + title__iendswith = django_filters.CharFilter( + name="title", lookup_type="endswith", + label="Title ends with (case insensitive)" + ) + title__contains = django_filters.CharFilter( + name="title", lookup_type="contains", + label="Title contains (case sensitive)" + ) + title__icontains = django_filters.CharFilter( + name="title", lookup_type="icontains", + label="Title contains (case insensitive)" + ) + + content__contains = django_filters.CharFilter( + name="content", lookup_type="contains") + content__icontains = django_filters.CharFilter( + name="content", lookup_type="icontains") + + tags__name = RelatedFilter(key="name") + tags__name__startswith = RelatedFilter(key="name__startswith") + tags__name__istartswith = RelatedFilter(key="name__istartswith") + tags__name__endswith = RelatedFilter(key="name__endswith") + tags__name__iendswith = RelatedFilter(key="name__iendswith") + tags__name__contains = RelatedFilter(key="name__contains") + tags__name__icontains = RelatedFilter(key="name__icontains") + + tags__slug = RelatedFilter(key="slug") + tags__slug__startswith = RelatedFilter(key="slug__startswith") + tags__slug__istartswith = RelatedFilter(key="slug__istartswith") + tags__slug__endswith = RelatedFilter(key="slug__endswith") + tags__slug__iendswith = RelatedFilter(key="slug__iendswith") + tags__slug__contains = RelatedFilter(key="slug__contains") + tags__slug__icontains = RelatedFilter(key="slug__icontains") + + correspondent__name = RelatedFilter(key="name") + correspondent__name__startswith = RelatedFilter(key="name__startswith") + correspondent__name__istartswith = RelatedFilter(key="name__istartswith") + correspondent__name__endswith = RelatedFilter(key="name__endswith") + correspondent__name__iendswith = RelatedFilter(key="name__iendswith") + correspondent__name__contains = RelatedFilter(key="name__contains") + correspondent__name__icontains = RelatedFilter(key="name__icontains") + + correspondent__slug = RelatedFilter(key="slug") + correspondent__slug__startswith = RelatedFilter(key="slug__startswith") + correspondent__slug__istartswith = RelatedFilter(key="slug__istartswith") + correspondent__slug__endswith = RelatedFilter(key="slug__endswith") + correspondent__slug__iendswith = RelatedFilter(key="slug__iendswith") + correspondent__slug__contains = RelatedFilter(key="slug__contains") + correspondent__slug__icontains = RelatedFilter(key="slug__icontains") + + class Meta(object): + model = Document + fields = ["title"] diff --git a/src/documents/views.py b/src/documents/views.py index 94db7abf0..6d56219f4 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -13,7 +13,7 @@ from rest_framework.viewsets import ( from paperless.db import GnuPG -from .filters import DocumentFilter, CorrespondentFilter, TagFilter +from .filters import DocumentFilterSet, CorrespondentFilterSet, TagFilterSet from .forms import UploadForm from .models import Correspondent, Tag, Document, Log from .serialisers import ( @@ -94,8 +94,9 @@ class CorrespondentViewSet(ModelViewSet): serializer_class = CorrespondentSerializer pagination_class = StandardPagination permission_classes = (IsAuthenticated,) - filter_backends = (filters.DjangoFilterBackend,) - filter_class = CorrespondentFilter + filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter) + filter_class = CorrespondentFilterSet + ordering_fields = ("name", "slug") class TagViewSet(ModelViewSet): @@ -104,8 +105,9 @@ class TagViewSet(ModelViewSet): serializer_class = TagSerializer pagination_class = StandardPagination permission_classes = (IsAuthenticated,) - filter_backends = (filters.DjangoFilterBackend,) - filter_class = TagFilter + filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter) + filter_class = TagFilterSet + ordering_fields = ("name", "slug") class DocumentViewSet(RetrieveModelMixin, @@ -118,9 +120,14 @@ class DocumentViewSet(RetrieveModelMixin, serializer_class = DocumentSerializer pagination_class = StandardPagination permission_classes = (IsAuthenticated,) - filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter) - filter_class = DocumentFilter + filter_backends = ( + filters.DjangoFilterBackend, + filters.SearchFilter, + filters.OrderingFilter + ) + filter_class = DocumentFilterSet search_fields = ("title", "correspondent__name", "content") + ordering_fields = ("id", "title", "correspondent__name") class LogViewSet(ReadOnlyModelViewSet): @@ -129,4 +136,5 @@ class LogViewSet(ReadOnlyModelViewSet): serializer_class = LogSerializer pagination_class = StandardPagination permission_classes = (IsAuthenticated,) - filter_backends = (filters.DjangoFilterBackend,) + filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter) + ordering_fields = ("time",)