? ? ? ? 條目較少的選項集合,確實可以在程序中直接定義(其實最合適的還是存儲在一個分類別的數據庫表里),但條目較多的選項集合,或者是復雜的樹型結構選項集合,一般都是存儲在數據庫中的,這樣維護起來比較方便,也容易遷移和共享。所以,在完成基礎選項集合框架后,考慮應用的方便性,面向數據庫存儲的選項集合擴展類就成了下一步開發的任務。
????????有了基礎類做底,這些實現擴展起來也是十分方便。基于數據庫存儲的類包括兩大類型,一種是列表類,這個就是基礎選項類的擴展,數據結構沒有區別,只是多了選項條目從數據庫中裝載到內存中的過程,另一種是樹型類,它的數據結構比列表類復雜了一些,數據庫里也是以父子ID方式存放數據,放在內存里則是一個包括父子ID和名稱的字典,依據父子ID關系可以生成與layui-tree組件接口一致的樹型結構傳送到前端。
? ? ? ? 下面這段是列表類的python服務類List_OptionSet,它是基于OptionSet基礎類擴展的,所以,相同功能的類函數直接復用基礎類。重寫的子類函數包括構造函數__init__()和get_dict(),新寫的函數包括load()和reload()。可以看出,列表類與基礎類的區別是比較小的。
?(注:寫到這兒時,發現原來的基礎類中有個錯誤,就是reload()函數也必須在基礎類里編寫出來,否則如果對基礎類選項集合進行重載時,就會出現系統級錯誤)
#取自數據庫表的列表選項集合
class List_OptionSet(OptionSet):def __init__(self,dbclass=None,fdinf=None,n_opt=None,t_opt=None) :super().__init__()self.dbclass = dbclassself.fdinf = fdinfself.opt_name = n_optself.opt_title = t_optif n_opt :sysOptionPool.add_optset(n_opt,self)# 取選項字典def get_dict(self):if (self.opt_dict == None):if self.load(self.dbclass,self.fdinf) == 0:return Nonereturn self.opt_dict# 數據裝入函數def load(self,dbclass,fdinf):logging.info('Load Table OptionSet %s....' % self.opt_name)fdkey = fdinf.get('key')fdname = fdinf.get('name')filtstr = fdinf.get('filter')try :rows = db.session.query(dbclass).filter(text(filtstr)).all()opt_info = {}for irow in rows:valkey = getattr(irow,fdkey)valname = getattr(irow,fdname)opt_info[valkey] = valnameself.opt_dict = opt_infoexcept SQLAlchemyError as e:logging.debug('裝入選項集合[%s]數據庫讀取失敗!!%s' % (self.opt_name,str(e.orig)))return 0return 1#數據重載函數def reload(self) :rtncode = self.load(self.dbclass,self.fdinf)if rtncode == 0 :return 0if self.opt_name :sysOptionPool.add_optset(self.opt_name,self)return 1
? ? ? ? 列表類實現時,重點要強調一下,類實例初始化時并不會從數據庫中取數據,而是在第一次使用時,如果判斷條目字典為空,才會從數據庫中加載條目數據,這是系統中經常用到的方法,因為在系統啟動時,所有的選項實例就已經生成了,但大部分的實例在運行時并不會用到,如果這時加載,那會占用較多內存,并且啟動時間也會加長。
? ? ? ? 上面這個實現在get_dict()中完成,當self.opt_dict為空時調用load函數完成加載。reload()函數的主要用途是將條目數據從數據庫重新裝載到類實例中。當選項條目被維護后,就會調用此函數完成選項集合的同步。重載是選項集合服務一個路由請求實現,在上一節中有體現。
? ? ? ? 下面這段代碼是樹型選項集合類Tree_OptionSet的實現,它是在列表類的基礎上擴展的。可以看出,樹型類進行了大量的擴充,幾乎所有的函數都進行了重寫,并且增加了多個函數。樹型選項的存儲結構不再是簡單的鍵值對字典,而是一個以ID為鍵字的復合字典,并為前端提供了列表、映射以及樹型等多種格式的數據服務。
##樹型的選項集合類
class Tree_OptionSet(List_OptionSet) :def __init__(self,dbclass=None,fdinf=None,n_opt=None,t_opt=None) :super().__init__()self.dbclass = dbclassself.fdinf = fdinfself.opt_name = n_optself.opt_title = t_optif n_opt :sysOptionPool.add_optset(n_opt,self)# 獲取選項名稱def get_name(self,id) :dt_id = self.get_dict().get(id)if dt_id == None :return '-'return dt_id.get('name')# 獲取選項格式串(廢棄保留)def id_format(self,id):dt_id = self.get_dict().get(id)if dt_id == None :return '-'idname = dt_id.get('name')if isinstance(id, str) :return id + '_' + idnameelse :return str(id) + '_' + idname# 獲取選項映射def get_map(self):itemlist = {}d_opt = self.get_dict()if (d_opt == None) :return Nonefor (k,v) in d_opt.items():itemlist[k] = v['name']return itemlist# 獲取選項列表def get_list(self,**kwargs) :itemlist = []d_opt = self.get_dict()if (d_opt == None) :return Nonefor (k,v) in d_opt.items():item = [k,v['name'],v['pid'],0]itemlist.append(item)f_sort = kwargs.get('sort')if f_sort == None or f_sort == False:return itemlistreturn sorted(itemlist)# 數據裝載def load(self,dbclass,fdinf):logging.debug('Load Table Tree Options %s....' % self.opt_name)fdkey = fdinf.get('key')fdname = fdinf.get('name')fdvalue = fdinf.get('value')fdparent = fdinf.get('parent')filtstr = fdinf.get('filter')try :rows = db.session.query(dbclass).filter(text(filtstr)).all()opt_info = {}for irow in rows:valkey = getattr(irow,fdkey)valname = getattr(irow,fdname)valparent = getattr(irow,fdparent)opt_info[valkey] = dict(name=valname,pid=valparent)if (fdvalue != None) :valvalue = getattr(irow,fdvalue)opt_info[valkey]['value'] = valvalueself.opt_dict = opt_infoexcept SQLAlchemyError as e:logging.debug('裝入樹型選項集合[%s]數據庫讀取失敗!!%s' % (self.opt_name,str(e.orig)))return 0return 1# 獲取選項集合def get_option(self,optcat=None) :return self.get_tree()# 獲取選項樹型def get_tree(self) :item_opt = self.get_list(sort=True)if item_opt == None:return Noneif hasattr(self,'fdinf') :root_id = self.fdinf.get('rootid')else :root_id = 0#logging.debug('item_opt: %s' % item_opt)item_tree = self.build_tree(item_opt,root_id,0)return item_tree# 生成選項樹型# 基于Layui-Tree接口格式生成數據def build_tree(self,data,p_id,level=0):tree = []for item in data:if item[2] !=p_id:continuerow = dict(id = item[0], title= item[1], parent_id=item[2],level= level)if level==0:row['spread'] = Truechild = self.build_tree(data, row['id'], level+1)row['children'] = []if child:row['children'] += childtree.append(row)return tree
? ? ? ? 通過加載函數load()完成條目字典的原始數據結構加載,其數據結構為{id111:{name:xxx,pid:xxx,value:xxx},id222:{...},...}。之后由原始數據結構為基礎,然后通過一系列函數完成數據服務,主要包括:
get_name():根據鍵值取名稱
id_format():生成格式化的選項展示(后臺生成展示項時用)
get_map():生成鍵值map映射表
get_list():生成鍵值列表
get_tree():生成樹型選項集合
get_option():對外提供的統一的選項集合接口
? ? ? ? 上述功能實現后,統一選項集合數據服務類就基本完成了。下面是幾個具體的選項集合實例。
# 系統用戶角色選項處理
optOprRole = List_OptionSet(Role,{'key': 'role_cd','name' : 'rolename','filter' : 'status=0 and roletype=0'},'OprRole')#機構樹型選項處理
optBranchTree = Tree_OptionSet(Branchs,{'key': 'id','name' : 'short_name','parent' : 'parent_id','rootid' : 0,'filter' : 'status=0'},'BranchTree')#機構狀態
optBranchStatus = OptionSet({0:'正常',1:'停用',9:'廢棄'},'BranchStatus')
????????前端通過服務請求可以下載選項集合數據,并按個性化的需求對前端組件進行渲染。比如對layui-form中已經有的select、checkbox進行功能增強,也可以自行實現單選樹型、多選樹型等復合選擇域,從而將layui-form的功能再次提升。
? ? ? ??