一.DRF之Jwt 實現自定義python
二.DRF(過濾,排序,分頁)組件git
三.Django-filter插件的使用和自定義數據庫
""" 一、drf-jwt手動簽發與校驗 :只是作token的產生和提升效率 緩解服務器的壓力 檢驗用戶的的合法性(tokenbase64反解析) 二、drf小組件:過濾、篩選、排序、分頁 => 針對與羣查接口 """
一.drf-jwt手動簽發與校驗django
事前準備工做後端
1.1url 獲取token 用戶登陸成功就能夠獲取tokenapi
model服務器
from django.db import models # Create your models here. # 重點: 若是咱們自定義user表, 再另外一個項目中採用原生的User表,完成數據庫遷移時,可能會失敗 #如何作*(1) 卸載Django 從新裝 # (2) 將Djjango中的 contrib 下面的admin 下面的數據庫遷移命令記錄清空 from django.contrib.auth.models import AbstractUser class User(AbstractUser): #繼承AbstractUuser) mobile = models.CharField(max_length=11,unique=True) # 自定義建立表名 class Meta: db_table = 'api_user' verbose_name = '用戶表' verbose_name_plural = verbose_name def __str__(self): return self.username
adminide
from django.contrib import admin from . import models # Register your models here. admin.site.register(models.User) admin.site.register(models.Car)
View函數
# 必須登陸後才能訪問 - 經過了認證權限組件 from rest_framework.permissions import IsAuthenticated # from rest_framework_jwt.authentication import JSONWebTokenAuthentication from utils.reponse import APIResponse from rest_framework.views import APIView # 自定義jwt校驗規則 #實現多方式登錄簽發token:帳號、手機號、郵箱等登錄 # 1) 禁用認證與權限組件 # 2) 拿到前臺登陸信息,交給序列化類 # 3) 序列化類校驗獲得登陸用戶與token存放在序列化對象中 # 4) 取出登陸用戶與token返回給前臺 from . import serializers, models from .authentications import JWTAuthenticate class LoginAPIView(APIView): # 1) 禁用認證與權限組件 authentication_classes = [] permission_classes = [] def post(self, request, *args, **kwargs): # 2) 拿到前臺登陸信息,交給序列化類,規則:帳號用usr傳,密碼用pwd傳 user_ser = serializers.UserModelSerializer(data=request.data) # 3) 序列化類校驗獲得登陸用戶與token存放在序列化對象中 user_ser.is_valid(raise_exception=True) # 4) 取出登陸用戶與token返回給前臺 # return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data) def get(self, request, *args, **kwargs): authentication_classes = [JWTAuthenticate] permission_classes = [IsAuthenticated] print(request.user) return APIResponse(1,'ok',results=request.user.username)
第一個測試是咱們是直接找的路由 提供用戶名和密碼 直接生成token 工具
url(r'^login/', views.LoginAPIView.as_view())
第二測驗是經過現有的用戶名 usr ='admin' paw = "admin123"
經過serializer 檢驗能過 後 user >>> payload >>> 生成token
View
from . import serializers, models from .authentications import JWTAuthenticate class LoginAPIView(APIView): # 1) 禁用認證與權限組件 authentication_classes = [] # 登陸時禁用 不能阻攔 permission_classes = [] def post(self, request, *args, **kwargs): # 2) 拿到前臺登陸信息,交給序列化類,規則:帳號用usr傳,密碼用pwd傳 user_ser = serializers.UserModelSerializer(data=request.data) # 3) 序列化類校驗獲得登陸用戶與token存放在序列化對象中 user_ser.is_valid(raise_exception=True) # 4) 取出登陸用戶與token返回給前臺 # return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data)
serializer >>> 檢驗字段 鉤子函數 >>> jwt 生成token
import re from rest_framework import serializers from rest_framework_jwt.serializers import jwt_payload_handler from rest_framework_jwt.serializers import jwt_encode_handler from . import models # 拿到前臺token的兩個函數: user >>>payload>>>token # 自定義序列化 和反序列化類 class UserModelSerializer(serializers.ModelSerializer): # 自定義反序列化字段:必須設置只參與發你序列化的(write_only)字段 序列化不會與model映射 只寫寫本身返回的字段 usr = serializers.CharField(write_only=True) pwd = serializers.CharField(write_only=True) # 只寫字段放在下面也可 class Meta: model = models.User fields = ['usr', 'pwd','email','mobile','username'] extra_kwargs = { 'username': { 'read_only': True }, 'mobile': {'read_only': True }, 'email': { 'read_only': True } } # 局部鉤子和全局鉤子 看狀況進行 使用 多的就用全局 def validate(self, attrs): usr = attrs.get('usr') pwd = attrs.get('pwd') # 多種方式進行登陸 請求的有手機 郵箱 用戶名等登陸方式 if re.match(r'.+@.+', usr): user_query = models.User.objects.filter(email=usr) elif re.match(r'^1[3-9][0-9]{9}$', usr): user_query = models.User.objects.filter(mobile=usr) else: user_query = models.User.objects.filter(username=usr) # 獲取對 user_obj = user_query.first() # print(user_obj, 333) # 簽發token :獲得用戶的登陸,簽發token並存儲在實列化對象中 # 判斷是否有用戶和密碼是否正確 if user_obj and user_obj.check_password(pwd): # 將產生的token 放到咱們實列化對象中 # (1) user_obj >>> payload >>>token payload = jwt_payload_handler(user_obj) # JWT_PAYLOAD_HANDLER token = jwt_encode_handler(payload) # 將當前的用戶和簽發的token 保存到實列化的 對象中 self.user = user_obj self.token = token print(self.token) return attrs # 異常 raise serializers.ValidationError({'data':'數據有誤'}) # 汽車的羣查序列化基本的數據和字段的操做 # 很簡單的直接的序列化 class CarModelSerializer(serializers.ModelSerializer): # 自定義返回字段 class Meta: model = models.Car fields = ['name', 'price', 'brand']
2.1. 檢驗token token的自定義(改源碼)
View的代碼是檢驗用戶是否能夠進行其餘操做 (訂單類的服務:查看,提交)
進行檢驗和判斷用戶是否登陸 進行 返回信息
authenticate 文件下
# 導包 import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework.exceptions import AuthenticationFailed from rest_framework_jwt.authentication import jwt_decode_handler # 自定義檢測檢驗規則: auth token jwt class JWTAuthenticate(BaseJSONWebTokenAuthentication): def authenticate(self, request): # 發送的token 反爬蟲的思路 jwt_token = request.META.get('HTTP_AUTHORIZATION') print(jwt_token,1) print(request.META,2) # 自定義檢驗 auth token jwt token = self.parse_jwt_token(jwt_token) if token is None: return None # 反解 經過 token >>>payload >>> user 確認登陸 try: payload = jwt_decode_handler(token) except jwt.ExpiredSignature: raise AuthenticationFailed('TOKEN已過時') except: raise AuthenticationFailed('非法用戶') user = self.authenticate_credentials(payload) return (user, token) # 自定義校驗規則:auth token jwt.auth爲自定義前言,jwt爲後鹽 def parse_jwt_token(self, jwt_token): token = jwt_token.split() if len(token) != 3 or token[0].lower() != 'auth' or token[2].lower() != 'jwt': return None return token[1]
補充:
admin使用自定義User表:新增用戶密碼密文
from django.contrib import admin from . import models # 自定義User表,admin後臺管理,採用密文密碼 from django.contrib.auth.admin import UserAdmin class MyUserAdmin(UserAdmin): add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('username', 'password1', 'password2', 'mobile', 'email'), }), ) admin.site.register(models.User, MyUserAdmin)
二.DRF(過濾,排序,分頁)組件
2.1搜索過濾組件
model
# drf 羣查接口model表 class Car(models.Model): name = models.CharField(max_length=16, verbose_name='車名') price = models.DecimalField(max_digits=8, decimal_places=2,verbose_name='價格') brand = models.CharField(max_length=16,verbose_name='品牌') class Meta: verbose_name = '汽車表' verbose_name_plural = verbose_name def __str__(self): return self.name
源碼分析的流程
seializer
# 汽車的羣查序列化基本的數據和字段的操做 # 很簡單的直接的序列化 class CarModelSerializer(serializers.ModelSerializer): # 自定義返回字段 class Meta: model = models.Car fields = ['name', 'price', 'brand']
filter_backends 過濾的組件
""" 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request)
URL
# 路由分發 from django.conf.urls import url from api import views from rest_framework_jwt.views import ObtainJSONWebToken,obtain_jwt_token # 登陸簽發token(生成) urlpatterns = [ url(r'^jlogin/', ObtainJSONWebToken.as_view()), url(r'^login/', views.LoginAPIView.as_view()), url(r'^cars/', views.CarsListApiView.as_view()), ]
View
# drf restframework # 羣查接口 # model >>>路由 >>> 視圖 >>> serializes(序列化和反序列化) from api.serializers import CarModelSerializer # 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數 # queryset = None 序列化 須要咱們 第一個參數:queryset() 數據庫的數據對象 # serializer_class = None # 第二個 是咱們的將序列化 對象賦值給serializer_class = serializer.CarModelSerializer() # print(serializer_class) # return APIResponse(1,'OK',results=queryset) # (1) search 搜查 # 相似與一個api.settings 也是全局的配置和局部的配置 # 在函數內屬於局部的配置 """ 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) """ """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """ # 搜索過濾查詢依賴的條件 >>>接口cars?search=1/寶馬等 # backends 後端 後頭 進咱們的過濾條件加我到咱們的;列表中 filter_backends = [SearchFilter] # search_fields = ['name', 'price']
Postman的配置
api/cars/後面不加添加其餘查詢all
如何實現自定義的過濾
2.2排序過濾組件
# drf restframework # 羣查接口 # model >>>路由 >>> 視圖 >>> serializes(序列化和反序列化) from api.serializers import CarModelSerializer # 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter # (2) 排序過濾組件 drf 的Ordering from rest_framework.filters import OrderingFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數 # queryset = None 序列化 須要咱們 第一個參數:queryset() 數據庫的數據對象 # serializer_class = None # 第二個 是咱們的將序列化 對象賦值給serializer_class = serializer.CarModelSerializer() # print(serializer_class) # return APIResponse(1,'OK',results=queryset) # (1) search 搜查 # 相似與一個api.settings 也是全局的配置和局部的配置 # 在函數內屬於局部的配置 """ 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) """ """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """ # 搜索過濾查詢依賴的條件 >>>接口cars?search=1/寶馬等 # backends 後端 後頭 進咱們的過濾條件加我到咱們的;列表中 # filter_backends = [SearchFilter] # # # search_fields = ['name','price'] # 添加過濾的類 filter_backends = [SearchFilter,OrderingFilter] # 篩選的字段 ordering_fields = ['pk','price']
2.3 分頁
1.1咱們依然的用的同一個接口
Pagenation 文件代碼
# drf 基礎的分頁組件 from rest_framework.pagination import PageNumberPagination # 自定義分頁的規則 class MyPageNumberPagination(PageNumberPagination): # ?page = 頁碼 page_queryset_param = 'page' # ?page=頁面下默認的一頁顯示的條數 page_size = 3 # 用戶能夠自定義顯示的頁面的條數 page_size_query_param = 'page_size' # 一個頁面顯示的最大的限制的條數
max_page_size = 5
VIew視圖代碼
# 必須登陸後才能訪問 - 經過了認證權限組件 from rest_framework.permissions import IsAuthenticated # from rest_framework_jwt.authentication import JSONWebTokenAuthentication from utils.reponse import APIResponse from rest_framework.views import APIView # 自定義jwt校驗規則 #實現多方式登錄簽發token:帳號、手機號、郵箱等登錄 # 1) 禁用認證與權限組件 # 2) 拿到前臺登陸信息,交給序列化類 # 3) 序列化類校驗獲得登陸用戶與token存放在序列化對象中 # 4) 取出登陸用戶與token返回給前臺 from . import serializers, models from .authentications import JWTAuthenticate class LoginAPIView(APIView): # # 1) 禁用認證與權限組件 # authentication_classes = [] # 登陸時禁用 不能阻攔 # permission_classes = [] # # def post(self, request, *args, **kwargs): # # 2) 拿到前臺登陸信息,交給序列化類,規則:帳號用usr傳,密碼用pwd傳 # user_ser = serializers.UserModelSerializer(data=request.data) # # 3) 序列化類校驗獲得登陸用戶與token存放在序列化對象中 # user_ser.is_valid(raise_exception=True) # # 4) 取出登陸用戶與token返回給前臺 # # # return APIResponse(token=user_ser.token, results=serializers.UserModelSerializer(user_ser.user).data) def get(self, request, *args, **kwargs): authentication_classes = [JWTAuthenticate] permission_classes = [IsAuthenticated] print(request.user,3334) return APIResponse(1,'ok',results=request.user.username) # drf restframework # 羣查接口 # model >>>路由 >>> 視圖 >>> serializes(序列化和反序列化) from api.serializers import CarModelSerializer # 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter # (2) 排序過濾組件 drf 的Ordering from rest_framework.filters import OrderingFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數 # queryset = None 序列化 須要咱們 第一個參數:queryset() 數據庫的數據對象 # serializer_class = None # 第二個 是咱們的將序列化 對象賦值給serializer_class = serializer.CarModelSerializer() # print(serializer_class) # return APIResponse(1,'OK',results=queryset) # (1) search 搜查 # 相似與一個api.settings 也是全局的配置和局部的配置 # 在函數內屬於局部的配置 """ 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) """ """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """ # 搜索過濾查詢依賴的條件 >>>接口cars?search=1/寶馬等 # backends 後端 後頭 進咱們的過濾條件加我到咱們的;列表中 # filter_backends = [SearchFilter, OrderingFilter] # search_fields = ['name','price'] # 添加過濾的類 # 篩選的字段 # ordering_fields = ['pk','price'] from api.pahenations import MyPageNumberPagination # 分頁組件 - 給視圖類配置分頁類須要自定義 繼承drf提供的分頁類便可 pagination_class = MyPageNumberPagination
2.4 drf偏移分頁組件
View 視圖
# 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter # (2) 排序過濾組件 drf 的Ordering from rest_framework.filters import OrderingFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數
pagenations
# drf 基礎的分頁組件 # from rest_framework.pagination import PageNumberPagination # (2)drf偏移分頁組件 from rest_framework.pagination import LimitOffsetPagination # 自定義分頁的規則 class MyLimitOffsetPagination(LimitOffsetPagination): # # ?page = 頁碼 # page_queryset_param = 'page' # # ?page=頁面下默認的一頁顯示的條數 # # page_size = 3 # # 用戶能夠自定義顯示的頁面的條數 # # page_size_query_param = 'page_size' # # # 一個頁面顯示的最大的限制的條數 # max_page_size = 5 # (2)drf偏移分頁組件 # ?offset=從頭偏移 的條數 $ limit=咱們要顯示的條數 limit_query = 'limit' offset_query_param = 'offset' # 不傳offset和limit 默認顯示前3條 只設置offset的化就是從偏移位置在日後顯示幾條 default_limit = 3 # 默認 max_limit = 5 # 最多顯示的條數 # ?ordering= -price$limit=2 >>>展現價格前2條
offset
結合ordering 使用
2.5 drf 遊標分頁組件(瞭解)
## drf遊標分頁組件(瞭解) ##### pahenations.py ```python # 注:必須基於排序規則下進行分頁 # 1)若是接口配置了OrderingFilter過濾器,那麼url中必須傳ordering # 1)若是接口沒有配置OrderingFilter過濾器,必定要在分頁類中聲明ordering按某個字段進行默認排序 from rest_framework.pagination import CursorPagination class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' page_size = 3 page_size_query_param = 'page_size' max_page_size = 5 ordering = '-pk' ``` ##### views.py ```python from rest_framework.generics import ListAPIView class CarListAPIView(ListAPIView): # 若是queryset沒有過濾條件,就必須 .all(),否則分頁會出問題 queryset = models.Car.objects.all() serializer_class = serializers.CarModelSerializer # 分頁組件 - 給視圖類配置分頁類便可 - 分頁類須要自定義,繼承drf提供的分頁類便可 pagination_class = pagenations.MyCursorPagination ```
2.6 自定義分頁組件
filter 文件中重開一個‘
# 自定義過濾器 接口:?limit=顯示條數 class LimitFilter: def filter_queryset(self,request, queryset,view): # 前臺固定用 ?limit=...傳遞過濾參數 limit = request.query_params.get('limit') if limit: limit = int(limit) return queryset[:limit] # 進行切片 return queryset
View中
# 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter # (2) 排序過濾組件 drf 的Ordering from rest_framework.filters import OrderingFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數 # queryset = None 序列化 須要咱們 第一個參數:queryset() 數據庫的數據對象 # serializer_class = None # 第二個 是咱們的將序列化 對象賦值給serializer_class = serializer.CarModelSerializer() # print(serializer_class) # return APIResponse(1,'OK',results=queryset) # (1) search 搜查 # 相似與一個api.settings 也是全局的配置和局部的配置 # 在函數內屬於局部的配置 """ 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) """ """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """ # 搜索過濾查詢依賴的條件 >>>接口cars?search=1/寶馬等 # backends 後端 後頭 進咱們的過濾條件加我到咱們的;列表中 from . filters import LimitFilter filter_backends = [SearchFilter, OrderingFilter, LimitFilter] # search_fields = ['name','price'] # 添加過濾的類 # 篩選的字段 ordering_fields = ['pk','price'] # from api.pahenations import MyLimitOffsetPagination # pagination_class = MyLimitOffsetPagination
三.Django-filter插件的使用和自定義
# 下載 django -filter
# 直接安裝
D:\day74_djproj>pip install django-filter
# 怎麼樣寫>>>使用自定義
# View 視圖層
# 羣查的接口就是 >>>>搜素組件 from rest_framework.generics import ListAPIView, ListCreateAPIView # (1)搜索查詢 關鍵字searchFilter from rest_framework.filters import SearchFilter # (2) 排序過濾組件 drf 的Ordering from rest_framework.filters import OrderingFilter class CarsListApiView(ListCreateAPIView): # # 獲取序列化(查詢的對象) get 請求 # def get(self, request, *agrs, **kwargs): queryset = models.Car.objects.all() # 咱們繼承ListAPIView 裏面是五大工具類繼承 六大方法 就是咱們不再須要進行寫咱們的get post patch list update create # print(queryset) serializer_class = serializers.CarModelSerializer # # 咱們須要兩個參數 # queryset = None 序列化 須要咱們 第一個參數:queryset() 數據庫的數據對象 # serializer_class = None # 第二個 是咱們的將序列化 對象賦值給serializer_class = serializer.CarModelSerializer() # print(serializer_class) # return APIResponse(1,'OK',results=queryset) # (1) search 搜查 # 相似與一個api.settings 也是全局的配置和局部的配置 # 在函數內屬於局部的配置 """ 源碼 def filter_queryset(self, request, queryset, view): search_fields = self.get_search_fields(view, request) search_terms = self.get_search_terms(request) """ """ for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """ # 搜索過濾查詢依賴的條件 >>>接口cars?search=1/寶馬等 # backends 後端 後頭 進咱們的過濾條件加我到咱們的;列表中 # filter_backends = [SearchFilter, OrderingFilter, LimitFilter] # search_fields = ['name','price'] # 添加過濾的類 # 篩選的字段 ordering_fields = ['pk','price'] # from api.pahenations import MyLimitOffsetPagination # pagination_class = MyLimitOffsetPagination # 視圖層: from django_filters.rest_framework import DjangoFilterBackend from .filter import CarFilter # 局部配置 過濾類們(全局配置的話就用DEFAULT_FILTER_BACKENDS) filter_backends = [DjangoFilterBackend] # django-filter過濾器插件使用 filter_class = CarFilter # 接口: ? brand=...&min_price...&max_price.... # ru ?brand=寶馬&min_price=12&min_price=15
filter文件的代碼
# django-filter 插件過濾器類 from django_filters.rest_framework import FilterSet from . import models # 自定義過濾字段 from django_filters import filters class CarFilter(FilterSet): min_price = filters.NumberFilter(field_name='price', lookup_expr='gte') max_price = filters.NumberFilter(field_name='price', lookup_expr='lte') class Meta: model = models.Car fields = ['brand', 'min_price', 'max_price'] # brand是model中存在的字段,通常都是能夠用於分組的字段 # min_price、max_price是自定義字段,須要本身自定義過濾條件
如圖:
===待改進....