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 来指定这个过滤集。

相关推荐
TDengine (老段)1 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
qq7422349841 小时前
Python操作数据库之pyodbc
开发语言·数据库·python
姚远Oracle ACE2 小时前
Oracle 如何计算 AWR 报告中的 Sessions 数量
数据库·oracle
Dxy12393102162 小时前
MySQL的SUBSTRING函数详解与应用
数据库·mysql
码力引擎2 小时前
【零基础学MySQL】第十二章:DCL详解
数据库·mysql·1024程序员节
杨云龙UP3 小时前
【MySQL迁移】MySQL数据库迁移实战(利用mysqldump从Windows 5.7迁至Linux 8.0)
linux·运维·数据库·mysql·mssql
l1t3 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
安当加密3 小时前
Nacos配置安全治理:把数据库密码从YAML里请出去
数据库·安全
ColderYY3 小时前
Python连接MySQL数据库
数据库·python·mysql
GW_Cheng4 小时前
达梦数据库适配遇到的一些问题
数据库·国产化·达梦数据库