如何通过远程API重置Datastore模型的属性类型

2 投票
2 回答
658 浏览
提问于 2025-04-16 00:19

我在一个已有的数据存储中有一个模型,长得像这样:

class SomeKind(db.Model):
  name = db.StringProperty(required=True)
  someField = db.BlobProperty(required=True)

在这个数据存储里,大约有20000多个这样的实体。现在我想重新调整这个模型,让它变成这样:

class SomeKind(db.Model):
  name = db.StringProperty(required=True)
  someField = db.StringProperty(required=True)

我觉得我需要:

  1. 遍历数据存储,删除现有的“someField”数据,来自models.py。
  2. 移除这个属性。
  3. 在models.py中添加这个属性,并给它一个新的定义。

我在第一步卡住了,我想用远程API删除现有的属性:

import sys, time, urllib2

sys.path.append("gae/paths")
...
sys.path.append("myapp/path")
from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.ext.remote_api import remote_api_stub
from models import *

def tryPut(db, set, tryLimit=10, seconds=5, trying=1):
  try:
    db.put(set)
    return True
  except urllib2.HTTPError:
    if trying <= tryLimit:
      print "retry (%d of %d) in 5 seconds" % (trying, tryLimit)
      time.sleep(5)
      tryPut(db, set, seconds, trying+1)
    else:
      print urllib2.HTTPError
      sys.exit()

def main():

    remote_api_stub.ConfigureRemoteDatastore(None, 
      '/remote_api', my_auth_func, 'myapp.appspot.com')
    q = db.GqlQuery("SELECT * FROM SomeKind")
    last_cursor = memcache.get('SomeKind/update')

    if last_cursor:
        q.with_cursor(last_cursor)

    set = q.fetch(100)

    while len(set) != 0:
      for someKind in set:
        print someKind.name

        # this doesn't work
        delattr(someKind, "someField")

        # this doesn't work either
        del someKind.someField

      print "update to Google"
      if tryPut(db, set):
        cursor = q.cursor()
        memcache.set('SomeKind/update', cursor)

if __name__ == "__main__":
  main()

我在我的机器上运行这个。问题是这个脚本,无论用哪种方法,总是会报错:

Traceback (most recent call last):
File "./query.py", line 91, in <module>
  main()
File "./query.py", line 66, in main
  del someKind.someField
AttributeError: __delete__

文档中(http://code.google.com/intl/en/appengine/articles/update_schema.html)提到过“使用delattr来删除过时的属性,然后保存实体”。但里面没有任何示例。

我该怎么做呢?我的步骤正确吗?我该如何删除这个属性?

2 个回答

1

这样使用

class News(db.Expando):
    title = db.StringProperty()
    #category = db.TextProperty()    ---> removed property
    tags = db.StringProperty()
    content_short = db.TextProperty()
    content_long = db.TextProperty()
    date = db.DateTimeProperty()
    views = db.IntegerProperty(default=0)

还有

news = query.fetch(limit=PAGESIZE+1,offset=(int(page)-1)*PAGESIZE)
for n in news:
    del n.category
    n.put()

完整代码在这里 - http://appengine4dummies.blogspot.com/2012/01/text-that-matters-deleting-appengine.html

3

你不能从一个模型中删除属性,因为每个模型实例都有一套相同的属性。不过,Expando 让你可以拥有动态属性。

最简单的做法可能是这样:

  1. 把你的模型类改成继承自 db.Expando,而不是 db.Model。
  2. 在模型类中添加新的属性(如果你想在数据存储中用不同的名字来表示这个属性,可以使用 'name' 这个关键词参数),然后把旧的属性删掉。
  3. 使用 mapreduce API 遍历每个实体,调用 "del mymodel.oldprop",并根据需要设置新的属性。
  4. 再次更新模型定义,把它改回继承自 db.Model。

撰写回答