2010-05-04 53 views
0

這裏介紹在單個事務中批量更新/刪除不同類型實體的僞代碼。請注意,專輯和歌曲實體具有AlbumGroup作爲根實體。 (即,具有相同的父實體)批量更新/刪除db.run_in_transaction中不同類型的實體

class Album: 
    pass 
class Song: 
    album = db.ReferenceProperty(reference_class=Album,collection_name="songs") 

def bulk_update_album_group(album): 
    updated = [album] 
    deleted = [] 
    for song in album.songs: 
    if song.is_updated: 
     updated.append(song)   
    if song.is_deleted: 
     deleted.append(song) 
    db.put(updated) 
    db.delete(deleted) 

a = Album.all().filter("...").get() 

# bulk update/delete album. 

db.run_in_transaction(bulk_update_album,a) 

但我「事務中只有祖先查詢」錯誤在迭代反向參考屬性滿足一個著名的像「album.songs」。我猜ancestor()過濾器沒有幫助,因爲這些實體在內存中被修改。

所以我修改這樣的例子:在調用事務之前準備所有更新/刪除的實體。

def bulk_update_album2(album): 
    updated = [album] 
    deleted = [] 
    for song in album.songs: 
    if song.is_updated: 
     updated.append(song)   
    if song.is_deleted: 
     deleted.append(song) 
    def txn(updated,deleted): 
    db.put(updated) 
    db.delete(deleted) 
    db.run_in_transaction(txn,updated,deleted) 

現在我發現,迭代反向參考屬性力量重裝現有實體。所以應該避免修改後重新迭代回參考屬性!

所有我想要確認的是:

當需要批量更新/刪除別樣的許多實體, 是有這種情況有什麼好的編碼模式? 我最後的代碼可以很好嗎?


這裏去全面的代碼示例:

from google.appengine.ext import webapp 
from google.appengine.ext.webapp import util 

import logging 

from google.appengine.ext import db 

class Album(db.Model): 
    name = db.StringProperty() 
    def __repr__(self): 
     return "%s%s"%(self.name,[song for song in self.songs]) 

class Song(db.Model): 
    album = db.ReferenceProperty(reference_class=Album,collection_name='songs') 
    name = db.StringProperty() 
    playcount = db.IntegerProperty(default=0) 
    def __repr__(self): 
     return "%s(%d)"%(self.name,self.playcount) 

def create_album(name): 
    album = Album(name=name) 
    album.put() 
    for i in range(0,5): 
     song = Song(parent=album, album=album, name='song#%d'%i) 
     song.put() 
    return album 

def play_all_songs(album): 
    logging.info(album) 

    # play all songs 
    for song in album.songs: 
     song.playcount += 1 
     logging.info(song) 

    # play count also 0 here 
    logging.info(album) 

    def save_play_count(album): 
     updated = [] 
     for song in album.songs: 
      updated.append(song) 
     db.put(updated) 

    db.run_in_transaction(save_play_count,album) 

def play_all_songs2(album): 
    logging.info("loading : %s"%album) 

    # play all songs 
    updated = [] 
    for song in album.songs: 
     song.playcount += 1 
     updated.append(song) 

    logging.info("updated: %s"%updated) 
    db.put(updated) 

    logging.info("after save: %s"%album)  

def play_all_songs3(album): 
    logging.info("loading : %s"%album) 

    # play all songs 
    updated = [] 
    for song in album.songs: 
     song.playcount += 1 
     updated.append(song) 

    # reload 
    for song in album.songs: 
     pass 

    logging.info("updated: %s"%updated) 
    def bulk_save_play_count(updated): 
     db.put(updated) 
    db.run_in_transaction(bulk_save_play_count,updated) 

    logging.info("after save: %s"%album) 

class MainHandler(webapp.RequestHandler): 
    def get(self): 
     self.response.out.write('Hello world!') 

     album = Album.all().filter('name =','test').get() 
     if not album:    
      album = db.run_in_transaction(create_album,'test') 

     # BadRequestError: Only ancestor queries are allowed inside transactions. 
     #play_all_songs(album) 

     # ok 
     #play_all_songs2(album) 

     play_all_songs3(album) 

def main(): 
    application = webapp.WSGIApplication([('/', MainHandler)], 
             debug=True) 
    util.run_wsgi_app(application) 


if __name__ == '__main__': 
    main() 

回答

1

請注意,該ReferenceProperty是不夠的,把實體在同一組。當您創建Song模型時,您應該將parent argument與模型的父級(例如,Album)一起傳遞。

它看起來像這樣:

album = Album.all().filter("...").get() 
new_song = Song(name='Seven Nation Army', parent=album) 
new_song.save() 

documentation about ancestors

+0

感謝您的好意。但我已經將父參數傳遞給新創建的歌曲實體。我想驗證的是「在交易功能中迭代引用屬性是被禁止的?」 – 2010-05-06 03:31:26