我在这里使用的方法并非是官方使用的方案,但仍然可以实现,如果想使用官方的方法,请到文末查看官方文档中的使用方式

问题背景

在Web应用当中,尤其是后台管理应用,经常遇到的一个场景是,需要根据用户的输入条件,在数据库中查询相应数据并展示。 那让我们看看在Django中如何处理?
先看看官网的介绍
https://docs.djangoproject.com/en/1.11/topics/db/queries/
官网介绍的很详细, 我就不重复粘贴复制了, 在这里只记录一下一个典型的使用场景.

场景描述

有一个关于书本信息的数据表, 包括书本的书名,价格,出版社,ISBN,作者。Model定义如下:

  1. class Book(models.Model):
  2. name = models.CharField(max_length=48)
  3. isbn = models.IntegerField(primary_key=True, unique=True)
  4. author = models.CharField(max_length=24)
  5. press = models.CharField(max_length=48)
  6. price = models.PositiveIntegerField(default=0)

在书本管理后台,需要实现根据用户输入的作者信息,进行模糊查询。 前端通过ajax POST将表单信息传送给后台。Django在收到请求之后,在view当中调用如下函数就可以进行数据库的查找过滤操作呢。 其中icontains表示忽略大小写的模糊查询。

  1. def filter_books(objects, request):
  2. filter_author = request.POST['author']
  3. if (filter_author):
  4. objects = objects.filter(author__icontains=filter_author)
  5. return objects

Django支持的查询方式有很多, 具体请查看以下官网介绍:
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#field-lookups

一切看起来都不错,有什么不妥?

目前看起来确实没有什么不妥,但是当定义的model多了, 要查询的表单多了之后, 相关的代码片段就变成了下面这样:

  1. def filter_libooks(objects, request):
  2. filter_status = request.POST['status']
  3. filter_uuid = request.POST['uuid']
  4. filter_isbn = request.POST['isbn']
  5. filter_name = request.POST['name']
  6. if (filter_status):
  7. objects = objects.filter(status=filter_status)
  8. if (filter_isbn):
  9. objects = objects.filter(book__isbn__contains=filter_isbn)
  10. if (filter_name):
  11. objects = objects.filter(book__name__contains=filter_name)
  12. if (filter_uuid):
  13. objects = objects.filter(uuid_contains=filter_uuid)
  14. return objects
  15. def filter_books(objects, request):
  16. filter_author = request.POST['author']
  17. filter_press = request.POST['press']
  18. filter_isbn = request.POST['isbn']
  19. filter_name = request.POST['name']
  20. if (filter_author):
  21. objects = objects.filter(author__contains=filter_author)
  22. if (filter_press):
  23. objects = objects.filter(press__contains=filter_press)
  24. if (filter_isbn):
  25. objects = objects.filter(isbn__contains=filter_isbn)
  26. if (filter_name):
  27. objects = objects.filter(name__contains=filter_name)
  28. return objects

代码重复的好像有点多, 虽然粘贴复制并不废什么功夫, 但是看起来心情就不是特别美丽。在这个时候, django_filters 就闪亮登场呢。

安装

  1. pip install djangoresframework
  2. pip install django
  3. pip install django-filter

配置

后台注册

  1. INSTALLED_APPS = [
  2. """
  3. 其他注册应用
  4. """
  5. 'rest_framework.apps.RestFrameworkConfig',
  6. 'django_filters',
  7. ]

应用

model.py

  1. from django.db import models
  2. class Book(models.Model):
  3. id = models.BigAutoField(primary_key=True)
  4. name = models.CharField(max_length=48)
  5. isbn = models.CharField(max_length=100)
  6. author = models.CharField(max_length=24)
  7. press = models.CharField(max_length=48)
  8. price = models.PositiveIntegerField(default=0)
  9. class Meta:
  10. db_table = "BookInfo"
  11. verbose_name = "BookInfo"
  12. verbose_name_plural = verbose_name

ModelSerializer.py

  1. from rest_framework import serializers
  2. from .models import Book
  3. class Book_serializers(serializers.ModelSerializer):
  4. class Meta:
  5. model = Book
  6. fields = '__all__'

ModelPagination.py

  1. # -*- coding: utf-8 -*-
  2. # @Time : 2021/1/25 上午12:18
  3. # @Author : void bug
  4. # @FileName: BooksPagination.py
  5. # @Software: PyCharm
  6. from rest_framework.pagination import PageNumberPagination
  7. class BooksPagination(PageNumberPagination):
  8. page_size = 4 # 表示每页的默认显示数量
  9. page_size_query_param = 'page_size' # 表示url中每页数量参数
  10. page_query_param = 'p' # 表示url中的页码参数
  11. max_page_size = 100000e100000 # 表示每页最大显示数量,做限制使用,避免突然大量的查询数据,数据库崩溃

ModelView.py

  1. from django_filters.rest_framework import DjangoFilterBackend
  2. from rest_framework import viewsets
  3. from .BooksPagination import BooksPagination
  4. from .models import Book
  5. from .serializer import Book_serializers
  6. class BooksView(viewsets.ModelViewSet):
  7. """
  8. 图书查询
  9. """
  10. queryset = Book.objects.all()
  11. serializer_class = Book_serializers
  12. pagination_class = BooksPagination
  13. # 筛选配置
  14. filter_backends = [DjangoFilterBackend] #应用筛选器
  15. filter_fields = ('name', 'author', 'isbn', 'press') # 筛选字段,其中括号内为应用筛选的字段

urls.py

  1. from django.urls import path, include
  2. from .views import BooksView
  3. from rest_framework import routers
  4. My_Views = routers.DefaultRouter()
  5. My_Views.register('Book', BooksView, basename="BookInfo")
  6. urlpatterns = [
  7. path('', include(My_Views.urls)),
  8. ]

应用截图

筛选前

5E15DE73-D513-4a56-8AFB-1BE4E0EC8523.pngB39CC4BC-1D14-422e-B807-C060A87CB40C.png

筛选后

778CF69F-BCDD-4492-A8D7-D2E9D194C4F6.png

进阶使用

  • 我们在使用时,进场遇到说用到的in,exact 等查询操作,及比较高级的筛选,我们到现在为止我们只能实现说精确的查询,但是无法满足说比较高级的查询操作,我们现在开始一些进阶
  • 相关的操作请参照以下说明

    Django QuerySet常用的字段查询

    contains就属于字段查询。

  • contains:包含,用来进行相似查询
  • icontains:同contains,只是忽略大小写
  • exact:精确匹配
  • iexact:同exact,忽略大小写
  • in:指定某个集合,比如Post.objects.filter(id__in=[1,2,3]),相当于SELECT * FROM blog_post WHERE IN (1,2,3)
  • gt:大于某值
  • gte:大于等于某值
  • lt:小于某值
  • lte:小于等于某值
  • startswith:以某个字符串开头,与contains类似,只是会产生LIKE
  • istartswith:同startswith,忽略大小写
  • endswith:以某个字符串结尾
  • iendswith:同endswith,忽略大小写
  • range:范围查询,多用于时间范围,如Post.objects.filter(created_time_range=(‘2020-06-01’,’2020-06-28’))

那么我们应该如何进行修改呢

我们对 ModelView.py 进行一点修改即可

  1. class UserViewSets(ModelViewSet):
  2. queryset = UserModels.objects.all()
  3. serializer_class = UserListSerializers
  4. pagination_class = BasePagination
  5. filter_backends = [DjangoFilterBackend]
  6. filter_fields = {"UserName": ['exact', 'lt', 'gt', "in", "contains"], "phone": ['exact', 'lt', 'gt', "in", "contains"]}

我们现在看下运行结果
4008BD1D-5D34-4922-8747-FF00A5C10675.png

  • 特别说明,我们使用 in 操作时,使用,(逗号)进行分割

    总结

    官方文档