django_filter Django Rest Framework(DRF)处理query_params get vs getlist

时间:2017-06-07 17:46:52

标签: django-rest-framework django-filter

在django_filter中,按值列表过滤仍然过于复杂,并且,通过扩展,Django Rest Framework(DRF)或者我错过了一些简单的东西,这是否正确?

具体来说,当我们处理通过URL提交的多值参数时,我们必须先使用'getlist'而不是'get'来指定应该处理的参数:

http://example.com?country=US&country=CN

现在,当我们处理此请求时,

请求时,

request.query_params.get('country')以及 request.query_params ['country'] 将错误地返回'CN'。 query_params.getlist('country')将返回['US','CN'],这是我们在此示例中所希望的。

所以我当前的逻辑规定我必须提前指定哪些参数可能指定了多个值,因为我不知道一个透明处理这个参数的内部方法:

所以为了解决这个问题,我现在做了类似的事情(为了简单起见,跳过所有实际的请求处理,过滤等):

class CountryFilter(filters.FilterSet):
     """ placeholder: this is a separate question as to why I can't get any of these to work as desired. """
     #country = filters.CharFilter(method='filter_country')
     #country = filters.CharFilter(name='country', lookup_expr='in')
     #country = filters.MultipleChoiceFilter()

class CountryViewSet(viewsets.ModelViewSet):
    """Viewset to work with countries by ISO code"""

    def list(self, request, *args, **kwargs):
        """do something based on a request for one or more countries
        such as http://example.com?country=CA&country=CN&country=BR
        """
        query_params = request.query_params
        filter0 = CountryFilter(query_params, queryset=Country.objects.all())       

        # if filter0 worked, we'd be done and wouldn't need below
        # but it doesn't

        query_dict = {}
        params_to_list = ['country', 'another_param']
        for k in query_params.keys():
            if k in params_to_list:
                query_dict[k] = query_params.getlist(k)
            else:
                query_dict[k] = query_params.get(k)

        # use the newly counstructed query_dict rather than query_params
        filter1 = CountryFilter(query_dict, queryset=Country.objects.all())

        import pdb; pdb.set_trace()
        return HttpResponse('ok')

理论上,filter1应该给我想要的结果,而filter0则不会。

基于此的代码在我的生产环境中有效,但还有一些其他效果,例如将有效列表转换回字符串。我不想在这里提出这个问题,只是想知道这是否是人们做的事情?

1 个答案:

答案 0 :(得分:0)

  

具体而言,我们必须先指定应使用' getlist'处理的参数。而不是与'得到'当我们处理通过URL提交的多值参数时,如下所示:

这不是必需的。 Filter个实例构造一个表单字段,然后执行查询参数验证。这些字段是标准的Django表单字段,其小部件将决定在从查询字典中提取值时是否使用.getlist().get()。您不需要预处理查询字典。

在您的示例中,filter0和filter1实际上应该具有相同的行为,因为QueryDict('a=1&a=2')在功能上等同于dict(a=['1', '2'])小部件的SelectMultiple。其.value_from_datadict()会在前者上调用.getlist(),在后者调用.get() - 两者都会返回值列表。

关于解决多个值过滤的问题,您有两个选择。

  1. 使用MultipleChoiceFilterdocs
  2. 使用基于CSV的过滤器(docs
  3. 多选过滤器会呈现一个选择多个窗口小部件,并且需要一个像?a=1&a=b这样的查询字符串。您尝试了上述操作,但省略了choices。没有选择,html选择将在没有选项的情况下呈现,验证将随后失败(因为它使用相同的选项来验证输入)。或者,您可以使用ModelMultipleChoiceFilter,这需要queryset来构建其选择。请注意,查询集/选择处理是底层表单字段的内置行为,并不是django-filter特有的。

    对于后一个选项,上面链接的文档中有更多详细信息,但值得一提的是,它会呈现文本输入,并且需要?a=1,2而不是?a=1&a=2这样的查询字符串。