django-filter的使用

django-filter是一个通用的、可重用的应用程序,它可以减轻视图代码的编写工作量。具体来说,它允许用户根据模型的字段筛选查询集,并显示表单让他们这样做。

安装

pip install django-filter

快速开始

settings.py中添加如下配置:

INSTALLED_APPS = [
    ...
    'django_filters',
]
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        ...
    ),
}

现有如下model:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    description = models.TextField()
    release_date = models.DateField()
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)

现在,我们在ListAPIView中添加filterset_fields属性:

class ProductAPIView(ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filterset_fields = ('category', 'in_stock')

或者这样:

from django_filters import rest_framework as filters

class ProductFilterSet(filters.FilterSet):
    
    class Meta:
        model = Product
        fields = ['category', 'in_stock']
        

class ProductAPIView(ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filterset_class = ProductFilterSet

此时,我们可以通过category或in_stock参数来精确查找product的列表数据。值得注意的是,如果同时存在filterset_classfilterset_fields属性时,将会优先使用filterset_class属性

FilterSet

在上面的例子我们可以看到,我们可以自定义字段来进行精确匹配…但是,这远远满足不了用户的需求,如果用户需要模糊匹配,范围查询等等操作时,那该怎么办呢?此时,我们可以对FilterSet改造,如下:

import django_filters
from django_filters import rest_framework as filters

class ProductFilterSet(filters.FilterSet):
    price = django_filters.NumberFilter()
    price__gt = django_filters.NumberFilter(field_name='price', lookup_expr='gt')                           # price 大于 指定值
    price__lt = django_filters.NumberFilter(field_name='price', lookup_expr='lt')                           # price 小于 指定值
    price__isnull = django_filters.NumberFilter(field_name='price', lookup_expr='isnull')                   # price 为空
    price__notnull = django_filters.NumberFilter(field_name='price', lookup_expr='isnull', exclude=True)    # price 不为空
    release_year = django_filters.NumberFilter(field_name='release_date', lookup_expr='year')               # release_date的年份 等于 指定值
    release_year__gt = django_filters.NumberFilter(field_name='release_date', lookup_expr='year__gt')       # release_date的年份 大于 指定值
    release_year__lt = django_filters.NumberFilter(field_name='release_date', lookup_expr='year__lt')       # release_date的年份 小于 指定值
    
    class Meta:
        model = Product
        fields = ['price', 'release_year', 'category', 'in_stock']

如果使用该FilterSet,那么传入"price", “price__gt”, “price__lt”, “release_year”, “release_year__gt”, “release_year__lt”, “category”, "in_stock"参数都可以进行过滤了。

关于过滤器有两个主要的参数:

  • field_name: 要筛选的模型字段的名称。你可以适用Django中的__语法遍历"关系路径"来过滤相关模型上的字段。例如:manufacturer__name
  • lookup_expr: 筛选时要适用的字段查找。Django的__语法也可以用来支持查找转换。例如:year__gte
  • exclude: 反转逻辑

此外,字典可用于为每个字段指定多个查找表达式:

from django_filters import rest_framework as filters

class ProductFilterSet(filters.FilterSet):
    
    class Meta:
        model = Product
        fields = {
            "price": ["exact", "lt", "gt"],
            "release_date": ["exact", "year__gt", "year__lt"]
        }

上述语句将生成"price", “price__lt”, “price__gt”, “release_date”, “release_date__year__gt”, "release_date__year__lt"过滤器,传入这些参数可以进行过滤操作。

filter_overrides

我们可以在Meta类上使用filter_overrides属性覆盖所有同类models字段的默认过滤器:

class ProductFilter(django_filters.FilterSet):

    class Meta:
        model = Product
        fields = ['name', 'description']
        filter_overrides = {
            models.CharField: {
                'filter_class': django_filters.CharFilter,
                'extra': lambda f: {
                    'lookup_expr': 'icontains',
                },
            }
        }

那么,“name”, "description"字段过滤将由原来的精确匹配,变成了模糊匹配了!

qs

如果你想要根据request对象来筛选queryset,你可以覆盖qs只读属性。例如,可以将博客文章过滤为已发布的文章和已登录用户拥有的文章(假设是作者的文章草稿):

class ArticleFilterSet(filters.FilterSet):
    
    class Meta:
        model = Article
        fields = [...]
    
    @property
    def qs(self):
        parent = super().qs
        author = getattr(self.request, 'user', None)
        return parent.filter(is_published=True) | parent.filter(author=author)

自定义过滤方法

可以通过指定执行筛选的方法来控制筛选器的行为。在方法参考中查看更多信息。请注意,你可以访问筛选器集的属性,例如request

class ProductFilterSet(filters.FilterSet):
    username = django_filters.CharFilter(method='my_custom_filter')
    
    class Meta:
        model = Product
        fields = ['username']
    
    def my_custom_filter(self, queryset, name, value):
        return queryset.filter(**{name: value})

自定义Filter

你可以重写过滤器类的filter()方法,来做一些特殊的事情。如下:

import django_filters
from django.core.validators import EMPTY_VALUES

class MyCharFilter(django_filters.CharFilter):
    
    def filter(self, qs, value):
        if value in EMPTY_VALUES:
            return qs
        if self.distinct:
            qs = qs.distinct()
        lookup = '%s__%s' % (self.field_name, self.lookup_expr)
        qs = self.get_method(qs)(**{lookup: value})
        return qs

重写FilterSet的创建

FilterSet的创建可以通过覆盖后端类上的以下方法进行自定义:

  • .get_filterset(self, request, queryset, view)
  • .get_filterset_class(self, view, queryset=None)
  • .get_filterset_kwargs(self, request, queryset, view)

你可以为每个视图逐个重写这些方法,创建唯一的后端,或者可以使用这些方法编写自己的视图类钩子。

视图属性重命名

几个与视图相关的属性被重命名,以提高与库其他部分的一致性。重命名属性如下:

  • ViewSet.filter_class => filterset_class
  • ViewSet.filter_fields => filterset_fields
  • DjangoFilterBackend.default_filter_set => filterset_base
  • DjangoFilterBackend.get_filter_class => get_filterset_class()
  • FilterMixin.filter_fields => filterset_fields

更多用法

https://django-filter.readthedocs.io/en/main/index.html

你可能感兴趣的:(python,django,python,后端,django-filter)