2013-10-12 165 views
1

我有一個程序,它有一個特定的模式的數據庫,v0.1.0數據庫更改軟件更新

在我的下一個版本(v0.1.1)中,我對數據庫模式進行了更改。

因此,當我更新到(0.1.1)時,我希望這些更改在不影響(0.1.0)及其後續版本中的原始數據的情況下生效。

如何在不影響(0.1.0)數據庫數據並跟蹤後續版本中的更改的情況下進行更改?

我正在使用python與sqlite3

更新

沒有爲軟件的多個版本不支持。數據庫取決於您正在使用的版本。

沒有併發訪問數據庫,每個版本有1個數據庫。

因此,用戶可以使用舊版本,但是當他們升級到新版本時,.sqlite模式將被更改。

+1

您是否使用ORM或任何其他數據庫抽象? – AndrewS

+0

備註:使用[語義版本控制](http://semver.org/)並使數據庫模式更改不會向後兼容主版本更改。 –

+0

你有多個版本的軟件同時讀取同一個數據庫嗎?或者你只是想支持降級?例如。你有一個數據庫文件,並且你的軟件的版本1需要能夠繼續訪問數據,即使版本2在同一個文件中使用了一個更新的模式* *? –

回答

0

有幾種方法可以做到這一點,我只提一個。

看來你已經跟蹤數據庫中的版本。在您的應用程序開始時,您需要根據正在運行的應用程序的版本來檢查此版本,並運行將執行架構更改的任何SQL腳本。

更新

在這一行動的一個例子:

import os 
import sqlite3 as sqlite 

def make_movie_table(cursor): 
    cursor.execute('CREATE TABLE movies(id INTEGER PRIMARY KEY, title VARCHAR(20), link VARCHAR(20))') 

def make_series_table(cursor): 
    cursor.execute('CREATE TABLE series(title VARCHAR(30) PRIMARY KEY,series_link VARCHAR(60),number_of_episodes INTEGER,number_of_seasons INTEGER)') 

def make_episode_table(cursor): 
    cursor.execute('CREATE TABLE episodes(id INTEGER PRIMARY KEY,title VARCHAR(30),episode_name VARCHAR(15), episode_link VARCHAR(40), Date TIMESTAMP, FOREIGN KEY (title) REFERENCES series(title) ON DELETE CASCADE)') 

def make_version_table(cursor): 
    cursor.execute('CREATE TABLE schema_versions(version VARCHAR(6))') 
    cursor.execute('insert into schema_versions(version) values ("0.1.0")') 

def create_database(sqlite_file): 
    if not os.path.exists(sqlite_file): 
     connection = sqlite.connect(sqlite_file) 
     cursor = connection.cursor() 
     cursor.execute("PRAGMA foreign_keys = ON") 
     make_movie_table(cursor) 
     make_series_table(cursor) 
     make_episode_table(cursor) 
     make_version_table(cursor) 
     connection.commit() 
     connection.close() 

def upgrade_database(sqlite_file): 
    if os.path.exists(sqlite_file): 
     connection = sqlite.connect(sqlite_file) 
     cursor = connection.cursor() 

     cursor.execute("select max(version) from schema_versions") 
     row = cursor.fetchone() 
     database_version = row[0] 
     print('current version is %s' % database_version) 

     if database_version == '0.1.0': 
      print('upgrading version to 0.1.1') 
      cursor.execute('alter table series ADD COLUMN new_column1 VARCHAR(10)') 
      cursor.execute('alter table series ADD COLUMN new_column2 INTEGER') 
      cursor.execute('insert into schema_versions(version) values ("0.1.1")') 

     #if database_version == '0.1.1': 
      #print('upgrading version to 0.1.2') 
      #etc cetera 

     connection.commit() 
     connection.close() 

#I need to add 2 columns to the series table, when the user upgrade the software. 

if __name__ == '__main__': 
    create_database('/tmp/db.sqlite') 
    upgrade_database('/tmp/db.sqlite') 

每次升級腳本將採取的making your database changes照顧,然後更新數據庫內的版本到最新版本。請注意,我們不使用elif語句,因此您可以根據需要將數據庫升級到多個版本。

有一些注意事項要注意:

  • 運行升級inside transactions,你會想回滾任何錯誤,避免讓數據庫處於不可用狀態。 - 更新這是不正確的,正如指出的,謝謝Martijn!
  • 如果可以,請避免重命名和刪除列,並且如果必須,請確保使用它們的任何視圖也會更新。
+1

請注意'sqlite3'綁定[不正確處理事務中的DDL](http://bugs.python.org/issue10740)。 –

+0

感謝反轉這似乎是我正在尋找。我的數據庫更改都是ALTERS。 – zeref

-2

長期來看,使用ORM(例如SQLAlchemy)和Alembic等遷移工具會更好。

+0

我想OP有多個客戶端從同一個數據庫文件中讀取; Alembic不會解決多個版本使用已更改的模式讀取文件的問題。 –

+2

所以你已經發布了一個鏈接只回答([容易鏈接腐爛](http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-答案)),甚至沒有回答這個問題。很好的資源,但OP的問題呢? – Lix

+0

@lix;我普遍同意鏈接腐爛,但我建議技術(ORM和遷移工具)並提供了示例鏈接。我向OP透露,有一些普遍接受的技術可以解決他們的問題。我的帖子仍然是有價值的,但我並不是簡單地推遲了答案。 – AndrewS

2

user_version PRAGMA跟蹤數據庫架構版本,並會在升級步驟序列:

def get_schema_version(conn): 
    cursor = conn.execute('PRAGMA user_version') 
    return cursor.fetchone()[0] 

def set_schema_version(conn, version): 
    conn.execute('PRAGMA user_version={:d}'.format(version)) 

def initial_schema(conn): 
    # install initial schema 
    # ... 

def ugrade_1_2(conn): 
    # modify schema, alter data, etc. 

# map target schema version to upgrade step from previous version 
upgrade_steps = { 
    1: initial_schema, 
    2: ugrade_1_2, 
} 

def check_upgrade(conn): 
    current = get_schema_version(conn) 
    target = max(upgrade_steps) 
    for step in range(current + 1, target + 1): 
     if step in upgrade_steps: 
      upgrade_steps[step](conn) 
      set_schema_version(conn, step)