Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Argomenti
2.1: Modello di dati di base
Questa applicazione di esempio mette in evidenza i concetti seguenti relativi al modello di dati di DynamoDB:
-
Tabella: in DynamoDB, una tabella è una raccolta di elementi (ovvero record) e ogni elemento è una raccolta di coppie nome-valore denominate attributi.
In questo Tic-Tac-Toe esempio, l'applicazione memorizza tutti i dati di gioco in una tabella,.
Games
L'applicazione crea una item nella tabella per ogni partita e archivia tutti i dati di gioco come attributi. Una tic-tac-toe partita può avere fino a nove mosse. Poiché le tabelle DynamoDB non hanno uno schema nei casi in cui l'attributo richiesto è solo la chiave primaria, l'applicazione può archiviare un numero variabile di attributi per elemento di gioco.La tabella
Games
ha una chiave primaria semplice costituita da un attributo,GameId
, di tipo stringa. L'applicazione assegna un ID univoco a ogni partita. Per ulteriori informazioni sulle chiavi primarie di DynamoDB, vedi Chiave primaria.Quando un utente inizia una tic-tac-toe partita invitando un altro utente a giocare, l'applicazione crea un nuovo elemento nella
Games
tabella con attributi che memorizzano i metadati del gioco, come i seguenti:-
HostId
, l'utente che ha avviato la partita. -
Opponent
, l'utente che è stato invitato a giocare. -
Utente che deve giocare il turno successivo. L'utente che avvia la partita gioca per primo.
-
Utente che usa il simbolo O sulla griglia di gioco. L'utente che avvia la partita usa il simbolo O.
L'applicazione crea anche un attributo concatenato
StatusDate
, contrassegnando lo stato iniziale della partita comePENDING
. Lo screenshot seguente mostra un elemento di esempio come appare nella console DynamoDB:Man mano che la partita procede, l'applicazione aggiunge un attributo alla tabella per ogni mossa nel gioco. Il nome dell'attributo è costituito dalla posizione sulla griglia, ad esempio
TopLeft
oBottomRight
. Una mossa, ad esempio, può avere un attributoTopLeft
con il valoreO
, un attributoTopRight
con il valoreO
e un attributoBottomRight
con il valoreX
. Il valore dell'attributo èO
oX
, a seconda dell'utente che ha fatto la mossa. Considera, ad esempio, la griglia seguente. -
-
Attributi dei valori concatenati: l'attributo
StatusDate
illustra un attributo valore concatenato. In base a questo approccio, anziché creare attributi separati per archiviare lo stato della partita (PENDING
,IN_PROGRESS
eFINISHED
) e la data (dell'ultima mossa), i valori vengono combinati in un unico attributo, ad esempioIN_PROGRESS_2014-04-30 10:20:32
.L'applicazione usa quindi l'attributo
StatusDate
per la creazione di indici secondari specificandoStatusDate
come chiave di ordinamento per l'indice. Il vantaggio dell'uso dell'attributo con valori concatenatiStatusDate
è spiegato ulteriormente negli indici illustrati più avanti. -
Indici secondari globali: è possibile utilizzare la chiave primaria della tabella,
GameId
, per interrogare in modo efficiente la tabella per trovare un oggetto di gioco. Per eseguire query sulla tabella in base ad attributi diversi da quelli della chiave primaria, DynamoDB supporta la creazione di indici secondari. In questa applicazione di esempio vengono creati i due indici secondari seguenti:-
HostId- -indice. StatusDate Questo indice ha
HostId
come chiave di partizione eStatusDate
come chiave di ordinamento. Puoi usare l'indice per eseguire query suHostId
, ad esempio per trovare le partite ospitate da un utente specifico. -
OpponentId- StatusDate -indice. Questo indice ha
OpponentId
come chiave di partizione eStatusDate
come chiave di ordinamento. Puoi usare l'indice per eseguire query suOpponent
, ad esempio per trovare le partite in cui un utente specifico è l'avversario.
Questi indici vengono detti indici secondari globali perché la loro chiave di partizione non corrisponde alla chiave di partizione (
GameId
) usata nella chiave primaria della tabella.Entrambi gli indici hanno
StatusDate
come chiave di ordinamento. Ciò permette quanto segue:-
Puoi eseguire query usando l'operatore di confronto
BEGINS_WITH
. Puoi ad esempio trovare tutte le partite con l'attributoIN_PROGRESS
ospitate da un utente specifico. In questo caso, l'operatoreBEGINS_WITH
cerca il valoreStatusDate
che inizia conIN_PROGRESS
. -
DynamoDB archivia gli elementi nell'indice ordinandoli in base al valore della chiave di ordinamento. Quindi, se tutti i prefissi di stato sono uguali (ad esempio,
IN_PROGRESS
), il ISO formato utilizzato per la parte relativa alla data avrà gli elementi ordinati dal più vecchio al più recente. Questo approccio permette di eseguire in modo efficace determinate query, come le seguenti:-
Recupera fino a 10 partite
IN_PROGRESS
più recenti ospitate dall'utente che ha eseguito l'accesso. Per questa query, specifica l'indiceHostId-StatusDate-index
. -
Recupera fino a 10 partite
IN_PROGRESS
più recenti in cui l'utente che ha eseguito l'accesso è l'avversario. Per questa query, specifica l'indiceOpponentId-StatusDate-index
.
-
-
Per ulteriori informazioni sugli indici secondari, consulta Miglioramento dell'accesso ai dati con indici secondari in DynamoDB.
2.2: Funzionamento dell'applicazione (procedura guidata per il codice)
Questa applicazione ha due pagine principali:
-
Pagina iniziale: questa pagina fornisce all'utente un accesso semplice, un CREATEpulsante per creare una nuova tic-tac-toe partita, un elenco delle partite in corso, la cronologia delle partite e tutti gli inviti a giocare attivi in sospeso.
La home page non si aggiorna automaticamente, ma deve essere aggiornata manualmente per aggiornare gli elenchi.
-
Pagina del gioco: questa pagina mostra la tic-tac-toe griglia in cui giocano gli utenti.
L'applicazione aggiorna la pagina del gioco automaticamente ogni secondo. JavaScript Nel tuo browser richiama il server web Python ogni secondo per chiedere alla tabella Giochi se gli elementi del gioco nella tabella sono cambiati. In caso affermativo, JavaScript attiva un aggiornamento della pagina in modo che l'utente veda la bacheca aggiornata.
Analizziamo nel dettaglio il funzionamento dell'applicazione.
Home page
Dopo l'accesso dell'utente, l'applicazione visualizza i tre elenchi di informazioni seguenti.

-
Inviti: in questo elenco vengono visualizzati fino a 10 inviti più recenti da altri che sono in attesa di accettazione da parte dell'utente che ha eseguito l'accesso. Nello screenshot precedente l'utente user1 ha inviti in attesa da user5 e user2.
-
Giochi in corso: questo elenco mostra fino ai 10 giochi più recenti in corso. Si tratta delle partite a cui l'utente sta giocando, che hanno stato
IN_PROGRESS
. Nello screenshot, l'utente1 sta giocando attivamente con user3 e tic-tac-toe user4. -
Cronologia recente: questo elenco mostra fino ai 10 giochi più recenti che l'utente ha terminato, che hanno lo stato
FINISHED
. Nello screenshot user1 ha giocato in precedenza con user2. Per ogni partita completata viene indicato il risultato.
Nel codice la funzione index
(in application.py
) esegue le tre chiamate seguenti per recuperare informazioni sullo stato della partita:
inviteGames = controller.getGameInvites(session["username"])
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames = controller.getGamesWithStatus(session["username"], "FINISHED")
Ognuna di queste chiamate restituisce un elenco di elementi di DynamoDB inclusi in oggetti Game
. È possibile estrarre i dati da questi oggetti nella vista in modo semplice. La funzione index passa questi elenchi di oggetti alla vista per renderizzare. HTML
return render_template("index.html",
user=session["username"],
invites=inviteGames,
inprogress=inProgressGames,
finished=finishedGames)
L' Tic-Tac-Toeapplicazione definisce la Game
classe principalmente per archiviare i dati di gioco recuperati da DynamoDB. Queste funzioni restituiscono elenchi di oggetti Game
che permettono di isolare il resto dell'applicazione dal codice correlato agli elementi Amazon DynamoDB. Queste funzioni aiutano quindi a separare il codice dell'applicazione dai dettagli del livello datastore.
Il pattern architettonico qui descritto viene anche chiamato pattern model-view-controller (MVC) UI. In questo caso, le istanze Game
dell'oggetto (che rappresentano i dati) sono il modello e la HTML pagina è la vista. Il controller è diviso in due file. Il file application.py
ha la logica del controller per il framework Flask e la logica di business è isolata nel file gameController.py
. Cioè, l'applicazione archivia tutto ciò che ha a che fare con SDK DynamoDB in un file separato all'interno della cartella. dynamodb
Esaminiamo le tre funzioni e come eseguono query sulla tabella Games usando gli indici secondari globali per recuperare i dati rilevanti.
Si usa getGameInvites per ottenere l'elenco degli inviti alle partite in sospeso
La funzione getGameInvites
restituisce l'elenco dei 10 inviti in attesa più recenti. Queste partite sono state create dagli utenti, ma gli avversari non hanno accettato l'invito. Per queste partite, lo stato rimane PENDING
fino a quando l'avversario non accetta l'invito. Se l'avversario rifiuta l'invito, l'applicazione rimuove l'item corrispondente dalla tabella.
La funzione specifica la query come segue:
-
Specifica l'indice
OpponentId-StatusDate-index
da usare con i valori delle chiavi di indice e gli operatori di confronto seguenti:-
La chiave di partizione è
OpponentId
e accetta la chiave di indice
.user ID
-
La chiave di ordinamento è
StatusDate
e accetta l'operatore di confronto e il valore della chiave di indicebeginswith="PENDING_"
.
È possibile utilizzare l'indice
OpponentId-StatusDate-index
per recuperare i giochi a cui è invitato l'utente connesso, ovvero dove l'utente connesso è l'avversario. -
-
La query prevede un limite di 10 item nel risultato.
gameInvitesIndex = self.cm.getGamesTable().query(
Opponent__eq=user,
StatusDate__beginswith="PENDING_",
index="OpponentId-StatusDate-index",
limit=10)
Nell'indice, per ogni valore OpponentId
(la chiave di partizione) DynamoDB mantiene gli elementi ordinati in base a StatusDate
(la chiave di ordinamento). Le partite restituite dalla query saranno quindi le 10 più recenti.
Utilizzo di getGamesWith Status per ottenere l'elenco dei giochi con uno stato specifico
Quando un avversario accetta un invito a una partita, lo stato diventa IN_PROGRESS
. Al termine della partita, lo stato cambia in FINISHED
.
Le query per trovare le partite in corso o terminate sono uguali, ad eccezione del valore di stato diverso. L'applicazione definisce quindi la funzione getGamesWithStatus
, che accetta un valore di stato come parametro.
inProgressGames = controller.getGamesWithStatus(session["username"], "IN_PROGRESS")
finishedGames = controller.getGamesWithStatus(session["username"], "FINISHED")
La sezione seguente si riferisce alle partite in corso, ma la stessa descrizione si applica alle partite terminate.
Un elenco delle partite in corso per un utente specifico include entrambi i tipi seguenti:
-
Partite in corso ospitate dall'utente
-
Partite in corso in cui l'utente è l'avversario
La funzione getGamesWithStatus
esegue le due query seguenti, ogni volta usando l'indice secondario appropriato.
-
La funzione esegue una query sulla tabella
Games
usando l'indiceHostId-StatusDate-index
. Per l'indice, la query specifica i valori della chiave primaria, sia la chiave di partizione (HostId
) che la chiave di ordinamento (StatusDate
), insieme agli operatori di confronto.hostGamesInProgress = self.cm.getGamesTable ().query(HostId__eq=user, StatusDate__beginswith=status, index="HostId-StatusDate-index", limit=10)
Ecco la sintassi Python per gli operatori di confronto:
-
HostId__eq=user
specifica l'operatore di confronto di uguaglianza. -
StatusDate__beginswith=status
specifica l'operatore di confrontoBEGINS_WITH
.
-
-
La funzione esegue una query sulla tabella
Games
usando l'indiceOpponentId-StatusDate-index
.oppGamesInProgress = self.cm.getGamesTable().query(Opponent__eq=user, StatusDate__beginswith=status, index="OpponentId-StatusDate-index", limit=10)
-
La funzione combina quindi i due elenchi, li ordina, crea un elenco di oggetti
Game
per i primi da 0 a 10 item e restituisce l'elenco alla funzione chiamante, ovvero all'indice.games = self.mergeQueries(hostGamesInProgress, oppGamesInProgress) return games
Pagina del gioco
La pagina del gioco è dove l'utente tic-tac-toe gioca. In questa pagina sono presenti la griglia di gioco e informazioni sulla partita. Lo screenshot seguente mostra una partita di esempio in corso:

L'applicazione visualizza la pagina del gioco nelle situazioni seguenti:
-
L'utente crea una partita invitando un altro utente a giocare.
In questo caso, la pagina mostra l'utente come host e lo stato della partita come
PENDING
, in attesa che l'avversario accetti l'invito. -
L'utente accetta uno degli inviti in attesa nella home page.
In questo caso, la pagina mostra l'utente come avversario e lo stato della partita come
IN_PROGRESS
.
Quando l'utente effettua una selezione sulla griglia, viene generata una richiesta POST
per l'applicazione. Cioè, Flask chiama la selectSquare
funzione (inapplication.py
) con i dati del HTML modulo. Questa funzione, a sua volta, chiama la funzione updateBoardAndTurn
(in gameController.py
) per aggiornare l'item di gioco come indicato di seguito:
-
Aggiunge un nuovo attributo specifico della mossa.
-
Aggiorna il valore dell'attributo
Turn
in base all'utente che deve giocare il turno successivo.
controller.updateBoardAndTurn(item, value, session["username"])
La funzione restituisce true se l'aggiornamento dell'item è stato eseguito correttamente, altrimenti restituisce false. Tieni presente quanto segue in relazione alla funzione updateBoardAndTurn
:
-
La funzione chiama la
update_item
funzione di SDK for Python per apportare un insieme finito di aggiornamenti a un elemento esistente. La funzione è mappata all'operazioneUpdateItem
in DynamoDB. Per ulteriori informazioni, consulta UpdateItem.Nota
La differenza tra le operazioni
UpdateItem
ePutItem
è legata al fatto chePutItem
sostituisce l'intero item. Per ulteriori informazioni, consulta PutItem.
Per la chiamata di update_item
, il codice identifica quanto segue:
-
La chiave primaria della tabella
Games
, ovveroItemId
.key = { "GameId" : { "S" : gameId } }
-
Il nuovo attributo da aggiungere, specifico della mossa dell'utente corrente, e il relativo valore (ad esempio
TopLeft="X"
).attributeUpdates = { position : { "Action" : "PUT", "Value" : { "S" : representation } } }
-
Le condizioni che devono essere soddisfatte affinché venga eseguito l'aggiornamento:
-
La partita deve essere in corso. Ciò significa che il valore dell'attributo
StatusDate
deve iniziare conIN_PROGRESS
. -
Il turno corrente deve essere un turno utente valido come specificato dall'attributo
Turn
. -
Il quadrato della griglia scelto dall'utente deve essere disponibile. Ciò significa che l'attributo corrispondente al quadrato non deve esistere.
expectations = {"StatusDate" : {"AttributeValueList": [{"S" : "IN_PROGRESS_"}], "ComparisonOperator": "BEGINS_WITH"}, "Turn" : {"Value" : {"S" : current_player}}, position : {"Exists" : False}}
-
La funzione chiama ora update_item
per aggiornare l'item.
self.cm.db.update_item("Games", key=key,
attribute_updates=attributeUpdates,
expected=expectations)
Dopo che la funzione restituisce il risultato, la funzione selectSquare
chiama il metodo redirect, come illustrato nell'esempio seguente.
redirect("/game="+gameId)
Questa chiamata causa l'aggiornamento del browser. Nel corso di questo aggiornamento, l'applicazione verifica se la partita si è conclusa con una vittoria o un pareggio. In caso affermativo, l'applicazione aggiorna l'item di gioco di conseguenza.