flask-admin章節五:wtforms FormField超級炫酷使用


1. 概述

查看wtforms代碼樹fields目錄的core.py,會發現在文件開頭有這樣的語句:

__all__ = (
    'BooleanField', 'DecimalField', 'DateField', 'DateTimeField', 'FieldList',
    'FloatField', 'FormField', 'IntegerField', 'RadioField', 'SelectField',
    'SelectMultipleField', 'StringField',
)

這個表示當前文件在被Import的時候,能夠導入的所有方法。上面的這些,除了FieldListFormField這兩個表單字段我們平時使用得比較少以外,

其他的我們或多或少都使用過了。而且通常情況下,上面的那些基本的字段已經完全符合我們要求了,除非特殊情況,否則,我們根本沒有機會使用到。

關於FormField的使用,估計大家在使用的時候就挺郁悶的。要么就是報錯,要么就是渲染出來的頁面出現格式問題,根本沒法使用。看官網或許幫助不大,

查找中文資料又很少有幫助的,所以就會很困惑。

不過,我最近遇到的一個問題發現用FormField非常的炫酷,而且特別特別有意思。

2. 遇到的問題

最近遇到日常工單系統,大概有13中常規的工單,每種都是不一樣的表單。表單創建之后還有對應List、Get以及處理。所以后面大概有30中不同的視圖。

如果像之前那種方式,每個表單對應一個HTML,那樣會導致頁面太多。

另外就是,主頁面空間有限,不能讓這些表單頁面堆積在一起。所以,還是希望這13種表單服用同一個頁面,根據用戶選擇的類型的不同來做不同的展示。

但是,怎樣把這13種表單融合在一個HTML文件中,並且不影響到每個表單每個字段的檢查。

這塊可能要多說下,大家知道我們在使用wtforms Form表單元類派生出自己的表單類的時候,可能對一些必須的字段定義些validator方法,這樣在

用戶提交表單的時候wtforms自身為我們做了對應的檢查。

很顯然,我們在做到這點肯定是使用某種隱藏繼續,在需要顯示的時候才顯示對應的頁面,其他情況下都是隱藏的。但是,這塊又有一個問題就是,

如果隱藏了,哪些隱藏了的定義了validator的表單字段會在表單提交之后直接報錯。

解決了這些問題,還有一個問題就是查看頁面詳情,用戶創建一個表單,期待的時候查看詳情也看到這塊的內容,而不希望看到其他的表單。所以,

這塊又需要對應13種不同的頁面來展示不同的表單類型的數據。這塊能不能用一種更優雅的方式解決?

概括下遇到的問題,大致如下:

  1.  需要展示的表單類型過多,而且后面可能會有新的需求加入,所以需要盡可能的簡潔以及可擴展的方式來呈現表單,盡可能把所有表單結構放在一個頁面中。
  2.  為了可擴展性,表單類需要繼承自wtforms Form類,但是如何確保隱藏的表單中必選字段對當前表單的影響。
  3.  給用戶展示對應創建的表單時,能否用一種通用的方式來展示這13種字段完全不太相同的表單結構,並且代碼盡可能具有良好的可擴展性。

 

3. 大致的解法

由於現在比較晚,所以就不給出詳細的解決方案,這塊會大致講下解決方法。不過,相信大家能夠從中找到靈感。

針對問題1:

使用wtforms 的FormField,對13種不同方格的表單,創建13種派生自wtforms Form基類的派生類。定義一個功能類,功能類中有13個字段都是FormField類型,

分別對應這13個表單結構。這樣就能夠做到對單個表單結構的精確控制,即使某個表單內容做了更改也不會影響到頁面的布局以及其他模塊。

頁面的顯示,需要重構flask admin系統默認模板admin/file/form.html的內容。具體,對於整個表單調用lib.render_form_fields(form),改為是

對每個表單結構調用lib.render_form_fields(form.XXX_field)。

這樣,能夠確保渲染出來的FormField字段不會出現格式有問題或者亂碼。

針對問題2:

重載派生類表單的validate方法,這塊有些技巧,具體可以看下我這塊使用的代碼:

def validate(self):
        monitor_type = self.monitor_type.data
        hidden_field_val = str(monitor_type.get('hidden_field', ''))

        fields = self._fields

        extra = {}
        success = True
        for name, field in iteritems(fields):
            if name == hidden_field_val:
                for _name, _field in iteritems(field._fields):
                    if not _field.validate(field._fields):
                        success = False
        return success

這塊有個技巧,就是在表單中使用隱藏表單來表征當前用戶選擇的表單類型。這樣我們在后端做驗證的時候,只

針對當前表單元素做驗證,而不是表單功能類的13中表單結構。這樣,隱藏的表單中的必選字段就不會影響到當前表單,因為我們只會檢查當前顯示表單的結構。

上述接粗的代碼就是取出隱藏的表單字段的值。不過有一點,由於隱藏表單字段的值是不能用戶輸入的,只能我們自己用jQuery代碼對它進行設置。

這樣,這塊的問題就解決了。

針對問題3:

這塊有個比較有意思的技巧,定義數據庫表結構的時候要盡可能的與表單結構無關,這樣的話可擴展性更好些。否則,一個是表結構字段太多,太龐大了,

另外就是可擴展性不太好。所以,我這塊是使用data字段直接表征整個表單所有數據,每次提交表單的時候,直接獲取表單的字典結構:

這塊使用了表單的data屬性,data屬性會直接返回一個字典結構。我們在存儲到數據庫中直接使用json.dumps(form.data),把表單值序列化成字符串並存到數據庫表結構data

字段中。用戶讀取的時候,在把對應字段json.loads回來,並展示給用戶就可以了。
如何給用戶展示的時候,不需要定義很多HTML頁面,而只使用一個,這塊的關鍵是巧用Form類,我們可以根據表單類型獲取到對應的表單派生類,並使用它來定義表單對象,

這塊可以使用預定義的map就可以了,而且可擴展性非常好。

定義好表單對象之后,從數據庫中load數據,把data字段的值json化之后傳給表單。然后,我們直接使用admin/file/form.html 模板渲染頁面問題就完全解決了。

 

4. 小結

當使用一種方法復雜到想讓你放棄的時候,一定要堅持,因為一定還有其他路要走。因為,你可能之前走錯了方向,換個角度思考,你會獲得很多意想不到的收獲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM