

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Schritt 2: Überprüfen des Datenmodells und der Implementierungsdetails
<a name="TicTacToe.Phase2"></a>

**Topics**
+ [2.1: Grundlegendes Datenmodell](#TicTacToe.Phase2.DataModel)
+ [2.2: Anwendung in Aktion (Anleitung des Codes)](#TicTacToe.Phase2.AppInAction)

## 2.1: Grundlegendes Datenmodell
<a name="TicTacToe.Phase2.DataModel"></a>

Diese Beispielanwendung hebt folgende DynamoDB-Datenmodellkonzepte hervor:
+ ****Tabelle**** – In DynamoDB ist eine Tabelle eine Sammlung von Elementen (das bedeutet, Datensätzen) und jedes Element ist eine Sammlung von Name-Wert-Paaren, die Attribute genannt werden.

  In diesem Tic-Tac-Toe Beispiel speichert die Anwendung alle Spieldaten in einer Tabelle. `Games` Die Anwendung erstellt pro Spiel ein Element in der Tabelle und speichert alle Spieldaten als Attribute. Ein tic-tac-toe Spiel kann bis zu neun Züge haben. Da DynamoDB-Tabellen über kein Schema in Fällen verfügen, in denen nur der Primärschlüssel das erforderliche Attribut ist, kann die Anwendung eine unterschiedliche Anzahl von Attributen pro Spiel speichern.

  Die Tabelle `Games` verfügt über einen einfachen Primärschlüssel, der aus einem Attribut des Typs Zeichenfolge, `GameId`, besteht. Die Anwendung weist jedem Spiel eine eindeutige ID zu. Weitere Informationen zu DynamoDB-Primärschlüsseln finden Sie unter [Primärschlüssel](HowItWorks.CoreComponents.md#HowItWorks.CoreComponents.PrimaryKey). 

  Wenn ein Benutzer ein tic-tac-toe Spiel startet, indem er einen anderen Benutzer zum Spielen einlädt, erstellt die Anwendung ein neues Element in der `Games` Tabelle mit Attributen, in denen Spielmetadaten gespeichert werden, wie z. B. die folgenden:
  + `HostId`, der Benutzer, der das Spiel gestartet hat.
  + `Opponent`, der Benutzer, der zum Spielen eingeladen wurde.
  + Der Benutzer, der an der Reihe ist zu spielen. Der Benutzer, der das Spiel gestartet hat, beginnt als erster zu spielen.
  + Der Benutzer, der das Symbol **O** auf dem Brett verwendet. Der Benutzer, der die Spiele gestartet hat, verwendet das Symbol **O**.

  Zudem erstellt die Anwendung ein `StatusDate`-verkettetes Attribut, das den ersten Spielstatus als `PENDING` markiert. Der folgende Screenshot zeigt ein Beispielelement, wie es in der DynamoDB-Konsole angezeigt wird:  
![Konsolen-Screenshot der Attributtabelle.](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/tic-tac-toe-10.png)

  Während das Spiel voranschreitet, fügt die Anwendung der Tabelle ein Attribut für jeden Spielzug hinzu. Der Attributname ist die Brettposition, zum Beispiel `TopLeft` oder `BottomRight`. Ein Zug verfügt beispielsweise über ein `TopLeft`-Attribut mit dem Wert `O`, ein `TopRight`-Attribut mit dem Wert `O` und ein `BottomRight`-Attribut mit dem Wert `X`. Der Attributwert ist entweder `O` oder `X`, abhängig davon, welcher Benutzer den Zug gemacht hat. Betrachten Sie beispielsweise das folgende Board:  
![Screenshot, der ein abgeschlossenes tic-tac-toe Spiel zeigt, das mit einem Unentschieden endete.](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/tic-tac-toe-30.png)
+ ****Verkettete Wertattribute**** – Das `StatusDate`-Attribut zeigt ein verkettetes Wertattribut. Anstatt bei diesem Ansatz getrennte Attribute zu erstellen, um den Spielstatus (`PENDING`, `IN_PROGRESS` und `FINISHED`) und das Datum (wann der letzte Zug erfolgte) zu speichern, kombinieren Sie sie als ein einzelnes Attribut. Zum Beispiel `IN_PROGRESS_2014-04-30 10:20:32`.

  Die Anwendung nutzt dann das `StatusDate`-Attribut bei der Erstellung von sekundären Indizes, indem `StatusDate` als Sortierschlüssel für den Index angegeben wird. Der Vorteil eines `StatusDate`-verketteten Wertattributs wird in den Indizes, die im nächsten Abschnitt beschrieben werden, weiter dargestellt.
+ ****Globale sekundäre Indizes**** – Sie können den Primärschlüssel `GameId` der Tabelle nutzen, um die Tabelle effizient nach Spielelementen abzufragen. Um die Tabelle nach anderen Attributen als den Primärschlüsselattributen abzufragen, unterstützt DynamoDB die Erstellung von sekundären Indizes. In dieser Beispielanwendung erstellen Sie die folgenden zwei sekundären Indizes:   
![Screenshot, der die in der Beispielanwendung erstellten oppStatusDate globalen Sekundärindizes hostStatusDate und die globalen Sekundärindizes zeigt.](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/tic-tac-toe-indexes-10.png)
  + **HostId- StatusDate -index**. Dieser Index hat `HostId` als Partitionsschlüssel und `StatusDate` als Sortierschlüssel. Sie können diesen Index für die Abfrage von `HostId` verwenden, um zum Beispiel Spiele zu finden, die von einem bestimmten Benutzer gehostet wurden. 
  + **OpponentId- StatusDate -index**. Dieser Index hat `OpponentId` als Partitionsschlüssel und `StatusDate` als Sortierschlüssel. Sie können diesen Index für die Abfrage von `Opponent` verwenden, um zum Beispiel Spiele zu finden, in denen ein bestimmter Benutzer der Gegner ist.

  Diese Indizes werden globale sekundäre Indizes genannt, weil der Partitionsschlüssel in diesen Indizes nicht identisch mit dem Partitionsschlüssel (`GameId`) ist, der in dem Primärschlüssel der Tabelle verwendet wird. 

  Beachten Sie, dass beide Indizes `StatusDate` als Sortierschlüssel angeben. Dies ermöglicht Folgendes:
  + Sie können Abfragen mithilfe des `BEGINS_WITH`-Vergleichsoperators durchführen. Sie können beispielsweise alle Spiele mit dem `IN_PROGRESS`-Attribut, das von einem bestimmten Benutzer gehostet wurde, finden. In diesem Fall überprüft der `BEGINS_WITH`-Operator den `StatusDate`-Wert, der mit `IN_PROGRESS` beginnt.
  + DynamoDB speichert die Elemente in sortierter Reihenfolge nach Sortierschlüssel in den Index. Wenn also alle Statuspräfixe identisch sind (zum Beispiel `IN_PROGRESS`), wird das ISO-Format, das für den Datumsteil verwendet wird, die Elemente vom ältesten zum neuesten sortiert haben. Dieser Ansatz ermöglicht das effiziente Durchführen bestimmter Abfragen, z. B. die Folgenden: 
    + Abrufen von bis zu zehn der letzten `IN_PROGRESS`-Spiele, die der angemeldete Benutzer gehostet hat. Sie geben den `HostId-StatusDate-index`-Index für diese Abfrage an.
    + Abrufen von bis zu 10 der letzten `IN_PROGRESS`-Spiele, in denen der angemeldete Benutzer der Gegner ist. Sie geben den `OpponentId-StatusDate-index`-Index für diese Abfrage an.

Weitere Informationen zu den sekundären Indizes finden Sie unter [Verbessern des Datenzugriffs mit sekundären Indizes in DynamoDB](SecondaryIndexes.md).

## 2.2: Anwendung in Aktion (Anleitung des Codes)
<a name="TicTacToe.Phase2.AppInAction"></a>

Diese Anwendung hat zwei Hauptseiten:
+ ****Startseite**** — Diese Seite bietet dem Benutzer eine einfache Anmeldung, eine **CREATE-Taste**, um ein neues tic-tac-toe Spiel zu erstellen, eine Liste der laufenden Spiele, den Spielverlauf und alle aktiven ausstehenden Spieleinladungen. 

  Die Homepage wird nicht automatisch aktualisiert. Sie müssen die Seite aktualisieren, um die Listen zu aktualisieren.
+ ****Spielseite — Auf**** dieser Seite wird das Spielfeld angezeigt tic-tac-toe, in dem die Nutzer spielen. 

  Die Anwendung aktualisiert automatisch die Spielseite im Sekundentakt. Der JavaScript in Ihrem Browser ruft jede Sekunde den Python-Webserver auf, um die Spieltabelle abzufragen, ob sich die Spielelemente in der Tabelle geändert haben. Wenn dies der Fall ist, JavaScript wird eine Seitenaktualisierung ausgelöst, sodass der Benutzer das aktualisierte Board sieht.

Lassen Sie uns im Detail anschauen, wie die Anwendung funktioniert.

### Homepage
<a name="TicTacToe.Phase2.AppInAction.HomePage"></a>

Nachdem sich der Benutzer anmeldet, zeigt die Anwendung die folgenden drei Informationslisten.

![Screenshot mit der Homepage der Anwendung mit 3 Listen: ausstehende Einladungen, laufende Spiele und aktueller Verlauf.](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/tic-tac-toe-homepage-10.png)

+ ****Einladungen**** – Die Liste zeigt bis zu zehn der letzten Einladungen von anderen an, die der angemeldete Benutzer noch nicht angenommen hat. Im vorherigen Screenshot hat Benutzer1 ausstehende Einladungen von Benutzer5 und Benutzer2.
+ ****Laufende Spiele**** – Diese Liste zeigt bis zu zehn der letzten Spiele an, die im Gange sind. Das sind Spiele, die der Benutzer aktiv spielt, die den Status `IN_PROGRESS` haben. Im Screenshot spielt Benutzer1 aktiv ein tic-tac-toe Spiel mit Benutzer3 und Benutzer4.
+ ****Aktueller Verlauf**** – Die Liste zeigt bis zu zehn der letzten Spiele an, die der Benutzer beendet hat, die den Status `FINISHED` haben. In dem im Screenshot gezeigten Spiel hat Benutzer1 zuvor mit Benutzer2 gespielt. Die Liste zeigt für jedes abgeschlossene Spiel die Spielergebnis an.

In dem Code führt die `index`-Funktion (in `application.py`) die folgenden drei Aufrufe durch, um Informationen über den Spielstatus abzurufen:

```
inviteGames     = controller.getGameInvites(session["username"])
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames   = controller.getGamesWithStatus(session["username"], "FINISHED")
```

Jeder dieser Aufrufe gibt eine Liste von Elementen von DynamoDB zurück, die von den `Game`-Objekten umschlossen sind. Es ist einfach, Daten aus diesen Objekten in der Ansicht zu extrahieren. Die Indexfunktion übergibt diese Objektlisten an die Ansicht, um die HTML zu rendern.

```
return render_template("index.html",
                       user=session["username"],
                       invites=inviteGames,
                       inprogress=inProgressGames,
                       finished=finishedGames)
```

Die Tic-Tac-Toe Anwendung definiert die `Game` Klasse hauptsächlich zum Speichern von Spieldaten, die von DynamoDB abgerufen wurden. Diese Funktionen geben Listen von `Game`-Objekten zurück, die es Ihnen ermöglichen, den Rest der Anwendung von dem Code zu isolieren, der zu Amazon-DynamoDB-Elementen gehört. Daher helfen diese Funktionen Ihnen dabei, Ihren Anwendungscode von den Details der Datenspeicherebene abzukoppeln. 

Das hier beschriebene Architekturmuster wird auch als model-view-controller (MVC) UI-Muster bezeichnet. In diesem Fall sind die `Game`-Objekt-Instances (die Daten repräsentieren) das Modell und die HTML-Seite ist die Ansicht. Der Controller ist in zwei Dateien aufgeteilt. Die Datei `application.py` hat die Controllerlogik für das Flask-Framework und die Geschäftslogik wird in der Datei `gameController.py` isoliert. Das bedeutet, dass die Anwendung alles speichert, was mit DynamoDB-SDK zu tun hat, in ihrer eigenen separaten Datei im Ordner `dynamodb`.

Betrachten wir die drei Funktionen und wie sie die Spiele-Tabelle mithilfe der globalen sekundären Indizes abfragen, um relevante Daten abzurufen.

#### Wird verwendet getGameInvites , um die Liste der ausstehenden Spieleinladungen abzurufen
<a name="TicTacToe.Phase2.GameInAction.ListInvitations"></a>

Die `getGameInvites`-Funktion ruft die Liste der zehn letzten ausstehenden Einladungen ab. Diese Spiele wurden von Benutzern erstellt, aber die Gegner haben die Spieleinladungen nicht angenommen. Für diese Spiele lautet der Status weiterhin `PENDING`, bis der Gegner die Einladung annimmt. Wenn der Gegner die Einladung ablehnt, entfernt die Anwendung das entsprechende Element aus der Tabelle. 

Die Funktion gibt die Abfrage wie folgt an:
+ Sie gibt den Index `OpponentId-StatusDate-index` an, der mit den folgenden Indexschlüsselwerten und Vergleichsoperatoren verwendet wird:
  + Der Partitionsschlüssel ist `OpponentId` und übernimmt den Indexschlüssel `{{user ID}}`.
  + Der Sortierschlüssel ist `StatusDate` und übernimmt den Vergleichsoperator und den Indexschlüsselwert `beginswith="PENDING_"`.

  Sie nutzen den `OpponentId-StatusDate-index`-Index, um Spiele abzurufen, für die der angemeldete Benutzer eingeladen ist – das bedeutet, in denen der angemeldete Benutzer der Gegner ist.
+ Die Abfrage begrenzt das Ergebnis auf zehn Elemente.

```
gameInvitesIndex = self.cm.getGamesTable().query(
                                            Opponent__eq=user,
                                            StatusDate__beginswith="PENDING_",
                                            index="OpponentId-StatusDate-index",
                                            limit=10)
```

Für jede `OpponentId` (Partitionsschlüssel) im Index behält DynamoDB die Sortierung der Elemente nach `StatusDate` (Sortierschlüssel) bei. Daher werden ausschließlich die zehn letzten Spiele von der Abfrage zurückgegeben.

#### Verwenden von getGamesWith Status, um die Liste der Spiele mit einem bestimmten Status abzurufen
<a name="TicTacToe.Phase2.GameInAction.ListGamesInProgressHistory"></a>

Nachdem ein Gegner eine Spieleinladung angenommen hat, ändert sich der Status in `IN_PROGRESS`. Nach dem Beenden des Spiels ändert sich der Status in `FINISHED`. 

Abfragen für das Finden von Spielen, die entweder in Bearbeitung oder abgeschlossen sind, sind mit Ausnahme des unterschiedlichen Statuswerts identisch. Daher definiert die Anwendung die `getGamesWithStatus`-Funktion, die den Statuswert als Parameter übernimmt. 

```
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames   = controller.getGamesWithStatus(session["username"], "FINISHED")
```

Der folgende Abschnitt erläutert laufende Spiele, aber die gleiche Beschreibung gilt ebenso für beendete Spiele.

Eine Liste der laufenden Spiele für einen bestimmten Benutzer umfasst die beiden Folgenden: 
+ Laufende Spiele, die von dem Benutzer gehostet werden 
+ Laufende Spiele, in denen der Benutzer der Gegner ist 

Die `getGamesWithStatus`-Funktion führt die folgenden zwei Abfragen aus und verwendet jedes Mal den entsprechenden sekundären Index. 
+ Die Funktion fragt die Tabelle `Games` mithilfe des Index `HostId-StatusDate-index` ab. Die Abfrage gibt für den Index Primärschlüsselwert an – die Partitionsschlüssel- (`HostId`) und die Sortierschlüsselwerte (`StatusDate`), zusammen mit Vergleichsoperatoren. 

  ```
  hostGamesInProgress = self.cm.getGamesTable ().query(HostId__eq=user,
                                                 StatusDate__beginswith=status,
                                                 index="HostId-StatusDate-index",
                                                 limit=10)
  ```

  Beachten Sie die Python-Syntax für Vergleichsoperatoren:
  + `HostId__eq=user` gibt den Gleichheitsvergleichsoperator an.
  + `StatusDate__beginswith=status` gibt den `BEGINS_WITH`-Vergleichsoperator an.
+ Die Funktion fragt die Tabelle `Games` mithilfe des Index `OpponentId-StatusDate-index` ab. 

  ```
  oppGamesInProgress = self.cm.getGamesTable().query(Opponent__eq=user,
                                               StatusDate__beginswith=status,
                                               index="OpponentId-StatusDate-index",
                                               limit=10)
  ```
+ Die Funktion kombiniert dann die beiden Listen, sortiert sie und erstellt für die ersten 0 bis 10 Elemente eine Liste der `Game`-Objekte. Diese Liste gibt sie der aufrufenden Funktion (das bedeutet, dem Index) zurück.

  ```
  games = self.mergeQueries(hostGamesInProgress,
                          oppGamesInProgress)
  return games
  ```

### Spielseite
<a name="TicTacToe.Phase2.AppInAction.GamePage"></a>

Auf der Spieleseite spielt der Benutzer tic-tac-toe Spiele. Sie zeigt das Spielraster zusammen mit spielrelevanten Informationen an. Der folgende Screenshot zeigt ein laufendes Beispielspiel:

![Screenshot, der ein tic-tac-toe laufendes Spiel zeigt.](http://docs.aws.amazon.com/de_de/amazondynamodb/latest/developerguide/images/tic-tac-toe-example-board-10.png)


Die Anwendung zeigt die Spielseite in den folgenden Situationen an:
+ Der Benutzer erstellt ein Spiel, um einen anderen Benutzer zum Spielen einzuladen. 

  In diesem Fall zeigt die Seite den Benutzer als Host und den Spielstatus als `PENDING` an, während darauf gewartet wird, dass der Gegner akzeptiert.
+ Der Benutzer akzeptiert eine der ausstehenden Einladungen auf der Homepage. 

  In diesem Fall zeigt die Seite den Benutzer als Gegner und den Spielstatus als `IN_PROGRESS` an.

Eine Benutzerauswahl auf dem Board generiert die Formularanforderung `POST` für die Anwendung. Das bedeutet, dass Flask die `selectSquare`-Funktion (in `application.py`) mit den HTML-Formulardaten aufruft. Diese Funktion ruft wiederum die `updateBoardAndTurn`-Funktion (in `gameController.py`) auf, um das Spielelement wie folgt zu aktualisieren:
+ Sie fügt ein neues Attribut hinzu, das für den Zug spezifisch ist.
+ Sie aktualisiert den Attributwert `Turn` für den Benutzer, der an der Reihe ist.

```
controller.updateBoardAndTurn(item, value, session["username"])
```

Die Funktion gibt True zurück, wenn das Element erfolgreich aktualisiert wurde; andernfalls False. Beachten Sie Folgendes zu der `updateBoardAndTurn`-Funktion:
+ Die Funktion ruft die `update_item`-Funktion des SDK for Python auf, um eine begrenzte Reihe von Aktualisierungen für ein vorhandenes Element durchzuführen. Die Funktion wird der Operation `UpdateItem` in DynamoDB zugeordnet. Weitere Informationen finden Sie unter [UpdateItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html). 
**Anmerkung**  
Der Unterschied zwischen der `UpdateItem`- und der `PutItem`-Operation besteht darin, dass `PutItem` das gesamte Element ersetzt. Weitere Informationen finden Sie unter [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html).

Für den `update_item`-Aufruf identifiziert der Code Folgendes:
+ Den Primärschlüssel der Tabelle `Games` (d. h. `ItemId`).

  ```
  key = { "GameId" : { "S" : gameId } }
  ```
+ Das neue hinzuzufügende Attribut, das für den aktuellen Benutzerzug spezifisch ist und sein Wert (zum Beispiel `TopLeft="X"`).

  ```
  attributeUpdates = {
      position : {
          "Action" : "PUT",
          "Value" : { "S" : representation }
      }
  }
  ```
+ Bedingungen, die erfüllt sein müssen, damit die Aktualisierung ausgeführt werden kann:
  + Das Spiel muss in Bearbeitung sein. Das bedeutet, dass der `StatusDate`-Attributwert mit `IN_PROGRESS` beginnen muss.
  + Der aktuelle Zug muss, wie von dem `Turn`-Attribut angegeben, ein gültiger Benutzerzug sein. 
  + Das Quadrat, das der Benutzer ausgewählt hat, muss verfügbar sein. Das bedeutet, dass das Attribut, das dem Quadrat entspricht, nicht existieren muss.

  ```
  expectations = {"StatusDate" : {"AttributeValueList": [{"S" : "IN_PROGRESS_"}],
      "ComparisonOperator": "BEGINS_WITH"},
      "Turn" : {"Value" : {"S" : current_player}},
      position : {"Exists" : False}}
  ```

Jetzt ruft die Funktion `update_item` auf, um das Element zu aktualisieren.

```
self.cm.db.update_item("Games", key=key,
    attribute_updates=attributeUpdates,
    expected=expectations)
```

Nach der Rückgabe der Funktion werden die `selectSquare`-Funktionsaufrufe wie im folgenden Beispiel gezeigt umgeleitet.

```
redirect("/game="+gameId) 
```

Dieser Aufruf bewirkt ein Aktualisieren des Browsers. Als Teil dieser Aktualisierung prüft die Anwendung, ob das Spiel mit einem Sieg oder einem Unentschieden beendet wurde. Wenn dies der Fall ist, aktualisiert die Anwendung das Spielelement entsprechend. 