12

Zunächst einmal bin ich relativ neu in Google App Engine, also mache ich wahrscheinlich etwas Dummes.Benutzerdefinierte Schlüssel für Google App Engine-Modelle (Python)

sagen, dass ich ein Modell Foo haben:

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

Ich möchte für jedes Objekt Fooname als eindeutiger Schlüssel verwenden. Wie wird das gemacht?

Wenn ich ein bestimmtes Foo Objekt erhalten möchten, ich Abfrage zur Zeit den Datenspeicher für alle Foo Objekte mit dem Ziel eindeutigen Namen, aber Abfragen sind langsam (plus es ist ein Schmerz, um sicherzustellen, dass name einzigartig ist, wenn jeder neue Foo erstellt).

Es muss einen besseren Weg geben, dies zu tun!

Danke.

Antwort

13

ich vor dem nachfolgenden Code in einem Projekt verwendet habe. Es funktioniert, solange das Feld, auf dem Sie Ihren Schlüsselnamen basieren, benötigt wird.

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()) 

Man könnte dann das Beispielmodell ändern wie folgt:

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

Natürlich ist dies dramatisch in Ihrem Fall könnte vereinfacht werden, um die erste Zeile der NamedModel ‚s __init__ Methode, um etwas zu ändern, wie :

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

Ah, das sieht cool aus, aber ein bisschen Overkill. Egal, es führte mich immer noch auf den richtigen Weg, um zu entdecken, was ich vermisste: Wissen über Schlüsselname! Es ist der Schlüssel zu allem :-) – Cameron

+0

Hahaha, nun, ich bin froh, dass ich dich wenigstens in die richtige Richtung wies. –

+2

Interessanter Ansatz. Gutes Beispiel dafür, wie __init__ auch robust überschrieben werden kann. –

Verwandte Themen