12

Tout d'abord, je suis relativement nouveau sur Google App Engine, donc je fais probablement quelque chose de stupide.Touches personnalisées pour les modèles Google App Engine (Python)

Dire que j'ai un Foo modèle:

class Foo(db.Model): 
    name = db.StringProperty() 

Je veux utiliser name comme une clé unique pour chaque objet Foo. Comment cela est-il fait?

Quand je veux obtenir un objet Foo spécifique, je requête actuellement le datastore pour tous Foo objets avec le nom de la cible unique, mais les requêtes sont lentes (plus il est une douleur pour faire en sorte que name est unique lorsque chaque nouvelle Foo est créé).

Il doit y avoir une meilleure façon de faire cela!

Merci.

Répondre

13

Je l'ai utilisé le code ci-dessous dans un projet avant. Cela fonctionnera tant que le champ sur lequel vous basez votre nom de clé est requis.

class NamedModel(db.Model): 
    """A Model subclass for entities which automatically generate their own key 
    names on creation. See documentation for _generate_key function for 
    requirements.""" 

    def __init__(self, *args, **kwargs): 
     kwargs['key_name'] = _generate_key(self, kwargs) 
     super(NamedModel, self).__init__(*args, **kwargs) 


def _generate_key(entity, kwargs): 
    """Generates a key name for the given entity, which was constructed with 
    the given keyword args. The entity must have a KEY_NAME property, which 
    can either be a string or a callable. 

    If KEY_NAME is a string, the keyword args are interpolated into it. If 
    it's a callable, it is called, with the keyword args passed to it as a 
    single dict.""" 

    # Make sure the class has its KEY_NAME property set 
    if not hasattr(entity, 'KEY_NAME'): 
     raise RuntimeError, '%s entity missing KEY_NAME property' % (
      entity.entity_type()) 

    # Make a copy of the kwargs dict, so any modifications down the line don't 
    # hurt anything 
    kwargs = dict(kwargs) 

    # The KEY_NAME must either be a callable or a string. If it's a callable, 
    # we call it with the given keyword args. 
    if callable(entity.KEY_NAME): 
     return entity.KEY_NAME(kwargs) 

    # If it's a string, we just interpolate the keyword args into the string, 
    # ensuring that this results in a different string. 
    elif isinstance(entity.KEY_NAME, basestring): 
     # Try to create the key name, catching any key errors arising from the 
     # string interpolation 
     try: 
      key_name = entity.KEY_NAME % kwargs 
     except KeyError: 
      raise RuntimeError, 'Missing keys required by %s entity\'s KEY_NAME '\ 
       'property (got %r)' % (entity.entity_type(), kwargs) 

     # Make sure the generated key name is actually different from the 
     # template 
     if key_name == entity.KEY_NAME: 
      raise RuntimeError, 'Key name generated for %s entity is same as '\ 
       'KEY_NAME template' % entity.entity_type() 

     return key_name 

    # Otherwise, the KEY_NAME is invalid 
    else: 
     raise TypeError, 'KEY_NAME of %s must be a string or callable' % (
      entity.entity_type()) 

Vous pouvez ensuite modifier votre modèle par exemple comme ceci:

class Foo(NamedModel): 
    KEY_NAME = '%(name)s' 
    name = db.StringProperty() 

Bien sûr, cela pourrait être considérablement simplifiée dans votre cas, en changeant la première ligne de la méthode de __init__NamedModel à quelque chose comme :

kwargs['key_name'] = kwargs['name'] 
+3

Ah, ça a l'air cool mais un peu exagéré. Peu importe, cela m'a quand même conduit sur la bonne voie pour découvrir ce qui me manquait: la connaissance de key_name! C'est la clé de tout :-) – Cameron

+0

Hahaha, eh bien, je suis content que je vous ai indiqué dans la bonne direction, au moins. –

+2

Approche intéressante. Bon exemple de comment surcharger __init__ de manière robuste, aussi. –