django 自定义筛选器类中不必要的筛选器代码执行

dgtucam1  于 2023-11-20  发布在  Go
关注(0)|答案(3)|浏览(127)

bounty将在2小时后过期。回答此问题可获得+50的声望奖励。Karthik正在寻找可靠来源的答案

问题-1

  • 即使我们没有在请求查询中传递任何tag,也会执行datasetfiletagsquery函数

问题-2

  • 如果我按要求设置了dataset_id,那么它也会在retrieve和其他action API函数中强制执行。

上下文

  • 只有在请求中传递tag查询时,我才需要将dataset_id设置为所需查询

代码

class DatasetFileViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):

    class DatasetFileFilter(filters.FilterSet):

        def datasetfiletagsquery(request):
            try:
                dataset_id = request.query_params['dataset_id']
                qs = Tag.objects.filter(object_id=dataset_id, content_type=ContentType.objects.get_for_model(Dataset),
                                        category=Tag.CategoryChoices.DATASET_FILE)
            except KeyError:
                raise serializers.ValidationError({'dataset_id': 'This query is required'})
            return qs

        def datasetfiletagsfilter(queryset, name, value):
            tag_ids = [tag.pk for tag in value]
            qs = queryset.filter_by_tags(tag_ids)
            return qs

        dataset_id = filters.NumberFilter()
        tag = filters.ModelMultipleChoiceFilter(queryset=datasetfiletagsquery, to_field_name='name', method=datasetfiletagsfilter)

        class Meta:
            model = DatasetFile
            fields = ['dataset_id', 'tag']

    queryset = DatasetFile.objects.prefetch_related('tags', 'related_child_dataset_files__child').select_related('datasetbatchfile').extend_order_by()
    serializer_class = DatasetFileSerializer
    filterset_class = DatasetFileFilter

    def get_queryset(self):
        if self.action == 'download':
            qs = DatasetFile.objects.all()
        else:
            qs = super().get_queryset()
        qs = qs.filter(dataset__organization_id=self.request.user.organization_id)
        return qs

    @action(methods=['POST'], detail=True, url_path='download', serializer_class=DatasetFileDownloadSerializer)
    def download(self, request, pk):
        """
        api for downloading dataset file
        """
        instance = self.get_object()
        instance = instance.dataset.get_datasetfiles_for_download(datasetfile_ids=[instance.pk])[0]
        serializer = self.get_serializer(instance)
        return Response(serializer.data, status=status.HTTP_200_OK)

字符串

问题1的预期执行

  • 如果请求中不存在tag查询,则不应执行datasetfiletagsquery函数

问题2的预期执行

  • 筛选器的required字段必须仅对list API强制执行。
eeq64g8w

eeq64g8w1#

执行datasetfiletagsquery以获取所有可能的值,以便按标签进行过滤。它在两种情况下执行:
1.如果提供了标记,则检查它是否是允许的标记(这是您所期望的)。
1.如果没有提供标签,用户可以使用OPTIONS http方法请求获得过滤器允许的选项。这对于REST API在网页上显示过滤器中的可能值也很有用。
所以不可能不执行它,DRF在其代码中强制执行它。这里唯一的可能性是从filter字段中删除queryset= datasetfiletagsquery属性,并在过滤时手动验证该标记是否允许。
第二个问题可能也无法直接解决,除非您将其设置为DRF不需要的,并在过滤过程中手动验证它。这也会导致端点的错误模式(它不会指示列表方法中需要这些字段)。如果这对您来说是一个问题,您可以将模式覆盖到您自己手动创建的模式。
此外,在stackoverflow上有一个规则,每个问题只需要解决一个问题。

rryofs0p

rryofs0p2#

要解决问题1,请修改DatasetFileViewSet中的datasetfiletagquery,以便在请求中不存在“tag”参数时返回空或默认的queryset。对于问题2,仅当存在标记查询时,在同一视图集中重写get_filterset_kwargs,以便根据需要动态设置dataset_id,允许您根据请求的上下文选择性地强制执行此条件。此解决方案确保了高效和上下文-Django应用程序中的敏感过滤。
你可以尝试修改代码:

class DatasetFileViewSet(...):
    # ... your existing code code ...

    def datasetfiletagsquery(request):
        # Check if 'tag' parameter is present
        if 'tag' not in request.query_params:
            return Tag.objects.none()  # or your default queryset
        # ... rest of the function ...

    def get_filterset_kwargs(self):
        kwargs = super().get_filterset_kwargs()
        if 'tag' in self.request.query_params:
            # Adjust the 'dataset_id' filter to be required
            # This may involve custom logic to modify the filter attributes
        return kwargs

    # ... rest of the viewset ...

字符串

gg58donl

gg58donl3#

from django_filters import rest_framework as filters
from rest_framework import mixins, serializers, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from yourapp.models import DatasetFile, Tag  # Replace WITH YOUR APP

class DatasetFileViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    class DatasetFileFilter(filters.FilterSet):
        def datasetfiletagsquery(self, queryset, name, value):
            dataset_id = self.request.query_params.get('dataset_id')
            if dataset_id:
                content_type = ContentType.objects.get_for_model(Dataset)
                return Tag.objects.filter(object_id=dataset_id, content_type=content_type,
                                          category=Tag.CategoryChoices.DATASET_FILE)
            else:
                raise serializers.ValidationError({'dataset_id': 'This query is required'})

        def datasetfiletagsfilter(self, queryset, name, value):
            tag_ids = [tag.pk for tag in value]
            return queryset.filter_by_tags(tag_ids)

        dataset_id = filters.NumberFilter(method='filter_dataset_id')
        tag = filters.ModelMultipleChoiceFilter(queryset=Tag.objects.all(), to_field_name='name', method='datasetfiletagsfilter')

        class Meta:
            model = DatasetFile
            fields = ['dataset_id', 'tag']

        def filter_dataset_id(self, queryset, name, value):
            if 'tag' in self.request.query_params:
                return queryset.filter(dataset_id=value)
            return queryset

    queryset = DatasetFile.objects.prefetch_related('tags', 'related_child_dataset_files__child').select_related('datasetbatchfile').extend_order_by()
    serializer_class = DatasetFileSerializer  # Replace with your actual serializer
    filterset_class = DatasetFileFilter

    def get_queryset(self):
        if self.action == 'download':
            return DatasetFile.objects.all()
        else:
            qs = super().get_queryset()
        qs = qs.filter(dataset__organization_id=self.request.user.organization_id)
        return qs

    @action(methods=['POST'], detail=True, url_path='download', serializer_class=DatasetFileDownloadSerializer)  # Replace with your actual serializer
    def download(self, request, pk):
        instance = self.get_object()
        instance = instance.dataset.get_datasetfiles_for_download(datasetfile_ids=[instance.pk])[0]
        serializer = self.get_serializer(instance)
        return Response(serializer.data, status=status.HTTP_200_OK)

字符串
您需要将占位符(如yourapp.modelsDatasetFileSerializer)替换为项目中使用的实际模块和类名。extend_order_by()方法和其他自定义方法或属性也应该根据应用程序的逻辑定义或替换。

相关问题