在“编译"时不知道属性名称的情况下,在 Python 中复制 Google App Engine 数据存储中的实体

在“编译

问题描述:

在我正在编写的 Python Google App Engine 应用程序中,我有一个实体存储在我需要检索的数据存储区中,制作它的精确副本(密钥除外),然后将该实体放回原处

In a Python Google App Engine app I'm writing, I have an entity stored in the datastore that I need to retrieve, make an exact copy of it (with the exception of the key), and then put this entity back in.

我该怎么做?特别是,在执行此操作时是否需要注意任何警告或技巧,以便我获得我期望的类型的副本,而不是其他内容.

How should I do this? In particular, are there any caveats or tricks I need to be aware of when doing this so that I get a copy of the sort I expect and not something else.

预计到达时间:嗯,我试过了,但确实遇到了问题.我想以这样一种方式制作我的副本,这样我在编写代码时就不必知道属性的名称.我的想法是这样做:

ETA: Well, I tried it out and I did run into problems. I would like to make my copy in such a way that I don't have to know the names of the properties when I write the code. My thinking was to do this:

#theThing = a particular entity we pull from the datastore with model Thing
copyThing = Thing(user = user)
for thingProperty in theThing.properties():
    copyThing.__setattr__(thingProperty[0], thingProperty[1])

这执行时没有任何错误...直到我尝试从数据存储中提取 copyThing,此时我发现所有属性都设置为 None(显然,用户和密钥除外).很明显,这段代码正在做一些事情,因为它用 None 替换了默认值(所有属性都设置了默认值),但根本不是我想要的.建议?

This executes without any errors... until I try to pull copyThing from the datastore, at which point I discover that all of the properties are set to None (with the exception of the user and key, obviously). So clearly this code is doing something, since it's replacing the defaults with None (all of the properties have a default value set), but not at all what I want. Suggestions?

给你:

def clone_entity(e, **extra_args):
  """Clones an entity, adding or overriding constructor attributes.

  The cloned entity will have exactly the same property values as the original
  entity, except where overridden. By default it will have no parent entity or
  key name, unless supplied.

  Args:
    e: The entity to clone
    extra_args: Keyword arguments to override from the cloned entity and pass
      to the constructor.
  Returns:
    A cloned, possibly modified, copy of entity e.
  """
  klass = e.__class__
  props = dict((k, v.__get__(e, klass)) for k, v in klass.properties().iteritems())
  props.update(extra_args)
  return klass(**props)

示例用法:

b = clone_entity(a)
c = clone_entity(a, key_name='foo')
d = clone_entity(a, parent=a.key().parent())

如果使用 NDB 会发生变化

将下面 Gus 的评论与指定不同数据存储名称的属性的修复相结合,以下代码适用于 NDB:

Combining Gus' comment below with a fix for properties that specify a different datastore name, the following code works for NDB:

def clone_entity(e, **extra_args):
  klass = e.__class__
  props = dict((v._code_name, v.__get__(e, klass)) for v in klass._properties.itervalues() if type(v) is not ndb.ComputedProperty)
  props.update(extra_args)
  return klass(**props)

示例用法(注意 key_name 在 NDB 中变成 id):

Example usage (note key_name becomes id in NDB):

b = clone_entity(a, id='new_id_here')

旁注:请参阅使用 _code_name 获取 Python 友好的属性名称.没有这个,像 name = ndb.StringProperty('n') 这样的属性会导致模型构造函数引发 AttributeError: type object 'foo' has no attribute 'n'>.

Side note: see the use of _code_name to get the Python-friendly property name. Without this, a property like name = ndb.StringProperty('n') would cause the model constructor to raise an AttributeError: type object 'foo' has no attribute 'n'.