Django REST framework分页、过滤、搜索、排序
分页(Pagination)
前提条件:使用了通用视图或视图集时,才会有自动分页。
分页:全局和局部分页。
全局分页
settings.py 文件中配置
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 10 # 配置每页显示多少条数据 }
局部分页
settings.py 中只需要配置PAGE_SIZE
REST_FRAMEWORK = { 'PAGE_SIZE': 10 # 每页数目,如果不设置,则每页进行分配 }
views.py
from rest_framework.pagination import PageNumberPagination from rest_framework.generics import mixins from rest_framework.viewsets import GenericViewSet from .models import Goods from .serializers import GoodsSerializer class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): queryset = Goods.objects.all() serializer_class = GoodsSerializer # 局部配置分页功能 pagination_class =PageNumberPagination
自定义分页类
通过使用pagination_class属性在单个视图上设置分页类。
views.py
from rest_framework.pagination import PageNumberPagination from rest_framework.generics import mixins from rest_framework.viewsets import GenericViewSet from .models import Goods from .serializers import GoodsSerializer # 自定义分页类 class GoodsPagination(PageNumberPagination): page_size = 10 # 每一页的数据量 page_query_param = "page" # 查询字符串中代表页码的变量名 page_size_query_param = 'page_size' #查询字符串中代表每一页数据的变量名 max_page_size = 100 #允许最大请求页面大小数值 class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsPagination # 自定义分页类
django-filter过滤器
安装:pip install django-filter
全局和局部过滤器,两个过滤器有冲突,只能配置其中一个。
添加django_filters到settings.py中INSTALLED_APPS
INSTALLED_APPS = [ ... 'django_filters', # 过滤器 ... ]
全局过滤器
settings.py
REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] }
views.py
例如:根据输入价格过滤出该价格的商品
class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): queryset = Goods.objects.all() serializer_class = GoodsSerializer filterset_fields = ['shop_price'] # 视图中通过类属性filter_fields=[]来指定过滤器字段,例如:商品价格
请求ulr
http://127.0.0.1:8087/goods/list/?shop_price=商品价格
直接在视图类中为filterset_fields指定字段方式,只适用于相等条件的过滤,对于大于、小于等复杂的过滤条件需要自定义过滤器类实现
局部过滤器
前提条件:将全局过滤器配置注释。
views.py
# 局部过滤器 from django_filters.rest_framework import DjangoFilterBackend class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): queryset = Goods.objects.all() serializer_class = GoodsSerializer # 局部过滤器 filter_backends = [DjangoFilterBackend] filterset_fields = ['shop_price'] # 视图中通过类属性filter_fields=[]来指定过滤器字段,例如:商品价格
自定义过滤条件
当过滤复杂条件时,使用自定义过滤器。
需求1:>=商品最低价格和<=商品最高价格来筛选商品
实现方式:通过自定义过滤器类来完成多种筛选条件
创建filters.py(与views同级目录)在里面定义过滤条件
filters.py
import django_filters from .models import Goods class GoodsFilter(django_filters.rest_framework.FilterSet): """ 商品的过滤类 """ # 配置过滤条件,models中Goods类中的shop_price(价格)字段 price_min = django_filters.NumberFilter(field_name='shop_price', help_text="最低价格", lookup_expr='gte')# 大于等于 price_max = django_filters.NumberFilter(field_name='shop_price', help_text="最高价格", lookup_expr='lte')# 小于等于 class Meta: model = Goods fields = ['price_min', 'price_max']
views.py
from rest_framework.generics import mixins from rest_framework.viewsets import GenericViewSet from rest_framework.pagination import PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend # 局部过滤 from .models import Goods from .serializers import GoodsSerializer from .filters import GoodsFilter class GoodsPagination(PageNumberPagination): page_size = 10 # 每页显示多少条 page_query_param = "page" # 分页查询参数名称 page_size_query_param = 'page_size' max_page_size = 100 #允许最大请求页面大小数值 class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsPagination filter_backends = [DjangoFilterBackend,] filter_class = GoodsFilter # 自定义过滤类
需求2:过滤查找第一类别下所有商品
filters.py
import django_filters from django.db.models import Q from .models import Goods class GoodsFilter(django_filters.rest_framework.FilterSet): """ 商品的过滤类 """ top_category = django_filters.NumberFilter(method='top_category_filter', help_text="第一类别下面所有商品") # 查找类别下面所有的商品,value是前端url中传递过来的类别ID def top_category_filter(self, queryset, name, value): # 自定义过滤方法,里面是自定义过滤条件 """ :param queryset: 默认参数,models.py中Goods的对象queryset :param name: 默认参数 :param value: 默认参数,url传递过来的商品类别ID :return: """ return queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q( category__parent_category__parent_category_id=value)) class Meta: model = Goods fields = ['top_category']
搜索
需求:商品名、商品描述、商品内容字段为搜索条件
views.py
from rest_framework import filters # 搜索 from rest_framework.viewsets import GenericViewSet from rest_framework.generics import mixins class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): ... filter_backends = [filters.SearchFilter] # 搜索 search_fields = ["name", "goods_brief", "goods_desc"] # 搜索字段配置 ...
排序Ordering
对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。
使用方法:
分为:全局排序和局部排序
全局配置:settings.py中配置,排序字段为模型中所有字段;
局部排序:类视图中设置filter_backends类属性。
使用rest_framework.filters.OrderingFilter过滤器, REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。
全局排序配置
settings.py
REST_FRAMEWORK = { # 全局排序 & 过滤 'DEFAULT_FILTER_BACKENDS': [ 'rest_framework.filters.OrderingFilter', # 排序 ], }
全局排序参数可选字段为model中所有字段。
全局排序时已指定字段为参数
需求:全局配置,已商品销量、添加时间降序或升序
views.py
class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): ... ordering_fields = ["sold_num", "add_time"] # 排序字段配(商品销售量,添加时间 ...
局部排序
前提:将全局排序配置注释
views.py
from rest_framework.generics import mixins from rest_framework.viewsets import GenericViewSet from rest_framework import filters from .models import Goods from .serializers import GoodsSerializer from .filters import GoodsFilter class GoodsListViewSet(GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin): ... filter_backends = [filters.OrderingFilter] # 局部排序 ordering_fields = ["sold_num", "add_time"] # 排序字段配(商品销售量,添加时间
...
全部代码
分页、过滤、搜索、排序
views.py
from rest_framework.generics import mixins from rest_framework.viewsets import GenericViewSet from rest_framework.pagination import PageNumberPagination from rest_framework import filters from rest_framework.response import Response from django_filters.rest_framework import DjangoFilterBackend # 过滤 from .models import Goods from .serializers import GoodsSerializer from .filters import GoodsFilter class GoodsPagination(PageNumberPagination): page_size = 10 # 每页显示多少条 page_query_param = "page" # 分页查询参数名称 page_size_query_param = 'page_size' max_page_size = 100 # 允许最大请求页面大小数值 class GoodsListViewSet(CacheResponseMixin, GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin, ): """ 商品列表页,分页,搜索,过滤,排序 """ queryset = Goods.objects.all() serializer_class = GoodsSerializer pagination_class = GoodsPagination # 自定义配置筛选、搜索、排序 filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter] filter_class = GoodsFilter # 自定义筛选 search_fields = ["name", "goods_brief", "goods_desc"] # 搜索字段配置 ordering_fields = ["sold_num", "add_time"] # 排序字段配置 # 商品点击数+1 def retrieve(self, request, *args, **kwargs): instance = self.get_object() instance.click_num += 1 instance.save() serializer = self.get_serializer(instance) return Response(serializer.data)
自定义过滤器filter.py
import django_filters from django.db.models import Q from .models import Goods class GoodsFilter(django_filters.rest_framework.FilterSet): """ 商品的过滤类 """ # 配置过滤条件,models中Goods类中的shop_price(价格)字段 price_min = django_filters.NumberFilter(field_name='shop_price', help_text="最低价格", lookup_expr='gte') # 大于等于 price_max = django_filters.NumberFilter(field_name='shop_price', help_text="最高价格", lookup_expr='lte') # 小于等于 top_category = django_filters.NumberFilter(method='top_category_filter', help_text="第一类别下面所有商品") # 查找类别下面所有的商品,value是前端url中传递过来的类别ID def top_category_filter(self, queryset, name, value): # 自定义过滤方法,里面是自定义过滤条件 """ :param queryset: 默认参数,models.py中Goods的对象queryset :param name: 默认参数 :param value: 默认参数,url传递过来的商品类别ID :return: """ return queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q( category__parent_category__parent_category_id=value)) class Meta: model = Goods fields = ['price_min', 'price_max','top_category']
serializers.py
from rest_framework import serializers from . import models class CategorySerializer(serializers.ModelSerializer): class Meta: model = models.GoodsCategory fields = "__all__" class GoodsImageSerializer(serializers.ModelSerializer): class Meta: model = models.GoodsImage fields = ["image",] class GoodsSerializer(serializers.ModelSerializer): category = CategorySerializer() # Goods中的category外键重写,category外键字段显示GoodsCategory中的信息 images = GoodsImageSerializer(many=True) # many=True表示有多个 class Meta: model = models.Goods fields = "__all__"
models.py
from django.db import models from datetime import datetime from django.db import models # Create your models here. # 富文本编辑器 # from ckeditor_uploader.fields import RichTextUploadingField class GoodsCategory(models.Model): """ 商品类别 """ atomic = False CATEGORY_TYPE = ( (1, "一级类目"), (2, "二级类目"), (3, "三级类目") ) name = models.CharField(default="", max_length=30, verbose_name="类别名", help_text="类别名") code = models.CharField(default="", max_length=30, verbose_name="类别code", help_text="类别code") desc = models.TextField(default="", verbose_name="类别描述", help_text="类别描述", ) category_type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name="类目级别", help_text="类目级别") parent_category = models.ForeignKey("self", null=True, blank=True, verbose_name="父类目级别", help_text="父目录", related_name="sub_cat", on_delete=models.CASCADE) is_tab = models.BooleanField(default=False, verbose_name="是否导航", help_text="是否导航") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta: verbose_name = "商品类别" verbose_name_plural = verbose_name def __str__(self): return self.name class GoodsCategoryBrand(models.Model): """ 品牌名 """ category = models.ForeignKey(GoodsCategory, related_name="brands", null=True, blank=True, verbose_name="商品类目", on_delete=models.CASCADE) name = models.CharField(default="", max_length=30, verbose_name="品牌名", help_text="品牌名") desc = models.TextField(default="", max_length=200, verbose_name="品牌描述", help_text="品牌描述") image = models.ImageField(upload_to="brands/", max_length=200) add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta: verbose_name = "品牌" verbose_name_plural = verbose_name def __str__(self): return self.name class Goods(models.Model): """ 商品 """ category = models.ForeignKey(GoodsCategory, verbose_name="商品类目", on_delete=models.CASCADE) goods_sn = models.CharField(max_length=50, default="", verbose_name="商品唯一货号") name = models.CharField(max_length=100, verbose_name="商品名") click_num = models.IntegerField(default=0, verbose_name="点击数") sold_num = models.IntegerField(default=0, verbose_name="商品销售量") fav_num = models.IntegerField(default=0, verbose_name="收藏数") goods_num = models.IntegerField(default=0, verbose_name="库存数") market_price = models.FloatField(default=0, verbose_name="市场价格") shop_price = models.FloatField(default=0, verbose_name="本店价格") goods_brief = models.TextField(max_length=500, verbose_name="描述") # goods_desc = UEditorField(verbose_name="内容", imagePath="goods/images/", width=1000, height=300, # filePath="goods/files/", default='') goods_desc = models.TextField(verbose_name="内容") ship_free = models.BooleanField(default=True, verbose_name="是否承担运费") goods_front_image = models.ImageField(upload_to="goods/images/", null=True, blank=True, verbose_name="封面图") is_new = models.BooleanField(default=False, verbose_name="是否新品") is_hot = models.BooleanField(default=False, verbose_name="是否热销") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta: verbose_name = '商品' verbose_name_plural = verbose_name def __str__(self): return self.name class IndexAd(models.Model): category = models.ForeignKey(GoodsCategory, related_name='category', verbose_name="商品分类", on_delete=models.CASCADE) goods = models.ForeignKey(Goods, related_name='goods', on_delete=models.CASCADE, verbose_name="商品") class Meta: verbose_name = '首页商品类别广告' verbose_name_plural = verbose_name def __str__(self): return self.goods.name class GoodsImage(models.Model): """ 商品轮播图 """ goods = models.ForeignKey(Goods, verbose_name="商品", related_name="images", on_delete=models.CASCADE) image = models.ImageField(upload_to="goods", verbose_name="图片", null=True, blank=True) add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta: verbose_name = '商品图片' verbose_name_plural = verbose_name def __str__(self): return self.goods.name class Banner(models.Model): """ 轮播的商品 """ goods = models.ForeignKey(Goods, verbose_name="商品", on_delete=models.CASCADE) image = models.ImageField(upload_to='banner', verbose_name="轮播图片") index = models.IntegerField(default=0, verbose_name="轮播顺序") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") class Meta: verbose_name = '轮播商品' verbose_name_plural = verbose_name def __str__(self): return self.goods.name class HotSearchWords(models.Model): """ 热搜词 """ keywords = models.CharField(default="", max_length=20, verbose_name="热搜词") index = models.IntegerField(default=0, verbose_name="排序") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间") def __str__(self): return self.keywords class Meta: verbose_name = '热搜词' verbose_name_plural = verbose_name