更改NDB字段的属性类型时迁移数据

时间:2013-11-07 17:30:36

标签: python google-app-engine app-engine-ndb

假设我最初创建一个ndb.Model并想要更改字段的ndb属性类型(例如IntegerProperty到StringProperty),但是想要转换存储在该字段中的当前数据,这样我就不会丢失该数据。一种方法是简单地创建一个新的字段名称,然后使用脚本迁移数据,但还有其他更方便的方法来实现吗?

例如,假设我有以下模型:

class Car(ndb.Model):
    name = ndb.StringProperty()
    production_year = ndb.IntegerProperty()

我存储了一个实体实例:

c = new Car()
c.name = "Porsche"
c.production_year = 2013 

并希望将production_year更改为ndb.StringProperty()而不会“丢失”我设置的值(它仍然存在,但不可检索)。如果我只是将production_year更改为ndb.StringProperty()的实例,则字段值不会报告一个有意义的值,因为该类型不匹配。

因此,如果我将模型更改为:

class Car(ndb.Model):
    name = ndb.StringProperty()
    production_year = ndb.StringProperty()

尝试使用点表示法检索字段将导致值为None。任何人遇到这种情况,你能解释一下你做了什么来解决它吗?感谢。

2 个答案:

答案 0 :(得分:10)

您如何处理这取决于您拥有多少实体。 如果您在10000中使用相对较少数量的实体,我会使用remote_api并从数据存储中检索原始基础数据并直接操作数据然后将其写回,而不是使用模型。例如,这将获取原始实体,并且可以像字典一样访问属性。此代码几乎从较低级别的appengine SDK代码中解除

from google.appengine.api import datastore
from google.appengine.api import datastore_errors

def get_entities(keys):
    rpc = datastore.GetRpcFromKwargs({})
    keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
    entities = None
    try:
        entities = datastore.Get(keys, rpc=rpc)
    except datastore_errors.EntityNotFoundError:
        assert not multiple

    return entities

def put_entities(entities):
    rpc = datastore.GetRpcFromKwargs({})
    keys = datastore.Put(entities, rpc=rpc)
    return keys

你可以按如下方式使用它(我使用fetch来简化这个例子的代码)

x = Car.query(keys_only=True).fetch(100)
results = get_entities([i.to_old_key() for i in x])

for i in results:
    i['production_year'] = unicode(i['production_year'])

put_entities(results)

这是我的旧代码,datastore.NormalizeAndTypeCheckKeys采用旧的db样式键,我没有看到ndb样式键的等效函数,但这确实有效。 (刚测试过; - )

此方法允许您在不部署任何新代码的情况下迁移数据 如果您有数百万个实体,那么您应该查看其他处理方法,即使用此代码并使用mapreduce。

答案 1 :(得分:2)

只要添加到Tim的答案中,如果您想将属性更改为Text,您可以:

from google.appengine.api import datastore_types

(...)

for i in results:
    i['production_year'] = datastore_types.Text(i['production_year'])