40 votes

Copier une entité dans le magasin de données Google App Engine dans Python sans connaître les noms de propriété au moment de la compilation

Dans un Python Google App Engine application que je suis en train d'écrire, j'ai une entité stockées dans la banque de données que j'ai besoin de récupérer, de faire une copie exacte de celui-ci (à l'exception de la clé), puis mettre cette entité dans.

Comment dois-je faire? En particulier, existe-il des mises en garde ou des trucs que j'ai besoin d'être conscient de quand vous faites cela de sorte que-je obtenir une copie de la sorte je attendre et pas autre chose.

ETA: eh Bien, je l'ai essayé et je n'ai couru dans des problèmes. Je voudrais faire ma copie d'une façon telle que je n'ai pas à connaître les noms des propriétés quand j'ai écrit le code. Ma pensée a été pour ce faire:

#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])

Cela exécute sans erreur... jusqu'à ce que j'essaie de tirer copyThing de la banque de données, à quel point je découvre que toutes les propriétés sont None (à l'exception de l'utilisateur et la clé, évidemment). Donc clairement, ce code est en train de faire quelque chose, puisque c'est de remplacer les valeurs par défaut à None (toutes les propriétés ont une valeur par défaut), mais pas du tout ce que je veux. Des Suggestions?

61voto

Nick Johnson Points 79909

Voici:

 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)
 

Exemple d'utilisation:

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

19voto

crizCraig Points 2041

Si vous utilisez le NDB, vous pouvez simplement copier avec: new_entity.populate(**old_entity.to_dict())

16voto

gaborlenard Points 778

C'est juste une extension de Nick Johnson excellent code pour régler les problèmes mis en évidence par Amir dans les commentaires:

  1. La db.Valeur de la clé de la ReferenceProperty n'est plus récupérée via inutile de faire des aller-retour à la banque de données.
  2. Vous pouvez maintenant spécifier si vous souhaitez ignorer DateTime propriétés avec l' auto_now et/ou auto_now_add drapeau.

Voici le code mis à jour:

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)

La première if expression n'est pas très élégant je me réjouis donc, si vous pouvez partager une meilleure façon de l'écrire.

1voto

jholster Points 3456

Je ne suis ni un gourou Python, ni un gourou AppEngine, mais ne pourrait-on pas obtenir / définir dynamiquement les propriétés?

 props = {}
for p in Thing.properties():
    props[p] = getattr(old_thing, p)
new_thing = Thing(**props).put()
 

0voto

Nick Franceschina Points 1794

Cela peut être difficile si vous avez renommé les clés sous-jacentes de vos propriétés... certaines personnes choisissent de le faire au lieu de faire masse des modifications de données

dire que vous avez commencé avec ceci:

class Person(ndb.Model):
   fname = ndb.StringProperty()
   lname = ndb.StringProperty()

puis, un jour, vous avez vraiment décidé qu'il serait plus agréable à utiliser first_name et last_name à la place... si vous faites cela:

class Person(ndb.Model):
   first_name = ndb.StringProperty(name="fname")
   last_name = ndb.StringProperty(name="lname")

maintenant, quand vous n'avez Personne._properties (ou .propriétés() ou person_instance._properties), vous obtiendrez un dictionnaire avec les touches qui correspondent aux sous-jacents des noms (fname et lname)... mais ne pas correspondre à la propriété des noms de la classe... donc il ne fonctionnera pas si vous les mettez dans le constructeur d'une nouvelle instance, ou utiliser le .remplir (), méthode (les exemples ci-dessus va casser)

Dans le NDB de toute façon, les instances de modèles ._values dictionnaire, qui est saisi par le bien immobilier sous-jacent noms... et vous pouvez les mettre à jour directement. Je me suis retrouvé avec quelque chose comme ceci:

    def clone(entity, **extra_args):
        klass = entity.__class__
        clone = klass(**extra_args)
        original_values = dict((k,v) for k,v in entity._values.iteritems() if k not in clone._values)
        clone._values.update(original_values)
        return clone

Ce n'est pas vraiment le moyen le plus sûr... car il existe d'autres méthodes helper privées qui ne sont plus de travail (comme la validation et la conversion des propriétés calculées en utilisant _store_value() et _retrieve_value())... mais si vous êtes modèles sont assez simples, et que vous aimez la vie sur le bord :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X