From 3294b37ef0119890e58acb30fb8643ec4f1c311a Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Wed, 5 Apr 2023 00:46:36 +0200 Subject: [PATCH] Reduce duplication with filters (#4583) * Reduce duplication with filters * fix typo * fix naming --- InvenTree/InvenTree/api.py | 11 ++----- InvenTree/InvenTree/filters.py | 25 +++++++++++++-- InvenTree/build/api.py | 8 ++--- InvenTree/common/api.py | 22 +++---------- InvenTree/company/api.py | 39 +++++------------------ InvenTree/order/api.py | 46 ++++++--------------------- InvenTree/part/api.py | 57 ++++++++-------------------------- InvenTree/plugin/api.py | 10 ++---- InvenTree/stock/api.py | 41 ++++++------------------ 9 files changed, 74 insertions(+), 185 deletions(-) diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index 798397bd95..81c2ac7f66 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -5,15 +5,14 @@ from django.db import transaction from django.http import JsonResponse from django.utils.translation import gettext_lazy as _ -from django_filters.rest_framework import DjangoFilterBackend from django_q.models import OrmQ -from rest_framework import filters, permissions +from rest_framework import permissions from rest_framework.response import Response from rest_framework.serializers import ValidationError from rest_framework.views import APIView import users.models -from InvenTree.filters import InvenTreeSearchFilter +from InvenTree.filters import SEARCH_ORDER_FILTER from InvenTree.mixins import ListCreateAPI from InvenTree.permissions import RolePermission from part.templatetags.inventree_extras import plugins_info @@ -202,11 +201,7 @@ class AttachmentMixin: RolePermission, ] - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER def perform_create(self, serializer): """Save the user information when a file is uploaded.""" diff --git a/InvenTree/InvenTree/filters.py b/InvenTree/InvenTree/filters.py index bdd1f3a81b..658de57d87 100644 --- a/InvenTree/InvenTree/filters.py +++ b/InvenTree/InvenTree/filters.py @@ -1,11 +1,12 @@ """General filters for InvenTree.""" -from rest_framework.filters import OrderingFilter, SearchFilter +from django_filters import rest_framework as rest_filters +from rest_framework import filters from InvenTree.helpers import str2bool -class InvenTreeSearchFilter(SearchFilter): +class InvenTreeSearchFilter(filters.SearchFilter): """Custom search filter which allows adjusting of search terms dynamically""" def get_search_fields(self, view, request): @@ -59,7 +60,7 @@ class InvenTreeSearchFilter(SearchFilter): return terms -class InvenTreeOrderingFilter(OrderingFilter): +class InvenTreeOrderingFilter(filters.OrderingFilter): """Custom OrderingFilter class which allows aliased filtering of related fields. To use, simply specify this filter in the "filter_backends" section. @@ -130,3 +131,21 @@ class InvenTreeOrderingFilter(OrderingFilter): ordering.append(a) return ordering + + +SEARCH_ORDER_FILTER = [ + rest_filters.DjangoFilterBackend, + InvenTreeSearchFilter, + filters.OrderingFilter, +] + +SEARCH_ORDER_FILTER_ALIAS = [ + rest_filters.DjangoFilterBackend, + InvenTreeSearchFilter, + InvenTreeOrderingFilter, +] + +ORDER_FILTER = [ + rest_filters.DjangoFilterBackend, + filters.OrderingFilter, +] diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index 05fb8c7b84..a76b51fd15 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -11,7 +11,6 @@ from django_filters import rest_framework as rest_filters from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView, StatusView from InvenTree.helpers import str2bool, isNull, DownloadFile -from InvenTree.filters import InvenTreeOrderingFilter, InvenTreeSearchFilter from InvenTree.status_codes import BuildStatus from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI @@ -20,6 +19,7 @@ import build.serializers from build.models import Build, BuildItem, BuildOrderAttachment import part.models from users.models import Owner +from InvenTree.filters import SEARCH_ORDER_FILTER_ALIAS class BuildFilter(rest_filters.FilterSet): @@ -99,11 +99,7 @@ class BuildList(APIDownloadMixin, ListCreateAPI): serializer_class = build.serializers.BuildSerializer filterset_class = BuildFilter - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_fields = [ 'reference', diff --git a/InvenTree/common/api.py b/InvenTree/common/api.py index 043b8aab14..80e509173f 100644 --- a/InvenTree/common/api.py +++ b/InvenTree/common/api.py @@ -7,10 +7,9 @@ from django.urls import include, path, re_path from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt -from django_filters.rest_framework import DjangoFilterBackend from django_q.tasks import async_task from djmoney.contrib.exchange.models import ExchangeBackend, Rate -from rest_framework import filters, permissions, serializers +from rest_framework import permissions, serializers from rest_framework.exceptions import NotAcceptable, NotFound from rest_framework.permissions import IsAdminUser from rest_framework.response import Response @@ -20,7 +19,7 @@ import common.models import common.serializers from InvenTree.api import BulkDeleteMixin from InvenTree.config import CONFIG_LOOKUPS -from InvenTree.filters import InvenTreeSearchFilter +from InvenTree.filters import ORDER_FILTER, SEARCH_ORDER_FILTER from InvenTree.helpers import inheritors from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI) @@ -168,11 +167,7 @@ class SettingsList(ListAPI): This is inheritted by all list views for settings. """ - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER ordering_fields = [ 'pk', @@ -340,11 +335,7 @@ class NotificationList(NotificationMessageMixin, BulkDeleteMixin, ListAPI): permission_classes = [permissions.IsAuthenticated, ] - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER ordering_fields = [ 'category', @@ -409,10 +400,7 @@ class NewsFeedMixin: class NewsFeedEntryList(NewsFeedMixin, BulkDeleteMixin, ListAPI): """List view for all news items.""" - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER ordering_fields = [ 'published', diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index 8be1181772..a0c4e8c34d 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -5,12 +5,12 @@ from django.urls import include, path, re_path from django_filters import rest_framework as rest_filters from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import filters import part.models from InvenTree.api import (AttachmentMixin, ListCreateDestroyAPIView, MetadataView) -from InvenTree.filters import InvenTreeOrderingFilter, InvenTreeSearchFilter +from InvenTree.filters import (ORDER_FILTER, SEARCH_ORDER_FILTER, + SEARCH_ORDER_FILTER_ALIAS) from InvenTree.helpers import str2bool from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI @@ -44,11 +44,7 @@ class CompanyList(ListCreateAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'is_customer', @@ -114,11 +110,7 @@ class ContactList(ListCreateDestroyAPIView): queryset = Contact.objects.all() serializer_class = ContactSerializer - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'company', @@ -192,11 +184,7 @@ class ManufacturerPartList(ListCreateDestroyAPIView): return self.serializer_class(*args, **kwargs) - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER search_fields = [ 'manufacturer__name', @@ -287,11 +275,7 @@ class ManufacturerPartParameterList(ListCreateDestroyAPIView): return self.serializer_class(*args, **kwargs) - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER search_fields = [ 'name', @@ -388,11 +372,7 @@ class SupplierPartList(ListCreateDestroyAPIView): serializer_class = SupplierPartSerializer - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_fields = [ 'SKU', @@ -493,10 +473,7 @@ class SupplierPriceBreakList(ListCreateAPI): return self.serializer_class(*args, **kwargs) - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER ordering_fields = [ 'quantity', diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 79a260d378..5769101799 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy as _ from django_filters import rest_framework as rest_filters from django_ical.views import ICalFeed -from rest_framework import filters, status +from rest_framework import status from rest_framework.exceptions import ValidationError from rest_framework.response import Response @@ -21,7 +21,7 @@ from common.settings import settings from company.models import SupplierPart from InvenTree.api import (APIDownloadMixin, AttachmentMixin, ListCreateDestroyAPIView, MetadataView, StatusView) -from InvenTree.filters import InvenTreeOrderingFilter, InvenTreeSearchFilter +from InvenTree.filters import SEARCH_ORDER_FILTER, SEARCH_ORDER_FILTER_ALIAS from InvenTree.helpers import DownloadFile, str2bool from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveUpdateDestroyAPI) @@ -61,11 +61,7 @@ class GeneralExtraLineList(APIDownloadMixin): return queryset - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter - ] + filter_backends = SEARCH_ORDER_FILTER ordering_fields = [ 'title', @@ -307,11 +303,7 @@ class PurchaseOrderList(PurchaseOrderMixin, APIDownloadMixin, ListCreateAPI): return queryset - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_field_aliases = { 'reference': ['reference_int', 'reference'], @@ -508,11 +500,7 @@ class PurchaseOrderLineItemList(PurchaseOrderLineItemMixin, APIDownloadMixin, Li return DownloadFile(filedata, filename) - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_field_aliases = { 'MPN': 'part__manufacturer_part__MPN', @@ -693,11 +681,7 @@ class SalesOrderList(SalesOrderMixin, APIDownloadMixin, ListCreateAPI): return queryset - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_field_aliases = { 'reference': ['reference_int', 'reference'], @@ -817,11 +801,7 @@ class SalesOrderLineItemList(SalesOrderLineItemMixin, APIDownloadMixin, ListCrea return DownloadFile(filedata, filename) - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter - ] + filter_backends = SEARCH_ORDER_FILTER ordering_fields = [ 'part__name', @@ -1154,11 +1134,7 @@ class ReturnOrderList(ReturnOrderMixin, APIDownloadMixin, ListCreateAPI): return DownloadFile(filedata, filename) - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_field_aliases = { 'reference': ['reference_int', 'reference'], @@ -1302,11 +1278,7 @@ class ReturnOrderLineItemList(ReturnOrderLineItemMixin, APIDownloadMixin, ListCr raise NotImplementedError("download_queryset not yet implemented for this endpoint") - filter_backends = [ - rest_filters.DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER ordering_fields = [ 'reference', diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 0d20eb8e9f..7144108b3c 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -9,7 +9,7 @@ from django.utils.translation import gettext_lazy as _ from django_filters import rest_framework as rest_filters from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import filters, permissions, serializers, status +from rest_framework import permissions, serializers, status from rest_framework.exceptions import ValidationError from rest_framework.response import Response @@ -17,7 +17,9 @@ import order.models from build.models import Build, BuildItem from InvenTree.api import (APIDownloadMixin, AttachmentMixin, ListCreateDestroyAPIView, MetadataView) -from InvenTree.filters import InvenTreeOrderingFilter, InvenTreeSearchFilter +from InvenTree.filters import (ORDER_FILTER, SEARCH_ORDER_FILTER, + SEARCH_ORDER_FILTER_ALIAS, + InvenTreeSearchFilter) from InvenTree.helpers import (DownloadFile, increment_serial_number, isNull, str2bool, str2int) from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI, @@ -152,11 +154,7 @@ class CategoryList(CategoryMixin, APIDownloadMixin, ListCreateAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'name', @@ -220,10 +218,7 @@ class CategoryTree(ListAPI): queryset = PartCategory.objects.all() serializer_class = part_serializers.CategoryTree - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER # Order by tree level (top levels first) and then name ordering = ['level', 'name'] @@ -384,11 +379,7 @@ class PartTestTemplateList(ListCreateAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - InvenTreeSearchFilter, - ] + filter_backends = SEARCH_ORDER_FILTER class PartThumbs(ListAPI): @@ -1224,11 +1215,7 @@ class PartList(PartMixin, APIDownloadMixin, ListCreateAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_fields = [ 'name', @@ -1337,11 +1324,7 @@ class PartParameterTemplateList(ListCreateAPI): queryset = PartParameterTemplate.objects.all() serializer_class = part_serializers.PartParameterTemplateSerializer - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - InvenTreeSearchFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'name', @@ -1462,10 +1445,7 @@ class PartStocktakeList(ListCreateAPI): return context - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER ordering_fields = [ 'part', @@ -1496,10 +1476,7 @@ class PartStocktakeReportList(ListAPI): queryset = PartStocktakeReport.objects.all() serializer_class = part_serializers.PartStocktakeReportSerializer - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER ordering_fields = [ 'date', @@ -1731,11 +1708,7 @@ class BomList(BomMixin, ListCreateDestroyAPIView): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS search_fields = [ 'reference', @@ -1836,11 +1809,7 @@ class BomItemSubstituteList(ListCreateAPI): serializer_class = part_serializers.BomItemSubstituteSerializer queryset = BomItemSubstitute.objects.all() - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'part', diff --git a/InvenTree/plugin/api.py b/InvenTree/plugin/api.py index cab07a5e9c..7491b13587 100644 --- a/InvenTree/plugin/api.py +++ b/InvenTree/plugin/api.py @@ -3,13 +3,13 @@ from django.urls import include, path, re_path from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import filters, permissions, status +from rest_framework import permissions, status from rest_framework.exceptions import NotFound from rest_framework.response import Response import plugin.serializers as PluginSerializers from common.api import GlobalSettingsPermissions -from InvenTree.filters import InvenTreeSearchFilter +from InvenTree.filters import SEARCH_ORDER_FILTER from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, UpdateAPI) from InvenTree.permissions import IsSuperuser @@ -57,11 +57,7 @@ class PluginList(ListAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'active', diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index aa5dd98bf4..f9b784a46c 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -11,8 +11,7 @@ from django.urls import include, path, re_path from django.utils.translation import gettext_lazy as _ from django_filters import rest_framework as rest_filters -from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import filters, status +from rest_framework import status from rest_framework.response import Response from rest_framework.serializers import ValidationError @@ -24,7 +23,8 @@ from company.models import Company, SupplierPart from company.serializers import CompanySerializer, SupplierPartSerializer from InvenTree.api import (APIDownloadMixin, AttachmentMixin, ListCreateDestroyAPIView, MetadataView, StatusView) -from InvenTree.filters import InvenTreeOrderingFilter, InvenTreeSearchFilter +from InvenTree.filters import (ORDER_FILTER, SEARCH_ORDER_FILTER, + SEARCH_ORDER_FILTER_ALIAS) from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull, str2bool, str2int) from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI, @@ -294,11 +294,7 @@ class StockLocationList(APIDownloadMixin, ListCreateAPI): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'name', @@ -333,10 +329,7 @@ class StockLocationTree(ListAPI): queryset = StockLocation.objects.all() serializer_class = StockSerializers.LocationTreeSerializer - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - ] + filter_backends = ORDER_FILTER # Order by tree level (top levels first) and then name ordering = ['level', 'name'] @@ -1007,11 +1000,7 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView): return queryset - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - InvenTreeOrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER_ALIAS ordering_field_aliases = { 'SKU': 'supplier_part__SKU', @@ -1054,11 +1043,7 @@ class StockAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): queryset = StockItemAttachment.objects.all() serializer_class = StockSerializers.StockItemAttachmentSerializer - filter_backends = [ - DjangoFilterBackend, - filters.OrderingFilter, - InvenTreeSearchFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'stock_item', @@ -1085,11 +1070,7 @@ class StockItemTestResultList(ListCreateDestroyAPIView): queryset = StockItemTestResult.objects.all() serializer_class = StockSerializers.StockItemTestResultSerializer - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'test', @@ -1310,11 +1291,7 @@ class StockTrackingList(ListAPI): headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - filter_backends = [ - DjangoFilterBackend, - InvenTreeSearchFilter, - filters.OrderingFilter, - ] + filter_backends = SEARCH_ORDER_FILTER filterset_fields = [ 'item',