我正在爲我們當前的生產App Engine應用程序編寫數據存儲遷移。是否有可能爲一種實體類型加載2個模型來支持數據遷移?
我們,所以我試圖把到位的架構,允許在未來更容易遷移做了一些數據模型相當廣泛的變化。這包括遷移腳本的測試套件和遷移腳本的通用類結構。
我遇到了我目前的戰略問題。對於遷移和測試腳本,我需要一種方法來將舊模式中的模型類和新數據模式中的模型類同時加載到內存中,並使用任何一種方式加載實體。
這裏是設定模式的一個例子。
rev1.py
class Account(db.Model):
_version = db.IntegerProperty(default = 1)
user = db.UserProperty(auto_current_user_add = True, required = True)
name = db.StringProperty()
contact_email = db.EmailProperty()
rev2.py
class Account(db.Model):
_version = db.IntegerProperty(default = 2)
auth_id = db.StringProperty()
name = db.StringProperty()
pwd_hash = db.StringProperty(required = True, indexed = False)
遷移腳本可能看起來像:
import rev1
import rev2
class MyMigration(...):
def isNeeded(self):
num_accounts = num_entities_with_version(rev1.Account, 1)
return num_accounts > 0
def run(self):
rev1_accounts = rev1.Account.all()
for account in [a for a in rev1_accounts if account._version == 1]:
auth_id = account.contact_email
if auth_id is None or auth_id == '':
auth_id = account.user.email()
new_account = rev2.Account.create(auth_id = auth_id,
name = account.name)
和測試套件會是這個樣子:
import rev1
import rev2
class MyTest(...):
def testIt(self):
# Setup data
act1 = rev1.Account(name = '..', contact_email = '..')
act1.put()
act2 = rev1.Account(name = '..', contact_email = '..')
act2.put()
# Run migration
migration.run()
# Check results
accounts = rev2.Account.all().fetch(99)
所以,你可以看到我在兩種方式使用舊版本。我在遷移中使用它作爲讀取舊格式數據並將其轉換爲新格式的方式。 (注意:由於諸如所需的pwd_hash字段和其他字段更改之類的內容,我無法以新格式讀取它)。在運行遷移之前,我正在測試套件中使用它來設置舊格式的測試數據。
這一切似乎在理論上很不錯,但在實踐中分崩離析,因爲GAE不允許被加載在同一種多個模型,或者更具體地說,查詢只返回最近定義的模型。
在開發服務器中,這似乎是由於實體的查詢(例如:Account.get(my_key))上調用get()的過程調用了構建結果模型對象的結果鉤子通過調用class_for_kind關於數據中的實體種類名稱。因此,儘管我可能會調用rev2.Account.get(),但它可能會構建rev1.Account模型對象,因爲類型'Account'映射到_kind_map字典中的rev1.Account。
這使我重新思考自己的遷移策略了一下,我想問問,如果任何人有想法。具體做法是:
- 這將是安全的在運行時可以手動覆蓋google.appengine.ext.db._kind_map測試和生產服務器上允許這種遷移方法的工作?
- 有沒有更好的方法來同時將兩個版本的模型保存在內存中?
- 是否有不同的遷移方法,可能是更智能的方式去做這項工作? 我也曾想過嘗試
其他方法包括:版本變化
- 更改實體類型時。 (使用kind()來更改它)然後,當我們遷移時,我們將所有的類移動到新的類名稱。
- 找到一種方法來查詢實體並找回尚未構建到完整對象中的「原始」對象(原始緩衝區)。 (不會與測試一起工作)
- 'Just Do It Live':不要爲任何測試編寫測試,而只是嘗試使用最新的模式進行遷移,然後加載舊數據來解決問題。
關於在存在重大模式更改的情況下創建新類型的想法的任何想法。看起來這會允許類似於使用Expando的東西,但允許遷移一次發生。 (即加載rev1.Account(kind:acct_v1)的所有舊實體,然後將它們保存到新模型rev2.Account(kind:acct_v2))。這對我來說似乎比較乾淨,然後暫時引入Expando,然後再將其拉出。它可能具有允許測試整個過程的額外好處。毫無疑問,我錯過了一些東西。 – Allen 2012-01-30 23:10:09
我得說我喜歡改變種類來表示版本的想法。這可以很乾淨。如果這個槍太大,我會選擇暫時的Expando路線。您也可以暫時從新字段中刪除「必需」標誌,然後您可以就地更改實體,並且仍然具有某種類型安全性。但請注意,您無法以這種方式完全擺脫舊屬性 - 這需要暫時使用Expando。但是,在將所有實體轉換爲新模式後,您可以在離線運行的一次性映射/縮減作業中執行清理步驟。不要把W弄亂。親切的地圖。 – 2012-01-31 03:18:28
我想在兩種情況下更新類型以表明版本可能是好的:1)真正的主要模式更改和2)當您沒有很多對該模型的引用時(或者它們全部是通過鍵名/ ID)。如果你有很多交叉引用,你可能會弄得一團糟。 – 2012-01-31 05:27:55