7

假设我最初创建了一个 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。任何人都遇到过这种情况,你能解释一下你做了什么来解决它吗?谢谢。

4

2 回答 2

11

你如何处理这将取决于你有多少实体。如果您说相对较少的实体在 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。

于 2013-11-07T23:40:47.023 回答
2

只需添加蒂姆的答案,如果您想将您的属性更改为文本,您可以:

from google.appengine.api import datastore_types

(...)

for i in results:
    i['production_year'] = datastore_types.Text(i['production_year'])
于 2015-01-13T11:20:07.430 回答