mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Fixes #164
There appears to be quite the mess out there with regard to how DRF
handles filtering.  DRF has its own built-in stuff, but recommends
django_filter for the advanced stuff, which has its own overriding
module that explodes with this message when used as per the
documentation:
  AttributeError: 'NoneType' object has no attribute 'DjangoFilterBackend'
Then there's djangorestframework-filter, another package that claims to
do the same thing, that does everything just differently enough that
nothing worked while I had it enabled.
I ended up using django_filter, but doing so importing each element
explicitly, rather than just using the recommended (and broken, at least
in this project) method of:
    import django_filter.restframework as fitlers
Anyway, this should bring the dependencies up to date, and strips out a
lot of redundant code.
			
			
This commit is contained in:
		@@ -1,10 +1,9 @@
 | 
			
		||||
Django==1.10.3
 | 
			
		||||
Django==1.10.4
 | 
			
		||||
Pillow>=3.1.1
 | 
			
		||||
django-crispy-forms>=1.6.0
 | 
			
		||||
django-extensions>=1.6.1
 | 
			
		||||
django-filter>=0.12.0,<1.0
 | 
			
		||||
django-filter>=1.0
 | 
			
		||||
djangorestframework>=3.4.4
 | 
			
		||||
djangorestframework-filters>=0.8.0
 | 
			
		||||
filemagic>=1.6
 | 
			
		||||
langdetect>=1.0.5
 | 
			
		||||
pyocr>=0.3.1
 | 
			
		||||
 
 | 
			
		||||
@@ -1,152 +1,58 @@
 | 
			
		||||
import django_filters
 | 
			
		||||
from django_filters.rest_framework import CharFilter, FilterSet
 | 
			
		||||
 | 
			
		||||
from rest_framework import filters
 | 
			
		||||
 | 
			
		||||
from .models import Document, Correspondent, Tag
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# I hate how copy/pastey this file is.  Recommendations are welcome.
 | 
			
		||||
#
 | 
			
		||||
from .models import Correspondent, Document, Tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 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",
 | 
			
		||||
        label="Name starts with (case sensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    name__istartswith = django_filters.CharFilter(
 | 
			
		||||
        name="name", lookup_type="istartswith",
 | 
			
		||||
        label="Name starts with (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    name__endswith = django_filters.CharFilter(
 | 
			
		||||
        name="name", lookup_type="endswith",
 | 
			
		||||
        label="Name ends with (case sensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    name__iendswith = django_filters.CharFilter(
 | 
			
		||||
        name="name", lookup_type="endswith",
 | 
			
		||||
        label="Name ends with (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    name__contains = django_filters.CharFilter(
 | 
			
		||||
        name="name", lookup_type="contains",
 | 
			
		||||
        label="Name contains (case sensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    name__icontains = django_filters.CharFilter(
 | 
			
		||||
        name="name", lookup_type="icontains",
 | 
			
		||||
        label="Name contains (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    slug__istartswith = django_filters.CharFilter(
 | 
			
		||||
        name="slug", lookup_type="istartswith",
 | 
			
		||||
        label="Slug starts with (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    slug__iendswith = django_filters.CharFilter(
 | 
			
		||||
        name="slug", lookup_type="endswith",
 | 
			
		||||
        label="Slug ends with (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
    slug__icontains = django_filters.CharFilter(
 | 
			
		||||
        name="slug", lookup_type="icontains",
 | 
			
		||||
        label="Slug contains (case insensitive)"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CorrespondentFilterSet(SluggableFilterSet):
 | 
			
		||||
class CorrespondentFilterSet(FilterSet):
 | 
			
		||||
 | 
			
		||||
    class Meta(object):
 | 
			
		||||
        model = Correspondent
 | 
			
		||||
        fields = ["name"]
 | 
			
		||||
        fields = {
 | 
			
		||||
            'name': [
 | 
			
		||||
                "startswith", "endswith", "contains",
 | 
			
		||||
                "istartswith", "iendswith", "icontains"
 | 
			
		||||
            ],
 | 
			
		||||
            "slug": ["istartswith", "iendswith", "icontains"]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TagFilterSet(SluggableFilterSet):
 | 
			
		||||
class TagFilterSet(FilterSet):
 | 
			
		||||
 | 
			
		||||
    class Meta(object):
 | 
			
		||||
        model = Tag
 | 
			
		||||
        fields = ["name", "slug"]
 | 
			
		||||
        fields = {
 | 
			
		||||
            'name': [
 | 
			
		||||
                "startswith", "endswith", "contains",
 | 
			
		||||
                "istartswith", "iendswith", "icontains"
 | 
			
		||||
            ],
 | 
			
		||||
            "slug": ["istartswith", "iendswith", "icontains"]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DocumentFilterSet(filters.FilterSet):
 | 
			
		||||
class DocumentFilterSet(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)"
 | 
			
		||||
    )
 | 
			
		||||
    CHAR_KWARGS = {
 | 
			
		||||
        "lookup_expr": (
 | 
			
		||||
            "startswith",
 | 
			
		||||
            "endswith",
 | 
			
		||||
            "contains",
 | 
			
		||||
            "istartswith",
 | 
			
		||||
            "iendswith",
 | 
			
		||||
            "icontains"
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
    correspondent__name = CharFilter(name="correspondent__name", **CHAR_KWARGS)
 | 
			
		||||
    correspondent__slug = CharFilter(name="correspondent__slug", **CHAR_KWARGS)
 | 
			
		||||
    tags__name = CharFilter(name="tags__name", **CHAR_KWARGS)
 | 
			
		||||
    tags__slug = CharFilter(name="tags__slug", **CHAR_KWARGS)
 | 
			
		||||
 | 
			
		||||
    class Meta(object):
 | 
			
		||||
        model = Document
 | 
			
		||||
        fields = ["title"]
 | 
			
		||||
        fields = {
 | 
			
		||||
            "title": [
 | 
			
		||||
                "startswith", "endswith", "contains",
 | 
			
		||||
                "istartswith", "iendswith", "icontains"
 | 
			
		||||
            ],
 | 
			
		||||
            "content": ["contains", "icontains"],
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,33 @@
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.http import HttpResponse
 | 
			
		||||
from django.views.decorators.csrf import csrf_exempt
 | 
			
		||||
from django.views.generic import FormView, DetailView, TemplateView
 | 
			
		||||
 | 
			
		||||
from rest_framework import filters
 | 
			
		||||
from django.views.generic import DetailView, FormView, TemplateView
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework.filters import SearchFilter, OrderingFilter
 | 
			
		||||
from paperless.db import GnuPG
 | 
			
		||||
from rest_framework.mixins import (
 | 
			
		||||
    RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin)
 | 
			
		||||
    DestroyModelMixin,
 | 
			
		||||
    ListModelMixin,
 | 
			
		||||
    RetrieveModelMixin,
 | 
			
		||||
    UpdateModelMixin
 | 
			
		||||
)
 | 
			
		||||
from rest_framework.pagination import PageNumberPagination
 | 
			
		||||
from rest_framework.permissions import IsAuthenticated
 | 
			
		||||
from rest_framework.viewsets import (
 | 
			
		||||
    ModelViewSet, ReadOnlyModelViewSet, GenericViewSet)
 | 
			
		||||
    GenericViewSet,
 | 
			
		||||
    ModelViewSet,
 | 
			
		||||
    ReadOnlyModelViewSet
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from paperless.db import GnuPG
 | 
			
		||||
 | 
			
		||||
from .filters import DocumentFilterSet, CorrespondentFilterSet, TagFilterSet
 | 
			
		||||
from .filters import CorrespondentFilterSet, DocumentFilterSet, TagFilterSet
 | 
			
		||||
from .forms import UploadForm
 | 
			
		||||
from .models import Correspondent, Tag, Document, Log
 | 
			
		||||
from .models import Correspondent, Document, Log, Tag
 | 
			
		||||
from .serialisers import (
 | 
			
		||||
    CorrespondentSerializer, TagSerializer, DocumentSerializer, LogSerializer)
 | 
			
		||||
    CorrespondentSerializer,
 | 
			
		||||
    DocumentSerializer,
 | 
			
		||||
    LogSerializer,
 | 
			
		||||
    TagSerializer
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IndexView(TemplateView):
 | 
			
		||||
@@ -94,7 +104,7 @@ class CorrespondentViewSet(ModelViewSet):
 | 
			
		||||
    serializer_class = CorrespondentSerializer
 | 
			
		||||
    pagination_class = StandardPagination
 | 
			
		||||
    permission_classes = (IsAuthenticated,)
 | 
			
		||||
    filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
 | 
			
		||||
    filter_backends = (DjangoFilterBackend, OrderingFilter)
 | 
			
		||||
    filter_class = CorrespondentFilterSet
 | 
			
		||||
    ordering_fields = ("name", "slug")
 | 
			
		||||
 | 
			
		||||
@@ -105,7 +115,7 @@ class TagViewSet(ModelViewSet):
 | 
			
		||||
    serializer_class = TagSerializer
 | 
			
		||||
    pagination_class = StandardPagination
 | 
			
		||||
    permission_classes = (IsAuthenticated,)
 | 
			
		||||
    filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
 | 
			
		||||
    filter_backends = (DjangoFilterBackend, OrderingFilter)
 | 
			
		||||
    filter_class = TagFilterSet
 | 
			
		||||
    ordering_fields = ("name", "slug")
 | 
			
		||||
 | 
			
		||||
@@ -120,11 +130,7 @@ class DocumentViewSet(RetrieveModelMixin,
 | 
			
		||||
    serializer_class = DocumentSerializer
 | 
			
		||||
    pagination_class = StandardPagination
 | 
			
		||||
    permission_classes = (IsAuthenticated,)
 | 
			
		||||
    filter_backends = (
 | 
			
		||||
        filters.DjangoFilterBackend,
 | 
			
		||||
        filters.SearchFilter,
 | 
			
		||||
        filters.OrderingFilter
 | 
			
		||||
    )
 | 
			
		||||
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
 | 
			
		||||
    filter_class = DocumentFilterSet
 | 
			
		||||
    search_fields = ("title", "correspondent__name", "content")
 | 
			
		||||
    ordering_fields = (
 | 
			
		||||
@@ -137,5 +143,5 @@ class LogViewSet(ReadOnlyModelViewSet):
 | 
			
		||||
    serializer_class = LogSerializer
 | 
			
		||||
    pagination_class = StandardPagination
 | 
			
		||||
    permission_classes = (IsAuthenticated,)
 | 
			
		||||
    filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
 | 
			
		||||
    filter_backends = (DjangoFilterBackend, OrderingFilter)
 | 
			
		||||
    ordering_fields = ("time",)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@ if os.path.exists("/etc/paperless.conf"):
 | 
			
		||||
    load_dotenv("/etc/paperless.conf")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Application definition
 | 
			
		||||
 | 
			
		||||
INSTALLED_APPS = [
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user