【问题标题】:Migrating data when changing an NDB field's property type更改 NDB 字段的属性类型时迁移数据
【发布时间】:2013-11-19 11:53:50
【问题描述】:

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

【问题讨论】:

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


【解决方案1】:

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

【讨论】:

  • 谢谢。虽然我还没有尝试过,但您的解决方案看起来相当简单。我猜想 RPC 检索到的键然后存储在模型的 _properties 字段中,所以如果我认为我怀疑我会更改字段的属性类型,我可能应该将我的字段保留为 StringProperty 类型并编写一个包装器来检索/存储值,而不必担心迁移数据。
【解决方案2】:

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

from google.appengine.api import datastore_types

(...)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多