應用公升級冷啟動場景下的資料匯入和匯出

2021-09-24 09:21:11 字數 4755 閱讀 7265

專案中遇到乙個比較棘手的問題,之前的應用是沒有migrate相關功能的。從下乙個版本起,需要實現應用的資料庫自動公升級。採用的是flask-migrate外掛程式。但是要做migrate的前提是有之前的資料庫版本檔案支撐。

最理想的情況是沒有什麼使用者資料,簡單粗暴的強制使用者重新安裝並且重新建立新的資料庫,這樣以後都可以直接呼叫migrate的相關命令進行公升級。這種做法顯然不是很負責的,雖然真的很想這麼幹。所以!作為乙個負責人的程式設計師!必須解決這樣的讓人頭大的,冷啟動問題。

第一種方案,給使用者下發migrate版本檔案,使用者沒有初始版本,那我們可以在更新包內給使用者乙個!貌似很完美的解決了這個問題。但是!使用者使用的版本,也許大概可能絕對不會只有乙個...下發哪個版本的指令碼檔案?所以該方案不完美。

第二種方案,根據資料庫和普遍orm的特徵,當模型字段少於資料庫欄位時,正常的資料庫操作不受影響。所以,我們可以在做本次公升級之前,保留使用者的全部資料。公升級建立新的資料庫和資料表,再將舊的資料全部匯入。但是!有個問題就是,當orm的字段多於資料庫時,所有的orm操作都會受到影響。假如只支援單資料庫,那麼可以通過資料庫的dumps或者類似命令去直接解決,假如支援使用者自行設定資料庫,這個問題則比較麻煩。好在問題停留在了**層,總歸是可以解決。ok,**這麼搞。

from flask_script import manager

from *** import session_maker, db

import json, os

from datetime import datetime

from sqlalchemy.exc import programmingerror, operationalerror

from modules.database import models

from ***x import g_init_cfg

def get_rows(model, columns=none):

"""獲取資料行

:param model: 模型類

:param columns: 需要獲取的列。因為**model的字段可能多於資料庫表,所以此處的列需要動態剔除不支援的列。

:return: 組裝成字典的資料

"""def inner(model, columns):

"""內部遞迴方法,因資料庫機制,每次拋錯只丟擲乙個錯誤,所以需要遞迴檢查並剔除檢查出的列。直到剩餘的column與資料庫表完全相容。

:param model: 模型類

:param columns: 需要獲取的列

:return: 資料庫資料的元組列表和最終剩餘的有效columns

"""with session_maker() as session:

if not columns:

''' 當columns為空時,預設獲取模型內所有的字段 '''

try:

''' 將字段型別轉化為sqlalchemy支援的型別,並嘗試獲取資料 '''

fields = [getattr(model, column.key) for column in columns]

rows = session.query(*fields).all()

except (programmingerror, operationalerror) as e:

''' 因為各資料庫的錯誤提示不同,根據不同的資料庫引擎分別處理不同的錯誤資訊 '''

engine = g_init_cfg.getengine()

if engine == 'sqlserver':

if 'invalid column name' in e.args[0]:

''' 當提示某字段不存在或者不支援,則記錄並從columns內剔除該欄位 '''

column_name = e.args[0].split('\'')[1]

elif 'invalid object name' in e.args[0]:

''' 當提示當前模型沒有對應資料表,則直接返回空,無需處理 '''

return , columns

elif engine == 'postgresql':

if 'does not exist' in e.orig.diag.message_primary and 'relation' not in e.orig.diag.message_primary:

column_name = e.orig.diag.message_primary.split('.')[1].split()[0]

elif 'does not exist' in e.orig.diag.message_primary and 'relation' in e.orig.diag.message_primary:

return , columns

elif engine == 'sqlite':

if 'no such column' in e.args[0]:

column_name = e.args[0].split('.')[2]

elif 'no such table' in e.args[0]:

return , columns

for i, column in enumerate(columns):

if column.key == column_name:

''' 剔除不支援的字段 '''

columns.pop(i)

''' 因為上文已經出現過報錯,在某些資料庫的引擎中,如postgres的引擎會將execute指令認為是事務,導致之後所有操作均不執行

此處直接關閉session,再遞迴進下一步時重新建立'''

session.close()

''' 遞迴入口 '''

rows, columns = inner(model, columns)

return rows, columns

rows, columns = inner(model, columns)

if rows:

''' 拼裝返回的資料為列表 '''

li =

for row in rows:

mo = dict()

for idx, field in enumerate(list(row)):

if isinstance(field, datetime):

field = field.strftime('%y-%m-%d %h:%m:%s')

mo[columns[idx].key] = field

return li

else:

return

customcommand = manager(help='project auxiliary tool.')

@customcommand.option('-d', '--directory', dest='directory', default='',

help=("json script directory (default is "

"'project home directory')"))

def exports(directory=''):

"""資料匯出 命令

:param directory: 可設定的匯出存放路徑,預設為當前目錄下

:return:

"""''' 獲取所有基於db.model的子類,即所有模型model '''

models = db.model.__subclasses__()

result = dict()

for model in models:

result[model.__name__] = get_rows(model)

f = open(directory + 'models.json', 'w')

f.write(json.dumps(result))

f.close()

print('export data to [%s] success!' % directory + 'models.json')

@customcommand.option('-d', '--directory', dest='directory', default='',

help=("json script directory (default is "

"'project home directory')"))

def imports(directory=''):

"""資料匯入 命令

:param directory: 資料庫檔案的存放路徑,預設為當前目錄下

:return:

"""os.path.exists(directory + 'models.json')

f = open(directory + 'models.json', 'r')

mo_data = f.read()

data = json.loads(mo_data)

for table in data.keys():

if data.get(table):

with session_maker() as session:

instance_li =

for value in data[table]:

instance = getattr(models, table)(**value)

session.add_all(instance_li)

print('import data success!')

注釋齊全。以上。

Activity啟動模式的應用場景

singletop適合接收通知啟動的內容顯示頁面。例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都開啟乙個新聞內容頁面是很煩人的。singletask適合作為程式入口點。例如瀏覽器的主介面。不管從多少個應用啟動瀏覽器,只會啟動主介面一次,其餘情況都會走onnewintent,並且會...

Activity的啟動模式及應用場景

標籤 空格分隔 activity 1.standard這是預設模式,每次啟用activity時都會建立activity例項,並放入任務棧中。使用場景 大多數activity startactivity activity a activity b activity c back activity c ...

Oracle imp 匯入資料的應用

1.它是作業系統下乙個可執行的檔案 存放目錄 oracle home bin imp匯入工具將exp形成的二進位制系統檔案匯入到資料庫中.它有三種模式 a.使用者模式 匯出使用者所有物件以及物件中的資料 b.表模式 匯出使用者所有表或者指定的表 c.整個資料庫 匯出資料庫中所有物件。只有擁有imp ...