一般在製作列表類的網頁時,常常最讓人困擾的就是篩選、排序跟自定輸出欄位。
那也很常看到這些處理方法:
第一個方式,問題是在網站端,「一次取得所有資料」會影響網站回應的速度。網站首先要去資料庫查詢,然後再把資料轉換為 JSON 回應給客戶端,如果沒有使用快取,那麼每次都會要從資料庫拿一次資料,再傳輸這麼多資料給客戶端。
第二個方式,問題在於回應會稍慢,因為每次變更條件,就會要告知網站,網站再次依據條件來查詢、回應。但是好處在於,回應的資料不會像前者那麼的多。
django-url-filter 主要是讓開發者在使用第二種方式時,能夠比較輕鬆。它把 filter 的處理機制標準化,讓開發者能夠簡便的做出篩選功能。它更棒的是,同時支援了 Django 的 class based view 跟 REST framework 的 APIView,接下來就讓我們看怎麼使用吧。
專案網址:https://github.com/miki725/django-url-filter
poetry add django-url-filter
django-url-filter 可以應用在 Django class based view 與 REST framework 的 ListAPI 上,下面我們就先介紹在 Django class based view 裡怎麼使用。
Django class based view 在取得資料時,都要填寫 queryset 屬性,實際上的運作,是因為 ListView/DetailView 所繼承的 MultipleObjectMixin/SingleObjectMixin 類別會透過 get_queryset() 來進行查詢,而 get_queryset() 會先讀取 queryset 屬性,如果沒有,預設使用 model 屬性的 objects 來當做 queryset,也就是 model.objects 。
所以在傳遞參數做篩選時,都會 override get_queryset() 來處理。
django-url-filter 也是如此,首先先繼承 ModelFilterSet ,然後指定 Meta ,聲明要用哪個 model 以及允許的欄位。
# views
from django.views.generic import ListView
from url_filter.filtersets import ModelFilterSet
from .models import Article
class ArticleFilterSet(ModelFilterSet):
class Meta(object):
model = Article
fields = ['title', 'reporter', 'content']
class ArticleListView(ListView):
"""ListView for MedicalPersonnel."""
model = Article
template_name = "news/article_list.html"
paginate_by = 10
ordering = 'id'
def get_queryset(self):
"""Return custom queryset."""
queryset = super().get_queryset()
return ArticleFilterSet(
data=self.request.GET, queryset=queryset).filter()
這裡我們建立 ArticleFilterSet 去繼承 ModelFilterSet,然後聲明是 Article 這個 model,並允許使用 title, reporter, content 來篩選。
ArticleListView 是處理 Article 列表的 View,它繼承了 ListView,然後告知要處理的 model,接著就在 get_queryset() 裡處理參數。這裡把 request.GET (也就是 Query parameter) 傳入 ArticleFilterSet 的初始函數裡,ArticleFilterSet 會解析 request.GET,然後進行轉換,呼叫 filter() 以後,就可以得到查詢結果。
接著來試試看,打開瀏覽器在網址列輸入:http://localhost:8000/article/?title__icontains=foo
就可以看到結果啦
django-url-filter 也可以搭配 REST framework 使用。
from rest_framework.generics import ListAPIView
from url_filter.integrations.drf import DjangoFilterBackend
from .models import Article
from .serializers import ArticleSerializer # REST framework (1) 時定義的
class ArticleFilterSet(ModelFilterSet):
class Meta(object):
model = Article
fields = ['title', 'reporter', 'content']
class ArticleListAPIView(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = [DjangoFilterBackend]
filter_class = ArticleFilterSet
一樣沿用前個例子的 ArticleFilterSet,然後撰寫 ArticleListAPIView ,繼承自 ListAPIView。
填寫 filter_backends 跟 filter_class,filter_backends 固定填寫 django-url-filter 所提供的 DjangoFilterBackend,這個 backend 會讀取 filter_class 這個屬性所指定的 FilterSet,那麼在 API 被呼叫、進行查詢時,就會使用 ArticleFilterSet 來讀取參數進行篩選。
django-url-filter 也可以應用在 SQLAlchemy 上,也就是說 flask 也可以使用。
django-url-filter 讓我們可以使用統一的方式來進行篩選,真的是很方便。