массовое обновление / удаление объектов различного вида в db.run_in_transaction - PullRequest
0 голосов
/ 04 мая 2010

Здесь представлен псевдокод массового обновления / удаления объектов различного вида в одной транзакции. Обратите внимание, что сущности Album и Song имеют AlbumGroup в качестве корневой сущности. (т.е. имеет ту же родительскую сущность)

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 Ответ

1 голос
/ 04 мая 2010

Обратите внимание, что ReferenceProperty недостаточно, чтобы поместить объекты в одну группу. При создании модели Song необходимо передать аргумент parent вместе с родителем модели (например, Album).

Это выглядит так:

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

См. документацию о предках .

...