django-filter使用文档

Django-Filter 使用笔记


前言:Django-Filter 是一个 Django 应用,用于过滤 QuerySets。它的目的是简化过滤 QuerySets 的过程,提供一个简单的方法来定义过滤器。

它的功能类似于 Django 的 ORM 查询,但是更简单。

官网地址: https://django-filter.readthedocs.io/en/stable/

要求: 支持的 Python 版本以及最新版本的 Django REST Framework(DRF)进行了测试。


1. 安装

shell 复制代码
    pip install django-filter

然后将django-filter添加到 INSTALLED_APPS

python 复制代码
    INSTALLED_APPS = [
        ...
        'django_filters',
    ]

2. 入门使用

2.1 模型

python 复制代码
"""
产品库
"""

from django.db import models

class ProductModel(models.Model):
    name = models.CharField(max_length=100, blank=True, verbose_name="产品名称")
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格")
    description = models.TextField(blank=True, verbose_name="描述")
    sn = models.CharField(max_length=100, blank=True, verbose_name="产品编号")
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.PROTECT, verbose_name="生产厂家")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    class Meta:
        verbose_name = "产品"
        verbose_name_plural = "产品"
        db_table = "product"

2.2 过滤器

在产品表中,需要根据产品名称、价格、产品编号、生产厂家等字段进行过滤。

python 复制代码
import django_filters

class ProductFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_expr='icontains')
    price = django_filters.NumberFilter()
    sn = django_filters.CharFilter(lookup_expr='icontains')
    manufacturer = django_filters.CharFilter(lookup_expr='icontains')

    class Meta:
        model = ProductModel
        fields = ['name', 'price', 'sn', 'manufacturer']

lookup_expr 是过滤器的查询表达式,icontains 表示忽略大小写的包含查询。

2.3 声明式过滤器

声明式语法在创建过滤器时为您提供了最大的灵活性,但它相当冗长。如果你只想简单地过滤字段,

那么您可以使用 FilterSet 类的 Meta.fields 属性来简化过滤器的创建。

python 复制代码
class ProductFilter(FilterSet):  
    # 价格范围过滤  
    price_gt = NumberFilter(field_name='price', lookup_expr='gt', label='价格大于')  
    price_lt = NumberFilter(field_name='price', lookup_expr='lt', label='价格小于')  
      
    # 制造商名称模糊搜索  
    manufacturer_name = CharFilter(field_name='manufacturer__name', lookup_expr='icontains', label='制造商名称包含')  
  
    class Meta:  
        model = Product  
        fields = ['price_gt', 'price_lt', 'manufacturer_name', 'sn']  # 显式列出所有你想在表单中显示的字段 
  • /api/product/?manufacturer_name=xxxx 将会查询所有制造商名称中包含 "xxxx"(不区分大小写,因为你使用了 icontains 查找表达式)的
    Product 实例。
  • /api/product/?price_gt=10 将会查询所有价格大于 10 的 Product 实例。
  • /api/product/?manufacturer_name=xxxx&price_gt=10 将会查询所有制造商名称中包含 "xxxx" 并且价格大于 10 的 Product 实例。

2.4 Meta.fields 生成过滤器

FilterSet Meta 类提供了一个fields属性,可用于轻松指定多个过滤器,而无需大量代码重复。基本语法支持多个字段名称的列表:

python 复制代码
import django_filters

class ProductFilter(django_filters.FilterSet):
    class Meta:
        model = Product
        fields = ['price', 'sn']

根据上面的代码,django-filter 将自动创建两个过滤器,一个用于价格,一个用于发布日期。这两个过滤器将使用默认的查询表达式(exact)。
exact 是精确查找,即只有字段值与查询值完全相等时才返回结果。

为每个字段指定多个查询表达式

python 复制代码
class ProductFilter(django_filters.FilterSet):  
    # 你可以在这里添加自定义的过滤方法或字段  
    # 但对于基本的查找,我们只需要在 Meta 类中配置  
  
    class Meta:  
        model = Product  
        fields = {  
            'price': ['lt', 'gt'],  # 允许小于和大于的价格过滤  
            'created_at': ['gt'],   # 允许大于特定创建时间的过滤  
            # 如果你还想要基于 sn 字段进行精确匹配,可以这样  
            'sn': ['exact'],  
        }  
  
        # 如果你想要为过滤器设置更友好的标签或帮助文本,可以这样做  
        # labels = {  
        #     'price': _('价格'),  
        #     'created_at': _('创建时间'),  
        #     'sn': _('编号'),  
        # }
  • /api/product/?price_gt=10&created_at_gt=2023-01-01 将会查询价格大于 10 并且创建时间大于 2023-01-01 的 Product 实例。

3.与DRF集成

3.1 配置Django全局过滤器

python 复制代码
# settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'django_filters',
]

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        # ...
    ),
}

3.2 在视图中使用自定义过滤器类 filterset_class

python 复制代码
from rest_framework import generics
from django_filters import rest_framework as filters
from myapp import Product


class ProductFilter(filters.FilterSet):
    min_price = filters.NumberFilter(field_name="price", lookup_expr='gte')
    max_price = filters.NumberFilter(field_name="price", lookup_expr='lte')

    class Meta:
        model = Product
        fields = ['category', 'in_stock']


class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    # filter_backends = (filters.DjangoFilterBackend,)  # 如果配置全局了,此处不需要再配置,如果没有配置3.1中的全局,需要手动在视图接口中写入此行代码
    filterset_class = ProductFilter  # 指定此视图的过滤类为 ProductFilter

上述 ProductList 接口中就可以使用 min_pricemax_price 进行价格区间过滤。

  • /api/product/?min_price=10&max_price=100 将会查询价格在 10 到 100 之间的 Product 实例。

3.3 在视图中使用自定义过滤器字段 filterset_fields

python 复制代码
from rest_framework import generics
from django_filters import rest_framework as filters
from myapp import Product


class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_fields = ('category', 'in_stock')
    # 也可以写为
    """
    filterset_fields = {
        'category': ['exact'],
        'in_stock': ['exact'],
    }
    或者
    filterset_fields = "__all__"  # 全部字段精确查找
    """
    

4. 注意事项

在 Django REST framework 结合 django-filter 的使用中,filterset_classfilterset_fields 通常不会同时在一个视图类中直接使用,因为它们提供了不同的方式来定义过滤逻辑。然而,理解它们之间的关系和用法是很重要的。

filterset_class

filterset_class 属性允许你指定一个 FilterSet 类,这个类定义了过滤字段、方法以及可能的自定义过滤逻辑。当你需要更复杂的过滤逻辑,比如跨字段的过滤、排除特定值的过滤等,你应该使用 filterset_class

filterset_fields

filterset_fields 是一个简单的快捷方式,用于快速指定应该被过滤的模型字段。当你只需要对模型的几个字段进行基本的过滤(如精确匹配、模糊匹配等),并且不需要复杂的过滤逻辑时,filterset_fields 是一个很方便的选择。

不能同时使用?

从技术上讲,filterset_classfilterset_fields 可以在同一个视图类中设置,但通常不推荐这样做,因为这样做可能会导致意料之外的行为或冲突。filterset_class 提供了更高的灵活性和控制力,而 filterset_fields 则是为了简化常见的用例。

如果你同时设置了 filterset_classfilterset_fieldsdjango-filter 可能会优先使用 filterset_class(这取决于 django-filter 的具体实现和版本),因为 filterset_class 提供了更具体的过滤配置。然而,由于这种不明确的行为,最好避免同时使用它们。

最佳实践

  • 如果你的过滤需求很简单,只需要对几个字段进行基本的过滤,那么使用 filterset_fields 就足够了。
  • 如果你的过滤需求更复杂,比如需要跨字段的过滤、自定义过滤方法或逻辑,那么你应该定义一个 FilterSet 类,并在视图中使用 filterset_class 来指定它。

示例

使用 filterset_class 的示例:

python 复制代码
from rest_framework import generics
from django_filters import FilterSet, CharFilter
from myapp.models import Product
from myapp.serializers import ProductSerializer

class ProductFilter(FilterSet):
    category_name = CharFilter(field_name='category__name', lookup_expr='icontains')

    class Meta:
        model = Product
        fields = ['category', 'in_stock', 'category_name']

class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [filters.DjangoFilterBackend]
    filterset_class = ProductFilter

在这个例子中,我们定义了一个 ProductFilter 类,它继承自 FilterSet 并包含了一个自定义的过滤字段 category_name。然后,我们在 ProductList 视图中使用了 filterset_class 来指定这个过滤集。

相关推荐
了一li42 分钟前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
码农君莫笑1 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
别致的影分身1 小时前
使用C语言连接MySQL
数据库·mysql
京东零售技术3 小时前
“慢”增长时代的企业数据体系建设:超越数据中台
数据库
sdaxue.com3 小时前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
o(╥﹏╥)4 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
阿里嘎多学长4 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_4 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
Sunyanhui14 小时前
牛客网 SQL36查找后排序
数据库·sql·mysql
老王笔记5 小时前
MHA binlog server
数据库·mysql