【问题标题】:How do you use Django-filter's '__in' lookup?你如何使用 Django-filter '__in' 查找?
【发布时间】:2013-08-03 16:52:57
【问题描述】:

我正在使用带有django-filter 包的Django REST 框架,但我的问题主要是关于django-filter。我不明白如何在“__in”查找中使用过滤器。

例如我有以下模型:

class Book(models.Model):
   name = models.CharField(max_length=100)

class BookView(viewsets.ReadOnlyModelViewSet):
   serializer_class = BookSerializer()
   model = Book
   filter_fields = ('id', 'name')

而且我不能使用像 /v1/books/?id__in=1,2,3 这样的 URL 来查找 ID 为 1、2 或 3 的书籍。

你如何使用 Django-filter 的 '__in' 查找?

【问题讨论】:

标签: python django


【解决方案1】:

django_filter 现在有一个简单的解决方案:

class BookView(viewsets.ReadOnlyModelViewSet):
   serializer_class = BookSerializer()
   model = Book
   filter_fields = {
      'id': ['exact', 'in'],
      'name': ['exact']
   }

然后您可以在查询字符串中完全按照您的意愿使用它:?id__in=1,2,3

【讨论】:

  • 尴尬的是我花了多长时间才找到这个。谢谢!
【解决方案2】:

问题在本期讨论:https://github.com/alex/django-filter/issues/137#issuecomment-77697870

建议的解决方案是创建一个自定义过滤器,如下所示:

from django_filters import Filter
from django_filters.fields import Lookup

from .models import Product

class ListFilter(Filter):
    def filter(self, qs, value):
        value_list = value.split(u',')
        return super(ListFilter, self).filter(qs, Lookup(value_list, 'in'))

class ProductFilterSet(django_filters.FilterSet):
    id = ListFilter(name='id')

    class Meta:
        model = Product
        fields = ['id']

你可以写如下:

products/?id=7,8,9

【讨论】:

    【解决方案3】:

    django-filter 提供BaseInFilter 与其他过滤器类一起使用,例如NumberFilterCharFilter。此类仅验证传入请求是 comma-separated。因此,如果您使用 Web Browsable API,则可以将请求发送为 /book/?id__in=1%2C3 其中%2C 是逗号。

    filters.py

    import django_filters
    
    
    class NumberInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
        pass
    
    
    class BookFilter(django_filters.FilterSet):
        id__in = NumberInFilter(field_name="id", lookup_expr="in")
    

    views.py

    from rest_framework import viewsets
    from django_filters import rest_framework as filters
    
    from book.filters import BookFilter
    from book.models import Book
    from book.serializers import BookSerializer
    
    
    class BookViewSet(viewsets.ReadOnlyModelViewSet):
        queryset = Book.objects.all()
        filter_backends = (filters.DjangoFilterBackend, )
        filterset_class = BookFilter
        serializer_class = BookSerializer
    

    【讨论】:

      【解决方案4】:

      django-filter 的文档很少。您可以尝试创建自定义过滤器并指定查找类型。比较复杂:

      class BookFilter(django_filters.FilterSet):
          id = django_filters.NumberFilter(name="id", lookup_type="in")
      
          class Meta:
              model = Book
              fields = ['id']
      

      然后修改你的视图以使用过滤器类:

      class BookView(viewsets.ReadOnlyModelViewSet):
          serializer_class = BookSerializer()
          model = Book
          filter_fields = ('id', 'name')
          filter_class = BookFilter
      

      然后您就可以通过它们的 ID 查找书籍(注意“__in”未使用):

      /v1/books/?id=1,2,3
      /v1/books/?id=1
      

      【讨论】:

        【解决方案5】:

        为您的 id 字段(AutoField)自定义 PKsField 和 PKsFilter,然后查询参数将起作用:'/v1/books/?id__in=1,2,3'

        from django.forms import Field
        from django_filters.filters import Filter
        from django.db.models import AutoField
        
        
        class PKsField(Field):
        
            def clean(self, value): # convert '1,2,3' to {1, 2, 3}
                return set(int(v) for v in value.split(',') if v.isnumeric()) if value else ()
        
        
        class PKsFilter(Filter):
            field_class = PKsField
        
        
        class BookFilter(FilterSet):
            # ids = PKsFilter(name='id', lookup_type="in") # another way, query string: ?ids=1,2,3
        
            filter_overrides = {
                AutoField: {
                    'filter_class': PKsFilter, # override default NumberFilter by the PKsFilter
                    'extra': lambda f: {
                        'lookup_type': 'in',
                    }
                }
            }
        
            class Meta:
                model = Book
                fields = {
                    'id': ('in',),
                }
        
        
        from rest_framework import viewsets
        
        
        class BookView(viewsets.ModelViewSet):
            queryset = ...
            serializer_class = ...
            filter_class = BookFilter

        希望能有所帮助。谢谢。

        【讨论】:

          【解决方案6】:

          你可以使用/v1/books/?id=1&id=2&id=3代替/v1/books/?id__in=1,2,3

          【讨论】:

            【解决方案7】:

            不确定是否有人回答过: 尝试: id=[1, 2, 3] 表示数字 name=["name1", "name2"] 用于字符串

            【讨论】:

              【解决方案8】:

              我刚刚在DjangoFilterBackend with multiple ids回答了同样的问题

              对于您的情况,这应该无需编写任何逻辑即可工作。

              from django_filters import rest_framework as filters
              
              class NumberInFilter(filters.BaseInFilter, filters.NumberFilter):
                  pass
              
              
              class BookFilter(filters.FilterSet):
                  id_in = NumberInFilter(field_name='id', lookup_expr='in')
              
                  class Meta:
                      model = Book
                      fields = ['id_in', 'name']
              
              class BookView(viewsets.ReadOnlyModelViewSet):
                 serializer_class = BookSerializer()
                 model = Book
                 filter_class = BookFilter
              

              现在您应该可以通过获取参数中的 id 列表进行过滤,例如 /v1/books/?id__in=1,2,3

              【讨论】:

                【解决方案9】:

                django 管理站点仅在模板 app_name/model_name/primary_key 下创建 url 来编辑模型的实例。它不提供通过 URL 过滤的__in

                你必须创建一个自定义视图:

                def myview(request):
                    # you can get parameters from request.GET or request.POST
                    selected_books = None
                    if request.method = "POST":
                        ids = request.POST["ids"].split("_")
                        selected_books = Book.objects.filter(id__in=ids)
                
                    return render_to_response('mytemplate.html', { 'selected_books': selected_books }, context_instance = RequestContext(request) )
                

                在 mytemplate.html 中:

                {% for entry in selected_books %}
                  ... {{ entry }} ...
                {% endfor %}
                

                在 urls.py 中添加一个条目到这个视图。

                然后尝试使用参数?ids=1_2_3 对您的 URL 发出 GET 请求

                【讨论】:

                • 谢谢,但我的问题是关于 django-filter 应用程序。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-03-29
                • 2020-06-09
                • 2015-09-10
                • 2011-11-13
                • 2016-01-16
                • 2019-02-06
                相关资源
                最近更新 更多