Daten in Datastore speichern


In diesem Teil der Anleitung zum Python-Gästebuch-Code wird gezeigt, wie Sie strukturierte Daten in Datastore speichern. Mit App Engine und Datastore müssen Sie sich nicht um die Verteilung, die Replikation und das Load-Balancing von Daten kümmern. Eine einfache API übernimmt diese Aufgaben für Sie – und bietet Ihnen eine leistungsstarke Abfrage-Engine und Transaktionen.

Diese Seite ist Teil einer mehrseitigen Anleitung. Wenn Sie die Anleitung von Anfang an durchgehen und eine Anleitung zur Einrichtung erhalten möchten, rufen Sie Gästebuchanwendungen erstellen auf.

Übermittelte Grußnachrichten speichern

In Datastore werden Daten in Objekte geschrieben, die als Entitäten bezeichnet werden. Jede Entität hat einen Schlüssel, der sie eindeutig identifiziert. Eine Entität kann eine andere Entität optional als übergeordnetes Element festlegen. Die erste Entität ist dann ein untergeordnetes Element der übergeordneten Entität. Die Entitäten im Datenspeicher bilden somit einen hierarchisch strukturierten Bereich ähnlich der Verzeichnisstruktur eines Dateisystems. Weitere Informationen finden Sie unter Daten für Strong Consistency strukturieren.

App Engine enthält eine Datenmodellierungs-API für Python. Die Beispielanwendung importiert das Modul google.appengine.ext.ndb, um die Datenmodellierungs-API zu verwenden. Jede Grußnachricht beinhaltet den Namen des Autors, den Inhalt der Nachricht sowie das Datum und die Uhrzeit der Nachrichtenübermittlung. Die Anwendung zeigt Nachrichten in chronologischer Reihenfolge an. Mit dem folgenden Code wird das Datenmodell definiert:

class Author(ndb.Model):
    """Sub model for representing an author."""
    identity = ndb.StringProperty(indexed=False)
    email = ndb.StringProperty(indexed=False)

class Greeting(ndb.Model):
    """A main model for representing an individual Guestbook entry."""
    author = ndb.StructuredProperty(Author)
    content = ndb.StringProperty(indexed=False)
    date = ndb.DateTimeProperty(auto_now_add=True)

Der Code definiert ein Greeting-Modell mit drei Attributen: author, dessen Wert ein Author-Objekt mit der E-Mail-Adresse und der Identität des Autors ist, content, dessen Wert ein String ist, sowie date, dessen Wert datetime.datetime ist.

Einige Attributkonstruktoren verwenden Parameter, um ihr Verhalten noch detaillierter zu konfigurieren. Bei der Übergabe des ndb.StringProperty-Konstruktors legt der Parameter indexed=False fest, dass die Werte für dieses Attribut nicht indexiert werden. Dadurch werden unnötige Schreibvorgänge eingespart, da die Anwendung dieses Attribut nie in einer Abfrage verwendet. Durch die Übergabe des ndb.DateTimeProperty-Konstruktors an den Parameter auto_now_add=True wird das Modell so konfiguriert, dass neue Objekte automatisch den datetime-Zeitstempel zum Zeitpunkt der Objekterstellung erhalten, sofern nicht durch die Anwendung anderweitig ein Wert festgelegt wird. Eine vollständige Liste der Attributtypen und ihrer Optionen finden Sie unter NDB-Attribute.

Die Anwendung erstellt mithilfe des Datenmodells neue Greeting-Objekte und fügt sie Datastore hinzu. Der Guestbook-Handler erstellt neue Grußnachrichten und speichert sie im Datenspeicher:

class Guestbook(webapp2.RequestHandler):

    def post(self):
        # We set the same parent key on the 'Greeting' to ensure each
        # Greeting is in the same entity group. Queries across the
        # single entity group will be consistent. However, the write
        # rate to a single entity group should be limited to
        # ~1/second.
        guestbook_name = self.request.get('guestbook_name',
                                          DEFAULT_GUESTBOOK_NAME)
        greeting = Greeting(parent=guestbook_key(guestbook_name))

        if users.get_current_user():
            greeting.author = Author(
                    identity=users.get_current_user().user_id(),
                    email=users.get_current_user().email())

        greeting.content = self.request.get('content')
        greeting.put()

        query_params = {'guestbook_name': guestbook_name}
        self.redirect('/?' + urllib.urlencode(query_params))

Dieser Guestbook-Handler erstellt ein neues Greeting-Objekt und legt anschließend dessen Attribute author und content gemäß den vom Nutzer übermittelten Daten fest. Das übergeordnete Element von Greeting ist eine Guestbook-Entität. Die Guestbook-Entität muss nicht erstellt werden, damit Sie sie als übergeordnetes Element einer anderen Entität festlegen können. In diesem Beispiel wird das übergeordnete Element als Platzhalter für Transaktions- und Konsistenzzwecke verwendet. Weitere Informationen finden Sie auf der Seite Transaktionen. Objekte, die einen gemeinsamen Ancestor haben, gehören zur selben Entitätengruppe. Das Attribut date wird nicht im Code festgelegt. Deshalb wird date mit auto_now_add=True automatisch auf das aktuelle Datum gesetzt.

Abschließend wird das neue Objekt durch greeting.put() im Datenspeicher abgelegt. Wenn wir dieses Objekt über eine Abfrage erhalten hätten, wäre das vorhandene Objekt durch put() aktualisiert worden. Da wir dieses Objekt mit dem Modellkonstruktor erstellt haben, fügt put() das neue Objekt dem Datenspeicher hinzu.

Abfragen in Datastore sind nur innerhalb von Entitätengruppen strikt konsistent. Deshalb werden im Code alle Grußnachrichten eines Gästebuchs derselben Entitätengruppe zugewiesen. Dafür wird für jede Grußnachricht das gleiche übergeordnete Element festgelegt. Dadurch wird die Grußnachricht eines Nutzers sofort angezeigt, nachdem sie verfasst wurde. Sie können jedoch immer nur einen Schreibvorgang pro Sekunde in derselben Entitätengruppe vornehmen. Wenn Sie eine reale Anwendung entwickeln, müssen Sie dies berücksichtigen. Durch die Nutzung von Diensten wie Memcache können Sie die Wahrscheinlichkeit verringern, dass einem Nutzer veraltete Ergebnisse angezeigt werden, wenn er nach einem Schreibvorgang über Entitätengruppen hinweg Abfragen durchführt.

Übermittelte Grußnachrichten abrufen

Datastore verfügt über eine ausgeklügelte Abfrage-Engine für Datenmodelle. Da Datastore keine herkömmliche relationale Datenbank ist, werden Abfragen nicht mit SQL erstellt. Stattdessen werden Daten auf zwei verschiedene Arten abgefragt: entweder mit Datastore-Abfragen oder mit einer SQL-ähnlichen Abfragesprache namens GQL. Um die gesamte Palette der Abfragefunktionen von Datastore nutzen zu können, empfehlen wir, Abfragen über GQL auszuführen.

Der MainPage-Handler ruft zuvor übermittelte Grußnachrichten ab und zeigt sie an. Der Aufruf greetings_query.fetch(10) führt die Abfrage aus.

Weitere Informationen zu Datenspeicherindexe

Jede Abfrage in Datastore wird anhand eines oder mehrerer Indexe berechnet. In diesen Tabellen werden geordneten Attributwerten Entitätsschlüssel zugewiesen. Dadurch können Ergebnisse in App Engine unabhängig von der Größe des Datenspeichers Ihrer Anwendung schnell bereitgestellt werden. Viele Abfragen lassen sich anhand der integrierten Indexe berechnen. Für komplexere Abfragen benötigt Datastore jedoch einen benutzerdefinierten Index. Ohne benutzerdefinierten Index kann Datastore diese Abfragen nicht effizient ausführen.

Die Gästebuchanwendung filtert zum Beispiel nach Gästebuch und sortiert nach Datum, wofür eine Ancestor-Abfrage und eine Sortierfolge verwendet werden. Dies erfordert einen benutzerdefinierten Index in der Datei index.yaml der Anwendung. Sie können diese Datei manuell bearbeiten oder automatisch bearbeiten lassen, indem Sie die Abfragen lokal in der Anwendung ausführen. Mit der Definition des Index in index.yaml werden beim Bereitstellen der Anwendung auch die benutzerdefinierten Indexinformationen bereitgestellt.

Die Definition für die Abfrage in index.yaml sieht in etwa so aus:

indexes:
- kind: Greeting
  ancestor: yes
  properties:
  - name: date
    direction: desc

Ausführliche Informationen zu Datastore-Indexen finden Sie auf der Seite Datenspeicherindexe. Informationen zur erforderlichen Spezifikation für index.yaml-Dateien erhalten Sie auf der Python-Seite unter Datenspeicherindexe konfigurieren.