Django DRF 排序过滤查询filter and order

前言

某些需求,前端可能需要后端数据的过滤,排序条件来查询数据,但是自己写这些接口又很是费劲,这不,今天推荐一个第三方包 django_filters   GitHub - carltongibson/django-filter: A generic system for filtering Django QuerySets based on user selections

 官方文档:django-filter — django-filter 22.1 documentation

文档说的很详细,本文就不在一一说明了,仅仅演示一下平常使用的过滤

模型定义

先看一下模型定义,该模型定义了一个文件存储相关信息

class FileInfo(models.Model):
    # 文件下载连接实效4个小时,通过缓存进行存储
    owner_id = models.ForeignKey(to=User, on_delete=models.CASCADE, verbose_name="所属用户ID")
    aliyun_drive_id = models.ForeignKey(to=AliyunDrive, on_delete=models.CASCADE, verbose_name="所属阿里云盘ID")

    name = models.CharField(max_length=256, verbose_name="文件名字")
    file_id = models.CharField(max_length=64, verbose_name="文件id")
    drive_id = models.CharField(max_length=64, verbose_name="drive_id")
    created_at = models.DateTimeField(verbose_name="上传时间", auto_now_add=True)
    size = models.BigIntegerField(verbose_name="文件大小")
    content_type = models.CharField(max_length=64, verbose_name="文件类型")
    content_hash = models.CharField(max_length=64, verbose_name="content_hash")
    crc64_hash = models.CharField(max_length=64, verbose_name="crc64_hash")

    downloads = models.BigIntegerField(verbose_name="下载次数", default=0)
    description = models.CharField(max_length=256, verbose_name="备注信息", blank=True)

    class Meta:
        verbose_name = '文件信息'
        verbose_name_plural = "文件信息"

    def __str__(self):
        return f"所属用户:{self.owner_id}-文件名:{self.name}-下载次数:{self.downloads}-文件大小:{self.size}"

需求如下

  1. 根据备注进行模糊搜索
  2.  根据文件名进行模糊搜索
  3. 根据文件大小排序
  4. 根据上传时间排序
  5. 根据下载次数排序

实现

定义一个filterset类,继承filter

from django_filters import rest_framework as filters
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from rest_framework.filters import OrderingFilter
from rest_framework.filters import BaseFilterBackend
from rest_framework.pagination import PageNumberPagination



class PageNumber(PageNumberPagination):
    page_size = 20  # 每页显示多少条
    page_size_query_param = 'size'  # URL中每页显示条数的参数
    page_query_param = 'page'  # URL中页码的参数
    max_page_size = 100  # 最大页码数限制


class OwnerUserFilter(BaseFilterBackend):

    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner_id=request.user)


class FileInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.FileInfo
        exclude = ["owner_id", "aliyun_drive_id"]
        read_only_fields = list(
            set([x.name for x in models.FileInfo._meta.fields]) - {"description"})


class FileInfoFilter(filters.FilterSet):
    description = filters.CharFilter(field_name='description', lookup_expr='icontains')
    name = filters.CharFilter(field_name='name', lookup_expr='icontains')

    class Meta:
        model = FileInfo
        fields = ['name']



class FileInfoView(ModelViewSet):
    queryset = FileInfo.objects.all()
    serializer_class = FileInfoSerializer
    pagination_class = PageNumber

    filter_backends = [OwnerUserFilter, filters.DjangoFilterBackend, OrderingFilter]
    ordering_fields = ['size', 'created_at', 'downloads']
    filterset_class = FileInfoFilter


解释一下定义的方法

1.定义一个专属用户过滤,只过滤当前用户数据

from rest_framework.filters import BaseFilterBackend

class OwnerUserFilter(BaseFilterBackend):

    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner_id=request.user)

2.定义用户名和备注过滤,通过lookup_expr定义过滤方式

class FileInfoFilter(filters.FilterSet):
    description = filters.CharFilter(field_name='description', lookup_expr='icontains')
    name = filters.CharFilter(field_name='name', lookup_expr='icontains')

    class Meta:
        model = FileInfo
        fields = ['name']

3.定义排序,排序的话,直接用drf自带的排序就行,直接写

ordering_fields = ['size', 'created_at', 'downloads']

4.分页的话,通过自带的 pagination 实现

from rest_framework.pagination import PageNumberPagination


class PageNumber(PageNumberPagination):
    page_size = 20  # 每页显示多少条
    page_size_query_param = 'size'  # URL中每页显示条数的参数
    page_query_param = 'page'  # URL中页码的参数
    max_page_size = 100  # 最大页码数限制

这样,一些简单的过滤查询就实现了

关于时间查询,比如要查询时间是否大于当前时间,获取取时间段,还可以通过自定义查询方式

class ShareCodeFilter(filters.FilterSet):
    description = filters.CharFilter(field_name='description', lookup_expr='icontains')
    expired = filters.BooleanFilter(field_name='expired_time', method='filter_expired')

    def filter_expired(self, queryset, name, value):
        default_timezone = timezone.get_default_timezone()
        now_time = timezone.make_aware(datetime.datetime.now(), default_timezone)
        if value:
            key = 'lt'
        else:
            key = 'gt'
        lookup = '__'.join([name, key])
        print({lookup: now_time})
        return queryset.filter(**{lookup: now_time})

    class Meta:
        model = ShareCode
        fields = ['short', 'expired', 'description']

上面就展示了要查询数据是否过期,查询已经完成,具体展示就需要前端来配合实现。

相关源码可以参考 https://github.com/nineaiyu/xshare

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值