drf - 筛选组件DjangoFilterBackend

时间:2019-09-15
本文章向大家介绍drf - 筛选组件DjangoFilterBackend,主要包括drf - 筛选组件DjangoFilterBackend使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

数据准备

model.py文件

定义两个表Car表和Brand表,其中Car中的brand字段外键关联Brand表

from django.db import models
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
        abstract = True


class Car(BaseModel):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    brand = models.ForeignKey('Brand', db_constraint=False, on_delete=models.DO_NOTHING, related_name='cars')
    @property
    def brand_name(self):
        return self.brand.name
    class Meta:
        db_table = 'old_boy_car'
        verbose_name = '汽车'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name
    
class Brand(BaseModel):
    name = models.CharField(max_length=32)
    class Meta:
        db_table = 'old_boy_brand'
        verbose_name = '品牌'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name

新建的serializer.py文件

brand字段只参与反序列化,brand_name只参与序列化

from rest_framework.serializers import ModelSerializer
from . import models
class CarModelSerializer(ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name','price','brand','brand_name')
        extra_kwargs = {
            "brand":{
                'write_only':True
            },
            'brand_name':{
                'read_only':True
            },
        }

分类筛选 filter_fields

views.py文件

from rest_framework.viewsets import ModelViewSet
from . import models, serializer
from django_filters.rest_framework import DjangoFilterBackend

class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializer.CarModelSerializer
    
    filter_backends = [DjangoFilterBackend]
    # 分类: 一般都是可以分组的字段
    filter_fields = ['brand']  

# 按品牌brand分类,url链接:/car/?brand=1

区间筛选 filter_class

新建的 filterset.py

max_price与min_price是用于参与区间分类,不写也可以仅做分类同 filter_fields效果一样

from django_filters import FilterSet,filters
from . import models
class CarFilterSet(FilterSet):
    max_price = filters.NumberFilter(field_name='price',lookup_expr='lte')
    min_price = filters.NumberFilter(field_name='price',lookup_expr='gte')
    class Meta:
        model = models.Car
        # brand 还是实现分类
        fields = ['brand','max_price','min_price']

views.py文件

from rest_framework.viewsets import ModelViewSet
from . import models, serializer
from django_filters.rest_framework import DjangoFilterBackend
from .filterset import CarFilterSet

class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializer.CarModelSerializer
    
    filter_backends = [DjangoFilterBackend]
    filter_class = CarFilterSet
# url链接:/car/?max_price=100  价格不超过100
# url链接:/car/?min_price=10  价格不低于10
# url链接:/car/?brand=1&min_price=10&max_price=100  品牌brand为1且价格在[10,100]内的汽车

DjangoFilterBackend部分源码解析

1. 在DjangoFilterBackend组件中先调用filter_queryset方法中

def filter_queryset(self, request, queryset, view):
    filterset = self.get_filterset(request, queryset, view)  # 获取筛选条件
    # 如果filterset为None,表示没有筛选条件
    if filterset is None:
        return queryset
    
    # 无效的筛选条件处理
    if not filterset.is_valid() and self.raise_exception:
        raise utils.translate_validation(filterset.errors)
    return filterset.qs

2. 调用get_filterset方法获取filterset

def get_filterset(self, request, queryset, view):
    filterset_class = self.get_filterset_class(view, queryset)  # 获取筛选类
    if filterset_class is None:
        return None

    kwargs = self.get_filterset_kwargs(request, queryset, view)
    return filterset_class(**kwargs)

3.调用get_filterset_class方法获取filterset_class

def get_filterset_class(self, view, queryset=None):
    """
    Return the `FilterSet` class used to filter the queryset.
    """
    filterset_class = getattr(view, 'filterset_class', None)  
    filterset_fields = getattr(view, 'filterset_fields', None)
    
    # 将filter_class映射给filterset_class
    if filterset_class is None and hasattr(view, 'filter_class'):
        utils.deprecate(
            "`%s.filter_class` attribute should be renamed `filterset_class`."
            % view.__class__.__name__)
        filterset_class = getattr(view, 'filter_class', None)

    # 将filter_fields映射给filterset_fields
    if filterset_fields is None and hasattr(view, 'filter_fields'):
        utils.deprecate(
            "`%s.filter_fields` attribute should be renamed `filterset_fields`."
            % view.__class__.__name__)
        filterset_fields = getattr(view, 'filter_fields', None)
    
    # 有filterset_class时执行,filterset_class定义的是一个类
    if filterset_class:
        filterset_model = filterset_class._meta.model 

        # FilterSets do not need to specify a Meta class
        if filterset_model and queryset is not None:
            assert issubclass(queryset.model, filterset_model), \
                'FilterSet model %s does not match queryset model %s' % \
                (filterset_model, queryset.model)

        return filterset_class
    
    # 有filterset_fields时执行
    if filterset_fields and queryset is not None:
        MetaBase = getattr(self.filterset_base, 'Meta', object)

        class AutoFilterSet(self.filterset_base):
            class Meta(MetaBase):
                model = queryset.model
                fields = filterset_fields
                
        return AutoFilterSet

    return None

原文地址:https://www.cnblogs.com/863652104kai/p/11522191.html