说明

我也很想一次把数据表结构都设计好

事实证明,唯一不变的就是变化。本篇讨论如何比较灵活的增加Flask的数据的数据对象以应对各种变化。

以新增面对变更

1 不要外键

外键会使得删表比较麻烦,因此我不建议使用外键。当然有少部分特别固定的关系表可以用外键(例如用户角色、用户表)。

2 用jinja模板创建新的对象

创建数据库时字段名是个特别烦人的东西,因此使用jinja模板
datamodel.j2:将固定的变量先创建好,其他的用for循环传入。

class {{classname}}(db.Model):
    __tablename__='{{classname|lower}}'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    {%for str_var in str_var_list%}{{str_var}}= db.Column(db.String(32))
    {%endfor%}
    {%for num_var in num_var_list%}{{num_var}}= db.Column(db.Float(precision=2))
    {%endfor%}
    {%for dt_var in dt_var_list%}{{dt_var}}= db.Column(db.DateTime())
    {%endfor%}

对应的python,将新的变量传入

import DataManipulation as dm 
# 不传空列表模板参数不处理,不会出错
# report
classname = 'report'
str_var_list = ['report_id','description','tier1', 'tier2', 'tier3']
num_var_list = []
dt_var_list = ['create_time','update_time']
content = dm.gen_by_j2(template_name='datamodel.j2', classname=classname, 
                       str_var_list=str_var_list, num_var_list=num_var_list, dt_var_list=dt_var_list)
with open('tem.py', 'w') as f:
    f.write(content)

tem.py ,这部分是结果

class report(db.Model):
    __tablename__='report'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    varname= db.Column(db.String(32))
    description= db.Column(db.String(32))
    tag= db.Column(db.String(32))
    
    ref_low= db.Column(db.Float(precision=2))
    ref_high= db.Column(db.Float(precision=2))
    ord= db.Column(db.Float(precision=2))
    
    create_time= db.Column(db.DateTime())
    update_time= db.Column(db.DateTime())

3 使用shell直接创建新对象

使用manage包装后, 可以在终端中启动shell

python3 manager.py shell

在打开的ipython中导入新表(对象),并执行create_all

from app import db
... # 老对象不用清除,不会对已存在的表重新创建
from app.datamodel import report
import dateutil.parser
import pytz
import pandas as pd
import DataManipulation as dm
from collections import OrderedDict
from datetime import datetime

'''
如果做好了迁移可以db.drop_all()再db.create_all()。否则只能create_all()
'''
# 本机上drop all ,服务器上不要
# db.drop_all()
# 新建表
db.create_all()
print('* db create all tables defined in app.datamodel')

4 使用pymysql从外部导入数据

import pandas as pd
import pymysql as pyl
import numpy as np
import DataManipulation as dm
from datetime import datetime

env = 'local'
if env.lower() == 'local':
    host = '111.111.111.111'
else:
    host = '222.222.222.222'

cfg_mysql = {
    "host": host,
    "port": 1111,
    "user": "aaaa",
    "password": "aaaaaaa",
    "db": "aaaaaaa"
}

connection = pyl.connect(**cfg_mysql)
with connection.cursor() as cursor:
    for i in range(2000, 2030):
        tem_sql = "insert into base8v2_reporttype(name, report_id, description,tier1,tier2,tier3, create_time, update_time) values('%s','%s','%s','%s','%s','%s','%s','%s')" % (
            str(i) + 'report', 'base8_' + str(i) ,'test report','basic', 'fen', str(i), dt_str, dt_str)
        cursor.execute(tem_sql)
    connection.commit()
connection.close()

注意connect没有用try的方法,如果插入语句出问题,可能连接要手动关闭(否则影响其他终端例如navicat的操作)

5 使用静态导入表在内部使用(弥补前面不加外键的操作)

因为没有加外键,那么在就在视图开始启动时读入静态表(通常都是映射表,很小)
例如:

report_df = sql_read_table('report')
# 将原始变量名映射为数据库字段名
varname_mapping_dict = dict(zip(list(report_df['name']), list(report_df['varname'])))

这样一个表对象的追加就结束了,根据新表对应的增加视图函数就可以了。

6 续

  • sqlalchemy对象在解析时会有点麻烦,所以我们给数据对象加点料,让其可以直接通过dict函数转换为字典

6.1 重新构建jinja模板

data_model1.j2

class {{classname}}(db.Model):
    __tablename__='{{classname|lower}}'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    {%for str_var in str_var_list%}{{str_var}}= db.Column(db.String(32))
    {%endfor%}
    {%for num_var in num_var_list%}{{num_var}}= db.Column(db.Float(precision=2))
    {%endfor%}
    {%for dt_var in dt_var_list%}{{dt_var}}= db.Column(db.DateTime())
    {%endfor%}    
    {#上面部分不动下面加料#}
    def keys(self):
        return ({%for var in all_var_list%}{%if not loop.first%},{%endif%}'{{var}}'{%endfor%})
    def __getitem__(self, item):
        return getattr(self, item)

6.2 对应修改一下python

classname = 'report'
default_var_list = ['id', 'name']
str_var_list = ['report_id', 'description', 'tier1', 'tier2', 'tier3']
num_var_list = []
dt_var_list = ['create_time', 'update_time']

all_var_list = default_var_list + str_var_list + num_var_list + dt_var_list

content = dm.gen_by_j2(template_name='datamodel1.j2', classname=classname,
                       str_var_list=str_var_list, num_var_list=num_var_list, dt_var_list=dt_var_list, all_var_list=all_var_list)
with open('tem.py', 'w') as f:
    f.write(content)

6.3 结果(将其拷贝到网站的数据对象定义下面)

tem.py (流程顺畅后也可以直接python文件读取存到report下面)

class report(db.Model):
    __tablename__='report'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    report_id= db.Column(db.String(32))
    description= db.Column(db.String(32))
    tier1= db.Column(db.String(32))
    tier2= db.Column(db.String(32))
    tier3= db.Column(db.String(32))
    
    
    create_time= db.Column(db.DateTime())
    update_time= db.Column(db.DateTime())
        
    
    def keys(self):
        return (id,name,report_id,description,tier1,tier2,tier3,create_time,update_time)
    def __getitem__(self, item):
        return getattr(self, item)

再之后通过 pyhton3 manager.py shell 中造作创建表。