2010-05-04 54 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