Flask+LayUI开发手记(十一):选项集合的数据库扩展类

        条目较少的选项集合,确实可以在程序中直接定义(其实最合适的还是存储在一个分类别的数据库表里),但条目较多的选项集合,或者是复杂的树型结构选项集合,一般都是存储在数据库中的,这样维护起来比较方便,也容易迁移和共享。所以,在完成基础选项集合框架后,考虑应用的方便性,面向数据库存储的选项集合扩展类就成了下一步开发的任务。

        有了基础类做底,这些实现扩展起来也是十分方便。基于数据库存储的类包括两大类型,一种是列表类,这个就是基础选项类的扩展,数据结构没有区别,只是多了选项条目从数据库中装载到内存中的过程,另一种是树型类,它的数据结构比列表类复杂了一些,数据库里也是以父子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 = dbclass
        self.fdinf = fdinf
        self.opt_name = n_opt
        self.opt_title = t_opt
        if 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 None
        return 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] = valname
            self.opt_dict = opt_info
        except SQLAlchemyError as e:
            logging.debug('装入选项集合[%s]数据库读取失败!!%s' % (self.opt_name,str(e.orig)))
            return 0
        return 1

    #数据重载函数
    def reload(self) :
        rtncode = self.load(self.dbclass,self.fdinf)
        if rtncode == 0 :
            return 0
        if 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 = dbclass
        self.fdinf = fdinf
        self.opt_name = n_opt
        self.opt_title = t_opt
        if 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 + '_' + idname
        else :
            return str(id) + '_' + idname

    # 获取选项映射
    def get_map(self):
        itemlist = {}
        d_opt = self.get_dict()
        if (d_opt == None) :
            return None
        for (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 None
        for (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 itemlist
        return 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'] = valvalue
            self.opt_dict = opt_info
        except SQLAlchemyError as e:
            logging.debug('装入树型选项集合[%s]数据库读取失败!!%s' % (self.opt_name,str(e.orig)))
            return 0
        return 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 None
        if 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:
                continue
            row = dict(id = item[0], title= item[1], parent_id=item[2],level= level)
            if level==0:
                row['spread'] = True
            child = self.build_tree(data, row['id'], level+1)
            row['children'] = []
            if child:
                row['children'] += child
            tree.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的功能再次提升。

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值