diff --git a/fyle_accounting_mappings/utils.py b/fyle_accounting_mappings/utils.py index c34d901..7fa2a28 100644 --- a/fyle_accounting_mappings/utils.py +++ b/fyle_accounting_mappings/utils.py @@ -1,5 +1,7 @@ from rest_framework.views import Response from rest_framework.serializers import ValidationError +from rest_framework.filters import BaseFilterBackend +from django.db.models import Q def assert_valid(condition: bool, message: str) -> Response or None: @@ -14,6 +16,7 @@ def assert_valid(condition: bool, message: str) -> Response or None: 'message': message }) + class LookupFieldMixin: lookup_field = 'workspace_id' @@ -23,3 +26,19 @@ def filter_queryset(self, queryset): filter_kwargs = {self.lookup_field: lookup_value} queryset = queryset.filter(**filter_kwargs) return super().filter_queryset(queryset) + + +class JSONFieldFilterBackend(BaseFilterBackend): + """ + Custom filter backend to filter on JSONField for dynamic key lookups. + Supports filters like detail__{key} and detail__{key}__in. + """ + + def filter_queryset(self, request, queryset, view): + filters = Q() + + for param, value in request.query_params.items(): + if param.startswith('detail__'): + filters &= Q(**{param: value if '__in' not in param else value.split(',')}) + + return queryset.filter(filters) diff --git a/fyle_accounting_mappings/views.py b/fyle_accounting_mappings/views.py index 1171b98..7dbce9e 100644 --- a/fyle_accounting_mappings/views.py +++ b/fyle_accounting_mappings/views.py @@ -7,7 +7,7 @@ from rest_framework.views import status from django.db.models import Count, Q -from .utils import LookupFieldMixin +from .utils import LookupFieldMixin, JSONFieldFilterBackend from .exceptions import BulkError from .utils import assert_valid from .models import MappingSetting, Mapping, ExpenseAttribute, DestinationAttribute, EmployeeMapping, \ @@ -194,7 +194,7 @@ def get(self, request, *args, **kwargs): assert_valid(destination_type is not None, 'query param destination_type not found') filters = { - 'attribute_type' : source_type, + 'attribute_type': source_type, 'workspace_id': self.kwargs['workspace_id'] } @@ -234,8 +234,8 @@ def get(self, request, *args, **kwargs): ).count() else: filters = { - 'source_type' : source_type, - 'destination_type' : destination_type, + 'source_type': source_type, + 'destination_type': destination_type, 'workspace_id': self.kwargs['workspace_id'] } if source_type in ('PROJECT', 'CATEGORY'): @@ -358,8 +358,8 @@ def get_queryset(self): # Prepare filters for ExpenseAttribute base_filters = Q(workspace_id=self.kwargs['workspace_id']) & \ - Q(attribute_type='CATEGORY') & \ - Q(active=True) + Q(attribute_type='CATEGORY') & \ + Q(active=True) # Get the 'Activity' mapping and attribute activity_mapping = CategoryMapping.objects.filter( @@ -419,7 +419,7 @@ def get_queryset(self): ).values_list('source_employee_id', flat=True) filters = { - 'workspace_id' : self.kwargs['workspace_id'], + 'workspace_id': self.kwargs['workspace_id'], 'attribute_type': 'EMPLOYEE' } @@ -456,7 +456,7 @@ class DestinationAttributesView(LookupFieldMixin, ListAPIView): queryset = DestinationAttribute.objects.all().order_by('value') serializer_class = DestinationAttributeSerializer pagination_class = None - filter_backends = (DjangoFilterBackend,) + filter_backends = (DjangoFilterBackend, JSONFieldFilterBackend,) filterset_fields = {'attribute_type': {'exact', 'in'}, 'display_name': {'exact', 'in'}, 'active': {'exact'}} @@ -478,5 +478,5 @@ class PaginatedDestinationAttributesView(LookupFieldMixin, ListAPIView): """ queryset = DestinationAttribute.objects.filter(active=True).order_by('value') serializer_class = DestinationAttributeSerializer - filter_backends = (DjangoFilterBackend,) + filter_backends = (DjangoFilterBackend, JSONFieldFilterBackend,) filterset_class = DestinationAttributeFilter diff --git a/setup.py b/setup.py index a9c606f..7556d0b 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='fyle-accounting-mappings', - version='1.34.8', + version='1.34.9', author='Shwetabh Kumar', author_email='shwetabh.kumar@fyle.in', description='Django application to store the fyle accounting mappings in a generic manner',