Django - model數(shù)據(jù)的過(guò)濾,使用django_filters

問題背景

在Web應(yīng)用當(dāng)中,尤其是后臺(tái)管理應(yīng)用,經(jīng)常遇到的一個(gè)場(chǎng)景是,需要根據(jù)用戶的輸入條件,在數(shù)據(jù)庫(kù)中查詢相應(yīng)數(shù)據(jù)并展示。 那讓我們看看在Django中如何處理?

先看看官網(wǎng)的介紹
https://docs.djangoproject.com/en/1.11/topics/db/queries/

官網(wǎng)介紹的很詳細(xì), 我就不重復(fù)粘貼復(fù)制了, 在這里只記錄一下一個(gè)典型的使用場(chǎng)景.

場(chǎng)景描述

有一個(gè)關(guān)于書本信息的數(shù)據(jù)表, 包括書本的書名,價(jià)格,出版社,ISBN,作者。Model定義如下:

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

在書本管理后臺(tái),需要實(shí)現(xiàn)根據(jù)用戶輸入的作者信息,進(jìn)行模糊查詢。 前端通過(guò)ajax POST將表單信息傳送給后臺(tái)。Django在收到請(qǐng)求之后,在view當(dāng)中調(diào)用如下函數(shù)就可以進(jìn)行數(shù)據(jù)庫(kù)的查找過(guò)濾操作呢。 其中icontains表示忽略大小寫的模糊查詢。

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

Django支持的查詢方式有很多, 具體請(qǐng)查看以下官網(wǎng)介紹:
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#field-lookups

一切看起來(lái)都不錯(cuò),有什么不妥?

目前看起來(lái)確實(shí)沒有什么不妥,但是當(dāng)定義的model多了, 要查詢的表單多了之后, 相關(guān)的代碼片段就變成了下面這樣:

def filter_libooks(objects, request):
    filter_status = request.POST['status']
    filter_uuid = request.POST['uuid']
    filter_isbn = request.POST['isbn']
    filter_name = request.POST['name']
    if (filter_status):
        objects = objects.filter(status=filter_status)
    if (filter_isbn):
        objects = objects.filter(book__isbn__contains=filter_isbn)
    if (filter_name):
        objects = objects.filter(book__name__contains=filter_name)
    if (filter_uuid):
        objects = objects.filter(uuid_contains=filter_uuid)        
    return objects

def filter_books(objects, request):
    filter_author = request.POST['author']
    filter_press = request.POST['press']
    filter_isbn = request.POST['isbn']
    filter_name = request.POST['name']
    if (filter_author):
        objects = objects.filter(author__contains=filter_author)
    if (filter_press):
        objects = objects.filter(press__contains=filter_press)
    if (filter_isbn):
        objects = objects.filter(isbn__contains=filter_isbn)
    if (filter_name):
        objects = objects.filter(name__contains=filter_name)
    return objects

代碼重復(fù)的好像有點(diǎn)多, 雖然粘貼復(fù)制并不廢什么功夫, 但是看起來(lái)心情就不是特別美麗。在這個(gè)時(shí)候, django_filters 就閃亮登場(chǎng)呢。

Django_Filters

單表查詢
Filter定義

先看看在上文已經(jīng)描述過(guò)的要進(jìn)行單表查詢的場(chǎng)景下, 使用django_filters如何來(lái)完成
model定義不變, 定義如下Filter類

class BookFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_expr='icontains')
    author = django_filters.CharFilter(lookup_expr='icontains')
    isbn = django_filters.NumberFilter(lookup_expr='icontains')
    press = django_filters.CharFilter(lookup_expr='icontains')

    class Meta:
        model = Book
        fields = {'name', 'author', 'isbn', 'press'}

這個(gè)類解釋如下:

  • model 該類是為Model Book定義的過(guò)濾類
  • fields 該過(guò)濾類可以處理Book model中字段name,author,isbn,press的查詢
  • name = django_filters.CharFilter(lookup_expr='icontains') 指定name字段的過(guò)濾條件為icontains

值得注意的是django_filters如何只指定fields,不指定特定fields的過(guò)濾方法, 那么默認(rèn)會(huì)使用exact的過(guò)濾條件進(jìn)行查詢。

在view中的使用
# filter objects according to user inputs
objects = BookFilter(request.POST, queryset=objects)

recordsFiltered = objects.qs.count()
objects = objects.qs[start:(start + length)]

在官網(wǎng)的介紹當(dāng)中,使用的比較多場(chǎng)景是使用過(guò)濾器的返回值作為參數(shù)去渲染模板文件。那如果需要后端進(jìn)行分頁(yè)處理, 就需要使用返回值的qs屬性呢
比如示例當(dāng)中的 recordsFiltered = objects.qs.count(), 將查詢得到的query set的記錄個(gè)數(shù)返回給前端Datatable插件。

多表查詢

單表查詢比較容易理解, 那么當(dāng)我們需要使用多表查詢的時(shí)候, 該怎么做呢?

model定義
class Book(models.Model):
    name = models.CharField(max_length=48)
    isbn = models.IntegerField(primary_key=True, unique=True)
    author = models.CharField(max_length=24)
    press = models.CharField(max_length=48)
    price = models.PositiveIntegerField(default=0)

class LibBook(models.Model):
    # Relations
    book = models.ForeignKey(Book, on_delete=models.PROTECT,null=False)
    # Attributes
   
    uuid = models.UUIDField(default=uuid.uuid4, null=False)
    inDate = models.DateField(auto_now_add=True)
    dueDate = models.DateField(blank=True, null=True)
    overDays = models.PositiveIntegerField(default=0)
    LendAmount = models.PositiveIntegerField(default=0)

在這個(gè)例子當(dāng)中,書本信息和館藏圖書是一對(duì)多的關(guān)系,我們?cè)贚ibBook當(dāng)中使用ForeignKey來(lái)指定LibBook和Book之間的關(guān)系。

問題: 如何使用書本的書名和isbn信息來(lái)查詢本地圖書館的藏書信息?
Fileter定義
class LibBookFilter(django_filters.FilterSet):
    book__name = django_filters.CharFilter(lookup_expr='icontains')
    book__isbn = django_filters.NumberFilter(lookup_expr='icontains')

    class Meta:
        model = LibBook
        fields = {'book__name', 'book__isbn'}

值得注意的是fields的定義, 要使用Django中規(guī)定的雙下劃線符號(hào)__來(lái)指定查詢的字段。
在本例中, 我們同樣指定查詢的條件是icontains

View使用
# filter objects according to user inputs
objects = LibBookFilter(request.POST, queryset=objects)
recordsFiltered = objects.qs.count()
objects = objects.qs[start:(start + length)]

看到這里,感覺和單表查詢也差不了太多啊。 在筆者使用django_filters的過(guò)程中,最大的坑就是在這里呢, 怎么設(shè)置都不好使。后來(lái)發(fā)現(xiàn)問題是:

POST 數(shù)據(jù)定義

前端同樣使用ajax將查詢數(shù)據(jù)通過(guò)POST傳到后臺(tái)。 ajax的data必須如下定義,django_filters才能正常工作。

"ajax": {
    "url": "#",
    "type": "POST",
    "data": function(d){
        return $.extend( {}, d, {
            "book__isbn"  : document.getElementById('isbn').value,
            "book__name"  : document.getElementById('book').value,
            });
    }
},

注意POST數(shù)據(jù)的數(shù)據(jù)名字必須和Filter中保持一致才行。

筆者找了好久,才在這篇博文中找到答案。
http://www.tomchristie.com/rest-framework-2-docs/api-guide/filtering

一語(yǔ)驚醒夢(mèng)中人大抵就是如此

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容