欢迎来到赛兔子家园

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

posted on 2022-09-15 15:00  赛兔子  阅读(421)  评论(0编辑  收藏  举报

导航