自定义过滤器类中执行不必要的过滤器代码

问题描述 投票:0回答:2

第 1 期

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

第 2 期

  • 如果我根据需要设置
    dataset_id
    ,那么它也会在
    retrieve
    和其他
    action
    api 函数中强制执行。

背景

  • 仅当请求中传递
    dataset_id
    查询时,我才需要将
    tag
    作为所需查询

代码

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 期的预期执行

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

第 2 期的预期执行

  • 过滤器的
    required
    字段必须仅针对
    list
    api 强制执行。
django django-rest-framework django-filter
2个回答
2
投票

执行

datasetfiletagsquery
以获取按标签过滤的所有可能值。它在两种情况下执行:

  1. 如果提供了标签,则会检查它是否是允许的标签(这是您所期望的)。
  2. 如果未提供标签,用户可以使用 OPTIONS http 方法请求以获取过滤器允许的选项。这对于 REST API 在网页下拉列表中显示可能的值也很有用。

因此不可能让它不被执行,DRF 在其代码中强制执行它。这里唯一的可能性是从过滤器字段中删除

queryset= datasetfiletagsquery
属性,并在过滤时手动验证是否允许该标签。

第二个问题可能也无法直接解决,除非您将其设为 DRF 不需要的并在过滤期间手动验证它。这也会导致端点的模式错误(它不会指示列表方法中需要这些字段)。如果这对您来说是个问题,您可以将模式覆盖为您自己手动创建的模式。

此外,stackoverflow 上有一条规则,每个问题只需要解决一个问题。


0
投票

要解决问题 1,请修改 DatasetFileViewSet 中的 datasetfiletagsquery,以在请求中缺少“tag”参数时返回空或默认查询集。对于问题 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 ...

© www.soinside.com 2019 - 2024. All rights reserved.