import random import time from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_babel import gettext as __ from flask_babel import lazy_gettext as _ from flask_babel import lazy_gettext import copy from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker from flask_appbuilder.actions import action import os from flask import jsonify, make_response from sqlalchemy import or_ from wtforms.validators import DataRequired, Length, Regexp from myapp import app, appbuilder,db from wtforms import BooleanField, StringField, SelectField from flask_appbuilder.fieldwidgets import BS3TextFieldWidget, Select2Widget from myapp.forms import MySelect2Widget, MyBS3TextAreaFieldWidget from .baseApi import MyappModelRestApi from flask import ( abort, flash, g, Markup, redirect, request ) from .base import MyappFilter from myapp.models.model_dimension import Dimension_table from flask_appbuilder import expose import pysnooper,datetime, json conf = app.config logging = app.logger class Dimension_table_Filter(MyappFilter): # @pysnooper.snoop() def apply(self, query, func): if g.user.is_admin(): return query.filter(self.model.status==1) return query.filter(self.model.status==1).filter( or_( self.model.owner.contains(g.user.username), self.model.owner.contains('*'), ) ) Metadata_column_fields = { "name":StringField( label=_("列名"), description='列名(小写字母、数字、_ 组成),最长50个字符', default='', widget=BS3TextFieldWidget(), validators=[Regexp("^[a-z][a-z0-9_]*[a-z0-9]$"), Length(1, 54),DataRequired()] ), "describe": StringField( label=_('列描述'), description='列名描述', default='', widget=BS3TextFieldWidget(), validators=[DataRequired()] ), "column_type": SelectField( label=_('字段类型'), description='列类型', widget=Select2Widget(), default='text', choices=[['int', 'int'], ['text', 'text'],['date', 'date'],['double','double'],['enum','enum']], validators=[DataRequired()] ), "unique": BooleanField( label=_('是否唯一'), description='是否唯一', default=False, widget=BS3TextFieldWidget(), ), "nullable": BooleanField( label=_('是否可为空'), description='是否可为空', default=True, widget=BS3TextFieldWidget(), ), "primary_key": BooleanField( label=_('是否为主键'), description='是否为主键', default=False, widget=BS3TextFieldWidget(), ), "choices": StringField( label=_('可选择项'), description='enum类型时,逗号分割多个可选择项,为空则为数据库记录已存在可选择项', default='', widget=BS3TextFieldWidget(), ) } @pysnooper.snoop() def ddl_hive_external_table(table_id): try: item = db.session.query(Dimension_table).filter_by(id=int(table_id)).first() if not item: return cols = json.loads(item.columns) # 创建hive外表 hive_type_map = {'INT': 'BIGINT', 'TEXT': 'STRING', 'STRING': 'STRING', 'DATE': 'STRING','ENUM':'STRING'} cols_lst = [] for col_name in cols: if col_name in ['id',]: continue column_type = cols[col_name].get('column_type', 'text').upper() if column_type not in hive_type_map: raise RuntimeError("更新了不支持新字段类型") column_type = hive_type_map[column_type] col_str = col_name + ' ' + column_type cols_lst.append(col_str) columns_sql = ',\n'.join(cols_lst).strip(',') import sqlalchemy.engine.url as url uri = url.make_url(item.sqllchemy_uri if item.sqllchemy_uri else default_uri) hive_sql = ''' # hive建外表 CREATE EXTERNAL TABLE IF NOT EXISTS {table_name} ( id BIGINT, {columns_sql} ) with (ip='{ip}',port='{port}',db_name='{pg_db_name}',user_name='{user_name}',pwd='{password}',table_name='{pg_table_name}',charset='utf8',db_type='pg'); '''.format( table_name=item.table_name, columns_sql=columns_sql, ip=uri.host, port=str(uri.port), user_name=uri.username, password=uri.password, pg_db_name=uri.database, pg_table_name=item.table_name ) return hive_sql except Exception as e: print(e) return str(e) default_uri='mysql+pymysql://your_username:your_password@your_host:port/your_db' class Dimension_table_ModelView_Api(MyappModelRestApi): datamodel = SQLAInterface(Dimension_table) label_title = '维表' route_base = '/dimension_table_modelview/api' base_permissions = ['can_add','can_list','can_delete','can_show','can_edit'] add_columns = ['sqllchemy_uri','app','table_name','label','describe','owner','columns'] edit_columns = add_columns show_columns = ['id','app','sqllchemy_uri','label','describe','table_name','owner','columns','status'] search_columns=['id','app','table_name','label','describe','sqllchemy_uri'] order_columns = ['id'] base_order = ('id', 'desc') list_columns = ['table_html','label','owner','describe','operate_html'] cols_width = { "table_html":{"type": "ellip2", "width": 300}, "label":{"type": "ellip2", "width": 300}, "owner": {"type": "ellip2", "width": 300}, "describe": {"type": "ellip2", "width": 300}, "operate_html":{"type": "ellip2", "width": 400} } spec_label_columns = { "sqllchemy_uri":"链接串地址", "owner":"负责人", "columns": "列信息", "table_html":"表名", "table_name":"表名" } base_filters = [["id", Dimension_table_Filter, lambda: []]] add_fieldsets = [ ( lazy_gettext('表元数据'), {"fields": ['sqllchemy_uri','app','table_name','label','describe','owner'], "expanded": True}, ), ( lazy_gettext('列信息'), {"fields": ['columns'], "expanded": True}, ) ] edit_fieldsets = add_fieldsets add_form_extra_fields = { "sqllchemy_uri":StringField( _(datamodel.obj.lab('sqllchemy_uri')), default="", description='链接串地址:
示例:mysql+pymysql://$账号:$密码@$ip:$端口/$库名?charset=utf8
示例:postgresql+psycopg2://$账号:$密码@$ip:$端口/$库名', widget=BS3TextFieldWidget(), validators=[DataRequired(),Regexp("^(mysql\+pymysql|postgresql\+psycopg2)")] ), "table_name":StringField( label=_(datamodel.obj.lab('table_name')), description='远程数据库的表名', widget=BS3TextFieldWidget(), default='', validators=[DataRequired(),Regexp("^[a-z][a-z0-9_\-]*[a-z0-9]$")] ), "label": StringField( label=_(datamodel.obj.lab('label')), description='中文名', widget=BS3TextFieldWidget(), default='', validators=[DataRequired()] ), "describe": StringField( label=_(datamodel.obj.lab('describe')), description='描述', widget=BS3TextFieldWidget(), default='', validators=[DataRequired()] ), "app":SelectField( label=_(datamodel.obj.lab('app')), description='产品分类', widget=MySelect2Widget(can_input=True,conten2choices=True), default='', choices=[[x,x] for x in ['产品1',"产品2","产品3"]], validators=[DataRequired()] ), "columns":StringField( label='字段信息', description='维表字段信息,必须包含自增主键列,例如id', widget=MyBS3TextAreaFieldWidget(expand_filed=Metadata_column_fields) ), "owner": StringField( label=_(datamodel.obj.lab('owner')), default='', description='责任人,逗号分隔的多个用户', widget=BS3TextFieldWidget(), validators=[DataRequired()] ), } edit_form_extra_fields = add_form_extra_fields def pre_add(self, item): if not item.columns: item.columns='{}' sqllchemy_uri = item.sqllchemy_uri if item.sqllchemy_uri else default_uri if item.columns: # 如果没有主键列就自动加上row主键列 cols = json.loads(item.columns) for col_name in cols: if cols[col_name].get('primary_key',False): return if 'postgresql' in sqllchemy_uri: cols['rowid']= { "column_type": "int", "describe": "主键", "name": "rowid", "nullable": False, "primary_key": True, "unique": True } if 'mysql' in sqllchemy_uri: cols['id']= { "column_type": "int", "describe": "主键", "name": "id", "nullable": False, "primary_key": True, "unique": True } # 对于罗盘维表,不允许有自定义主键 if not sqllchemy_uri or sqllchemy_uri==default_uri: for col_name in cols: if cols[col_name].get("primary_key",False) and col_name!='rowid': cols[col_name]["primary_key"]=False item.columns=json.dumps(cols,indent=4,ensure_ascii=False) if not item.owner or g.user.username not in item.owner: item.owner = g.user.username if not item.owner else item.owner + "," + g.user.username flash('添加或修改字段类型,需要点击"更新远程表",以实现在远程数据库上建表','warning') def pre_update(self, item): if not item.sqllchemy_uri: item.sqllchemy_uri=self.src_item_json.get('sqllchemy_uri','') self.pre_add(item) # 更新以后表结构会变 all_dimension = conf.get('all_dimension_instance', {}) if "dimension_%s" % item.id in all_dimension: del all_dimension["dimension_%s" % item.id] # 转换为前端list def pre_show_res(self,_response): data = _response['data'] columns=json.loads(data.get('columns','{}')) columns_list=[] for name in columns: col = columns[name] col.update({"name":name}) columns_list.append(col) data['columns']=columns_list # 添加或者更新前将前端columns list转化为字段存储 def pre_add_req(self, req_json=None): if req_json and 'columns' in req_json: columns={} for col in req_json.get('columns',[]): columns[col['name']]=col req_json['columns'] = json.dumps(columns,indent=4,ensure_ascii=False) return req_json pre_update_req=pre_add_req # 获取指定维表里面的数据 @staticmethod def get_dim_target_data(dim_id): import pandas dim = db.session.query(Dimension_table).filter_by(id=int(dim_id)).first() import sqlalchemy.engine.url as url uri = url.make_url(dim.sqllchemy_uri if dim.sqllchemy_uri else default_uri) sql_engine = create_engine(uri) sql = 'select * from %s' % (dim.table_name,) results = pandas.read_sql_query(sql, sql_engine) return results.to_dict() @expose("/external/", methods=["GET"]) def external(self,dim_id): ddl_sql = ddl_hive_external_table(dim_id) print(ddl_sql) return Markup(ddl_sql.replace('\n','
')) # @expose("/clear/", methods=["GET"]) @action("clear", __("清空"), __("Delete all Really?"), "fa-trash", single=True) def delete_all(self,items): if not items: abort(404) dim_id='' try: for dim in items: dim_id = dim.id import sqlalchemy.engine.url as url uri = url.make_url(dim.sqllchemy_uri if dim.sqllchemy_uri else default_uri) engine = create_engine(uri) dbsession = scoped_session(sessionmaker(bind=engine)) dbsession.execute('TRUNCATE TABLE %s;'%dim.table_name) dbsession.commit() dbsession.close() flash('清空完成','success') except Exception as e: flash('清空失败:'+str(e), 'error') url_path = conf.get('MODEL_URLS', {}).get("dimension")+'?targetId='+dim_id return redirect(url_path) @expose("/create_external_table/", methods=["GET"]) # @pysnooper.snoop() def create_external_table(self, dim_id): item = db.session.query(Dimension_table).filter_by(id=int(dim_id)).first() sqllchemy_uri = item.sqllchemy_uri if item.sqllchemy_uri else default_uri if sqllchemy_uri: # 创建数据库的sql(如果数据库存在就不创建,防止异常) if 'postgresql' in sqllchemy_uri: # 创建pg表 import sqlalchemy.engine.url as url uri = url.make_url(sqllchemy_uri) from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker engine = create_engine(uri) dbsession = scoped_session(sessionmaker(bind=engine)) cols = json.loads(item.columns) table_schema = 'public' import pandas as pd read_col_sql = r"select column_name from information_schema.columns where table_schema='%s' and table_name='%s' "%(table_schema,item.table_name) print(read_col_sql) company_data = pd.read_sql(read_col_sql,con=engine) # 如果表不存在 sql='' if company_data.empty: # 如果远程没有表,就建表 sql = ''' CREATE TABLE if not exists {table_name} ( id BIGINT PRIMARY KEY, {columns_sql} ); '''.format( table_name=item.table_name, columns_sql='\n'.join( [" %s %s %s %s," % (col_name, 'BIGINT' if cols[col_name].get('column_type','text').upper() == 'INT' else 'varchar(2000)', '' if int(cols[col_name].get('nullable', True)) else 'NOT NULL', '' if not int(cols[col_name].get('unique', False)) else 'UNIQUE') for col_name in cols if col_name not in ['id',]] ).strip(',') ) # 执行创建数据库的sql print(sql) if sql: dbsession.execute(sql) dbsession.commit() flash('创建新表成功', 'success') else: exist_columns=list(company_data.head().to_dict()['column_name'].values()) print(exist_columns) if exist_columns: col = json.loads(item.columns) for column_name in col: col_type = 'INT' if col[column_name].get('column_type','text').upper() == 'INT' else 'varchar(2000)' if column_name not in exist_columns: try: sql = 'ALTER TABLE %s ADD %s %s;'%(item.table_name,column_name,col_type) print(sql) dbsession.execute(sql) dbsession.commit() flash('增加新字段成功', 'success') except Exception as e: dbsession.rollback() print(e) flash('增加新字段失败:'+str(e), 'error') dbsession.close() # 如果远程有表,就增加字段 # 创建数据库的sql(如果数据库存在就不创建,防止异常) if 'mysql' in sqllchemy_uri: # 创建mysql表 import sqlalchemy.engine.url as url uri = url.make_url(sqllchemy_uri) from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker engine = create_engine(uri) dbsession = scoped_session(sessionmaker(bind=engine)) cols = json.loads(item.columns) import sqlalchemy try: table = sqlalchemy.Table(item.table_name, sqlalchemy.MetaData(), autoload=True, autoload_with=engine) exist_columns=[str(col).replace(item.table_name,'').replace('.','') for col in table.c] print(exist_columns) if exist_columns: col = json.loads(item.columns) for column_name in col: col_type = 'varchar(2000)' if col[column_name].get('column_type', 'text').upper() == 'INT': col_type = 'INT' if col[column_name].get('column_type', 'text').upper() in ['DOUBLE','FLOAT']: col_type = 'DOUBLE' if column_name not in exist_columns: try: sql = 'ALTER TABLE %s ADD %s %s;'%(item.table_name,column_name,col_type) print(sql) dbsession.execute(sql) dbsession.commit() flash('增加新字段成功', 'success') except Exception as e: dbsession.rollback() print(e) flash('增加新字段失败:'+str(e), 'error') except sqlalchemy.exc.NoSuchTableError: print('表不存在') # 如果表不存在 # 如果远程没有表,就建表 sql = ''' CREATE TABLE if not exists {table_name} ( id BIGINT PRIMARY KEY AUTO_INCREMENT, {columns_sql} ); '''.format( table_name=item.table_name, columns_sql='\n'.join( [" %s %s %s %s," % (col_name, 'BIGINT' if cols[col_name].get('column_type','text').upper() == 'INT' else 'DOUBLE' if cols[col_name].get('column_type','text').upper() in ['DOUBLE','FLOAT'] else 'varchar(2000)', '' if int(cols[col_name].get('nullable', True)) else 'NOT NULL', '' if not int(cols[col_name].get('unique', False)) else 'UNIQUE') for col_name in cols if col_name not in ['id',]] ).strip(',') ) # 执行创建数据库的sql print(sql) if sql: dbsession.execute(sql) dbsession.commit() flash('创建新表成功','success') dbsession.close() # 如果远程有表,就增加字段 all_dimension = conf.get('all_dimension_instance', {}) if "dimension_%s"%dim_id in all_dimension: del all_dimension["dimension_%s"%dim_id] url_path = conf.get('MODEL_URLS', {}).get("dimension")+'?targetId='+dim_id return redirect(url_path) def add_more_info(self,response,**kwargs): from myapp.views.baseApi import API_RELATED_RIS_KEY, API_ADD_COLUMNS_RES_KEY, API_EDIT_COLUMNS_RES_KEY for col in response[API_ADD_COLUMNS_RES_KEY]: if col['name']=='columns': response[API_EDIT_COLUMNS_RES_KEY].remove(col) for col in response[API_EDIT_COLUMNS_RES_KEY]: if col['name'] == 'columns': response[API_EDIT_COLUMNS_RES_KEY].remove(col) response[API_ADD_COLUMNS_RES_KEY].append({ "name": "columns", "ui-type": "list", "info": self.columnsfield2info(Metadata_column_fields) }) response[API_EDIT_COLUMNS_RES_KEY].append({ "name": 'columns', "ui-type": "list", "info": self.columnsfield2info(Metadata_column_fields) }) appbuilder.add_api(Dimension_table_ModelView_Api) from flask_appbuilder import Model from myapp.models.base import MyappModelBase from sqlalchemy import Column, Integer, String, ForeignKey, Float,BigInteger,Date from sqlalchemy.orm import relationship class Dimension_remote_table_ModelView_Api(MyappModelRestApi): datamodel = SQLAInterface(Dimension_table) route_base = '/dimension_remote_table_modelview' # @pysnooper.snoop() def set_model(self,dim_id): dim = db.session.query(Dimension_table).filter_by(id=int(dim_id)).first() if not dim: raise Exception("no dimension") all_dimension = conf.get('all_dimension_instance',{}) if "dimension_%s"%dim_id not in all_dimension: columns = json.loads(dim.columns) if dim.columns else {} column_class = {} spec_label_columns = {} search_columns = [] label_columns={} add_columns=[] edit_columns=[] show_columns=[] list_columns=[] description_columns={} add_form_extra_fields={} add_form_query_rel_fields={} validators_columns={} order_columns=[] cols_width = { } for column_name in columns: column_type = columns[column_name].get('column_type','string') if column_type == 'int': cols_width[column_type] ={ "type": "ellip1", "width": 100 } if column_type == 'double': cols_width[column_type] ={ "type": "ellip1", "width": 100 } if column_type == 'date': cols_width[column_type] ={ "type": "ellip1", "width": 200 } if column_type == 'text': cols_width[column_type] ={ "type": "ellip2", "width": 300 } if column_type == 'enum': cols_width[column_type] ={ "type": "ellip2", "width": 200 } column_sql_type = BigInteger if column_type == 'int' else String # 因为实际使用的时候,会在数据库中存储浮点数据,通用性也更强 val=[DataRequired()] if not columns[column_name].get('nullable',True) else [] if column_type =='date': column_sql_type=String add_form_extra_fields[column_name] = StringField( _(column_name), default=datetime.datetime.now().strftime('%Y-%m-%d'), description='', # columns[column_name]['describe'], widget=BS3TextFieldWidget(), validators=[Regexp("^[0-9]{4,4}-[0-9]{2,4}-[0-9]{2,2}$")]+val ) elif column_type =='enum': column_sql_type=String add_form_extra_fields[column_name] = SelectField( _(column_name), default='', description='', widget=MySelect2Widget(can_input=True,conten2choices=False if columns[column_name].get('choices','') else True), choices=[[x,x] for x in columns[column_name].get('choices','').split(',')] ) else: add_form_extra_fields[column_name] = StringField( _(column_name), default='', description='', # columns[column_name]['describe'], widget=BS3TextFieldWidget(), validators=val ) column_class[column_name] = Column( column_sql_type, nullable=columns[column_name].get('nullable',True), unique=columns[column_name].get('unique',False), primary_key=columns[column_name].get('primary_key',False) ) spec_label_columns[column_name] = columns[column_name]['describe'] label_columns[column_name]=columns[column_name]['describe'] description_columns[column_name] = columns[column_name]['describe'] if not int(columns[column_name].get('primary_key',False)): add_columns.append(column_name) show_columns.append(column_name) if not int(columns[column_name].get('primary_key', False)): list_columns.append(column_name) if column_type == 'string' or column_type=='text' or column_type=='int' or column_type=='enum': if not int(columns[column_name].get('primary_key',False)): search_columns.append(column_name) # if column_type == 'int': order_columns.append(column_name) bind_key = 'dimension_%s' % dim.id # SQLALCHEMY_BINDS = conf.get('SQLALCHEMY_BINDS', {}) # for key in SQLALCHEMY_BINDS: conf['SQLALCHEMY_BINDS'][bind_key] = dim.sqllchemy_uri if dim.sqllchemy_uri else default_uri # if dim.sqllchemy_uri in SQLALCHEMY_BINDS[key]: # bind_key=key # break # model 类 model_class = type( "Dimension_Model_%s" % dim.id, (Model, MyappModelBase), dict( __tablename__=dim.table_name, __bind_key__=bind_key if bind_key else None, **column_class ) ) # 页面视图 url = '/dimension_remote_table_modelview/%s/api/' % dim_id print(url) # 预处理一下 # @pysnooper.snoop(watch_explode=('item')) def pre_add(self,item): # 浮点转型 for key in self.cols: if key in ['id','rowid']: continue if self.cols[key].get('column_type', 'text') == 'int': try: setattr(item,key,int(getattr(item,key))) except Exception: setattr(item,key,None) if self.cols[key].get('column_type', 'text') == 'double': try: setattr(item,key,float(getattr(item,key))) except Exception: setattr(item, key, None) def pre_update(self,item): # 浮点转型 for key in self.cols: if key in ['id','rowid']: continue if self.cols[key].get('column_type', 'text') == 'int': try: setattr(item,key,int(getattr(item,key))) except Exception: setattr(item,key,None) if self.cols[key].get('column_type', 'text') == 'double': try: setattr(item,key,float(getattr(item,key))) except Exception: setattr(item, key, None) def get_primary_key(cols): for name in cols: if cols[name].get('primary_key',False): return name return '' @expose("/upload/", methods=["POST"]) # @pysnooper.snoop(watch_explode=('attr')) def upload(self): csv_file = request.files.get('csv_file') # FileStorage dim = db.session.query(Dimension_table).filter_by(id=int(self.dim_id)).first() # 文件保存至指定路径 i_path = csv_file.filename if os.path.exists(i_path): os.remove(i_path) csv_file.save(i_path) # 读取csv,读取header,按行处理 import csv csv_reader = csv.reader(open(i_path, mode='r', encoding='utf-8-sig')) header = None result = [] cols = json.loads(dim.columns) error_message = [] for line in csv_reader: if not header: header = line # 判断header里面的字段是否在数据库都有 for col_name in header: # attr = self.datamodel.obj if not hasattr(self.datamodel.obj, col_name): flash('csv首行header与数据库字段不对应', 'warning') back = { "status": 1, "result": [], "message": "csv首行header与数据库字段不对应" } return self.response(400, **back) continue # 个数不对的去掉 if len(line)!=len(header): continue # 全是空值的去掉 ll = [l.strip() for l in line if l.strip()] if not ll: continue data = dict(zip(header, line)) try: # 把整型做一下转换,因为文件离线全部识别为字符串 for key in copy.deepcopy(data): try: if cols.get(key,{}).get('column_type','text')=='int': data[key]=int(data[key]) elif cols.get(key,{}).get('column_type','text')=='double': data[key]=float(data[key]) if data[key] else None else: data[key]=str(data[key]).replace('\n',' ') except Exception as e: print(e) data[key] = None model = self.datamodel.obj(**data) self.pre_add(model) db.session.add(model) self.post_add(model) db.session.commit() result.append('success') # except SQLAlchemyError as ex: # db.session.rollback() except Exception as e: db.session.rollback() print(e) error_message.append(str(e)) result.append(str(e)+"-----------") # flash('成功导入%s行,失败导入%s行' % (len([x for x in result if x == 'success']), len([x for x in result if x == 'fail'])), 'success') # flash('上传失败%s'%error_message,'error') # back = { # "status": 0, # "result": result, # "message": "result为上传成功行,共成功%s" % len([x for x in result if x == 'success']) # } # return self.response(200, **back) message = '成功导入%s行,失败导入%s行' % (len([x for x in result if x == 'success']), len([x for x in result if x != 'success'])) message += ','.join(result) message=Markup(message) return make_response(message,200) @action("muldelete", __("Delete"), __("Delete all Really?"), "fa-trash", single=False) # @pysnooper.snoop(watch_explode=('items')) def muldelete(self, items): if not items: abort(404) success = [] fail = [] for item in items: try: self.pre_delete(item) db.session.delete(item) success.append(item.to_json()) except Exception as e: flash(str(e), "danger") fail.append(item.to_json()) db.session.commit() return json.dumps( { "success": success, "fail": fail }, indent=4, ensure_ascii=False ) @action("copy_row", __("Copy"), __("复制所选记录 all Really?"), "fa-trash", single=False) # @pysnooper.snoop(watch_explode=('items')) def copy_row(self, items): if not items: abort(404) success = [] fail = [] for item in items: try: req_json = item.to_json() if 'id' in req_json: del req_json["id"] json_data = self.pre_add_req(req_json) new_item = self.add_model_schema.load(json_data) self.pre_add(new_item.data) self.datamodel.add(new_item.data, raise_exception=True) self.post_add(new_item.data) result_data = self.add_model_schema.dump(new_item.data, many=False).data success.append(item.to_json()) except Exception as e: flash(str(e), "danger") fail.append(item.to_json()) db.session.commit() return json.dumps( { "success": success, "fail": fail }, indent=4, ensure_ascii=False ) view_class = type( "Dimension_%s_ModelView_Api"%dim.id, (MyappModelRestApi,), dict( datamodel=SQLAInterface(model_class,session=db.session), route_base=url, add_form_extra_fields=add_form_extra_fields, edit_form_extra_fields=add_form_extra_fields, spec_label_columns=spec_label_columns, search_columns=search_columns, order_columns=order_columns, add_columns=add_columns, label_title = dim.label, base_permissions = ['can_list','can_add','can_delete','can_edit','can_show'], pre_add=pre_add, pre_update=pre_update, upload=upload, muldelete=muldelete, copy_row=copy_row, dim_id=dim_id, import_data=True, download_data=True, cols_width=cols_width, base_order=(get_primary_key(columns), "desc") if get_primary_key(columns) else None, cols = columns ) ) view_instance = view_class() view_instance._init_model_schemas() all_dimension["dimension_%s"%dim_id]=view_instance return all_dimension["dimension_%s"%dim_id] @expose("//api/_info", methods=["GET"]) def dim_api_info(self,dim_id, **kwargs): view_instance = self.set_model(dim_id) return view_instance.api_info(**kwargs) @expose("//api/", methods=["GET"]) def dim_api_show(self, dim_id,pk, **kwargs): view_instance = self.set_model(dim_id) return view_instance.api_show(pk,**kwargs) @expose("//api/", methods=["GET"]) def dim_api_list(self,dim_id, **kwargs): view_instance = self.set_model(dim_id) try: return view_instance.api_list(**kwargs) except Exception as e: print(e) flash(Markup(str(e)),'error') return jsonify({ "status":1, "message":str(e), "result":"" }) @expose("//api/", methods=["POST"]) def dim_api_add(self,dim_id): view_instance = self.set_model(dim_id) try: return view_instance.api_add() except Exception as e: print(e) flash(Markup(str(e)),'error') return jsonify({ "status":1, "message":str(e), "result":"" }) @expose("//api/", methods=["PUT"]) # @pysnooper.snoop(watch_explode=('item','data')) def dim_api_edit(self, dim_id,pk): view_instance = self.set_model(dim_id) return view_instance.api_edit(pk) @expose("//api/", methods=["DELETE"]) def dim_api_delete(self,dim_id, pk): view_instance = self.set_model(dim_id) return view_instance.api_delete(pk) @expose("//api/upload/", methods=["POST"]) # @pysnooper.snoop() def dim_api_upload(self,dim_id): view_instance = self.set_model(dim_id) return view_instance.upload() @expose("//api/download_template/", methods=["GET"]) # @pysnooper.snoop() def dim_api_download_template(self,dim_id): view_instance = self.set_model(dim_id) return view_instance.download_template() @expose("//api/download/", methods=["GET"]) # @pysnooper.snoop() def dim_api_download(self,dim_id): view_instance = self.set_model(dim_id) return view_instance.download() @expose("//api/multi_action/", methods=["POST"]) def multi_action(self,dim_id,name): view_instance = self.set_model(dim_id) return view_instance.multi_action(name) appbuilder.add_api(Dimension_remote_table_ModelView_Api)