Index > Google App Engine データストア上のデータをまとめて消す方法
Sat, June 20, 2009

Google App Engine データストア上のデータをまとめて消す方法

Google App Engineでデータストアに入れたデータを削除したい場合、 どうすればいいのか。
件数が少なければ 管理コンソール(Datastore - Data Viewer)上で削除することができる。
しかし件数が多いととてもこれでは対応できない。

Google App EngineのDatastoreの中身を全部消す とか、 GoogleAppEngineでremote_apiを使う では、件数が多い場合の対応方法が紹介されていたが、ちょっと面倒そうに思ったので、 以下の方法で対処することに。

普通にアプリケーション中にデータストア削除インタフェースを用意

アイデア自体は普通です。

データストア中に MyDataというデータモデルがあれば、そこにある実体データは、以下のコードに削除できます。

for data in MyData.all():
    data.delete()

/RemoveAll などのURLを呼び出したら、このコードを実行するようにすればよい。

問題に対処

ただし、この方法では、データストアに登録されているデータ量が多い場合エラーになります。

その理由は、GAEの以下のような制限にひっかかってしまうためです。

したがって、これを回避するため以下のようにします。

  1. /RemoveAll というURLにアクセスするとデータストアを削除するプログラムを起動する
  2. /RemoveAll では、一度に20件程度、GAEの制約に触れない範囲で削除を実行する
  3. /RemoveAll 完了後、HTML metaの refresh によるリダイレクトにより ブラウザが 10秒後に再度自動的に /RemoveAll を呼び出す

このようにして、/RemoveAllにアクセスすると一度に20件ずつデータを削除するを ひたすら削除対象のデータ無くなるまで繰り返すようにします。

redirect間隔は10秒より短くても問題ないかもしれません。 必要なら管理コンソールで負荷状況を見て調整します。

具体的には

以上のアイデアを具体的にコードで表現すると以下のようになります。

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




class MyData(db.Model):
    pass


class RemoveAll(webapp.RequestHandler):

    def get(self) :

        limit = 10

        list=self.getMyList(limit)

        if len(list)<1:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write( 'OK remove all data.' )
            return 

        for c in list:
            c.delete()

        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write( '<html>' )
        self.response.out.write( '<head>' )
        self.response.out.write( '<META HTTP-EQUIV="REFRESH" CONTENT="10;URL=/RemoveAll">')
        self.response.out.write( '</head>' )
        self.response.out.write( '<body>' )
        self.response.out.write( 'Again after 10seconds.' )
        self.response.out.write( '</body>' )
        self.response.out.write( '</html>' )


    def getMyList(self,limit):
        query = MyData.gql('LIMIT '+str(limit))
        return query[0:min(query.count(),limit)]


application = webapp.WSGIApplication(
    [
        ('/RemoveAll', RemoveAll)
    ],
    debug=True)


def main(): 
    run_wsgi_app(application)

if __name__ == "__main__": 
    main()

データモデルが二種類以上ある場合...

以下のようにすると、モデルの種類が多い場合に便利です。

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


class MyData1(db.Model):
    pass

class MyData2(db.Model):
    pass


class AbstractRemoveAll(webapp.RequestHandler):

    def get(self) :

        #limit = 50
        limit = 100
        list  = self.getMyList(limit)

    if list==None :
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write( 'Error.' )
            return 

        if len(list)<1:
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write( 'OK remove all data.' )
            return 


        for c in list:
            c.delete()

        self.response.headers['Content-Type'] = 'text/html'
        self.response.out.write( '<html>' )
        self.response.out.write( '<head>' )
        self.response.out.write( '<META HTTP-EQUIV="REFRESH" CONTENT="10;URL='+self.getMyUrl()+'">')
        self.response.out.write( '</head>' )
        self.response.out.write( '<body>' )
        self.response.out.write( '<p>' )
        self.response.out.write( len(list) )
        self.response.out.write( '</p>' )
        self.response.out.write( 'Again after 10seconds.' )
        self.response.out.write( '</body>' )
        self.response.out.write( '</html>' )


    def getMyUrl(self) :
        return '/RemoveAll'

    def getMyList(self,limit):
        return []


class RemoveAll1(AbstractRemoveAll):
    def getMyUrl(self) :
        return '/RemoveAll1'

    def getMyList(self,limit):
        q=MyData1.gql('LIMIT '+str(limit))
    return q[0:min(q.count(),limit)]

class RemoveAll2(AbstractRemoveAll):
    def getMyUrl(self) :
        return '/RemoveAll2'

    def getMyList(self,limit):
        q=MyData2.gql('LIMIT '+str(limit))
    return q[0:min(q.count(),limit)]



application = webapp.WSGIApplication(
    [
        ('/RemoveAll2', RemoveAllPage2),
        ('/RemoveAll1', RemoveAllPage1)
    ],
    debug=True)


def main(): 
    run_wsgi_app(application)

if __name__ == "__main__": 
    main()
 Twitter
follow me on Twitter
 Categories