使用Python在Google App Engine数据存储区中复制实体,而无需在"编译"时知道属性名称

G G*_*III 39 python google-app-engine google-cloud-datastore

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

我该怎么做?特别是,在做这个时我是否需要注意任何警告或技巧,以便获得我期望的那种副本而不是其他东西.

ETA: 嗯,我试了一下,确实遇到了问题.我想以这样的方式制作我的副本,以便在编写代码时不必知道属性的名称.我的想法是这样做:

#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])
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*son 59

干得好:

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)
Run Code Online (Sandbox Code Playgroud)

用法示例:

b = clone_entity(a)
c = clone_entity(a, key_name='foo')
d = clone_entity(a, parent=a.key().parent())
Run Code Online (Sandbox Code Playgroud)

编辑:如果使用NDB则更改

将下面的Gus'注释与指定不同数据存储名称的属性的修复相结合,以下代码适用于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)
Run Code Online (Sandbox Code Playgroud)

示例性的使用(注意key_name变得id在NDB):

b = clone_entity(a, id='new_id_here')
Run Code Online (Sandbox Code Playgroud)

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

  • 对于使用NDB的下一代,您将不得不为`klass._properties`更改`klass.properties()`.并且您将获得Computed属性的错误,因此请注意.为此替换了props var:`props = dict((k,v .__ get __(e,klass))for k,v in klass._properties.iteritems()如果type(v)不是ndb.ComputedProperty) (5认同)
  • 很好,干净,但如果需要,它将从数据存储中提取引用属性.例如,如果实体有20个ReferenceProperty字段,那么它们将按顺序从数据存储区中拉出.(20次往返很糟糕:)这可以通过检查db.ReferenceProperty并使用get_value_for_datastore来解决. (3认同)

cri*_*aig 19

如果您正在使用NDB,您只需复制: new_entity.populate(**old_entity.to_dict())


zen*_*bor 15

这只是尼克约翰逊优秀代码的扩展,以解决Amir在评论中强调的问题:

  1. 不再通过不必要的往返数据存储区检索ReferenceProperty的db.Key值.
  2. 您现在可以指定是否要使用auto_now和/或auto_now_add标志跳过DateTime属性.

这是更新的代码:

def clone_entity(e, skip_auto_now=False, skip_auto_now_add=False, **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
    skip_auto_now: If True then all DateTimeProperty propertes will be skipped which have the 'auto_now' flag set to True
    skip_auto_now_add: If True then all DateTimeProperty propertes will be skipped which have the 'auto_now_add' flag set to True
    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 = {}
  for k, v in klass.properties().iteritems():
    if not (type(v) == db.DateTimeProperty and ((skip_auto_now and getattr(v, 'auto_now')) or (skip_auto_now_add and getattr(v, 'auto_now_add')))):
      if type(v) == db.ReferenceProperty:
        value = getattr(klass, k).get_value_for_datastore(e)
      else:
        value = v.__get__(e, klass)
      props[k] = value
  props.update(extra_args)
  return klass(**props)
Run Code Online (Sandbox Code Playgroud)

第一个if表达不是很优雅,所以我很感激你能分享一个更好的写作方式.