Fino ad ora abbiamo usato solo metodi di tipo mask: Impareremo ora come usare view e function.
CherryClass Root: variable: # Sample book list data. In real life, this would probably come from a database # (title, author, price) bookListData=[ ('Harry Potter and the Goblet of Fire', 'J. K. Rowling', '9$'), ('The flying cherry', 'Remi Delon', '5$'), ('I love cherry pie', 'Eric Williams', '6$'), ('CherryPy rules', 'David stults', '7$') ] function: def getBookListData(self): return self.bookListData def getBookData(self, id): return self.bookListData[id] mask: def index(self): <html><body> Hi, choose a book from the list below:<br> <py-for="title, dummy, dummy in self.getBookListData()"> <a py-attr="'displayBook?id=%s'%_index" href="" py-eval="title"></a><br> </py-for> </body></html> def displayBook(self, id): <html><body> <py-exec="title, author, price=self.getBookData(int(id))"> Details about the book:<br> Title: <py-eval="title"><br> Author: <py-eval="author"><br> Price: <py-eval="price"><br> </body></html>
Andiamo a vedere un esempio leggermente più complicato...
In questo esempio aggiungeremo alcune caratteristiche al nostro sito web:
Questo significa che avremo bisogno di 6 tipi di pagine:
Se volessimo mantenere la stessa architettura del primo esempio, dovremmo scrivere 6 maschere (più le funzioni). Proviamo a fare qualcosa di meglio ...
Non c'è molto da fare per le ultime 2 pagine (la 5 e la 6). Ma per le prime quattro, possiamo in effetti usare 2 funzioni e 2 maschere. Combinando ogni funzione con ogni maschera, abbiamo le nostre 4 combinazioni (2 per 2). Useremo ciò che segue:
Per permettere l'unione tra maschera e funzione, useremo una view. Questo significa che avremo 4 view, una per ogni combinazione. Ogni view avrà un codice veramente semplice: applica questa maschera al risultato di questa funzione
Il codice per il nostro sito web è il seguente:
CherryClass Root: variable: # Sample book list data. In real life, this would probably come from a database # (title, author, price) bookListData=[ ('Harry Potter and the Goblet of Fire', 'J. K. Rowling', '9$'), ('The flying cherry', 'Remi Delon', '5$'), ('I love cherry pie', 'Eric Williams', '6$'), ('CherryPy rules', 'David Stults', '7$') ] function: def getBookListByTitleData(self): titleList=[] for title, dummy, dummy in self.bookListData: titleList.append(title) return titleList def getBookListByAuthorData(self): authorList=[] for dummy, author, dummy in self.bookListData: authorList.append(author) return authorList def getBookData(self, id): return self.bookListData[id] mask: def bookListInEnglishMask(self, myBookListData): Hi, choose a book from the list below:<br> <py-for="data in myBookListData"> <a py-attr="'displayBookInEnglish?id=%s'%_index" href="" py-eval="data"></a><br> </py-for> <br> def bookListInFrenchMask(self, myBookListData): Bonjour, choisissez un livre de la liste:<br> <py-for="data in myBookListData"> <a py-attr="'displayBookInFrench?id=%s'%_index" href="" py-eval="data"></a><br> </py-for> <br> def displayBookInEnglish(self, id): <html><body> <py-exec="title, author, price=self.getBookData(int(id))"> Details about the book:<br> Title: <py-eval="title"><br> Author: <py-eval="author"><br> Price: <py-eval="price"><br> <br> <a py-attr="'displayBookInFrench?id=%s'%id" href="">Version francaise</a> </body></html> def displayBookInFrench(self, id): <html><body> <py-exec="title, author, price=self.getBookData(int(id))"> Details du livre:<br> Titre: <py-eval="title"><br> Auteur: <py-eval="author"><br> Prix: <py-eval="price"><br> <br> <a py-attr="'displayBookInEnglish?id=%s'%id" href="">English version</a> </body></html>
view: def englishByTitle(self): page="<html><body>" byTitleData=self.getBookListByTitleData() page+=self.bookListInEnglishMask(byTitleData) page+='<a href="englishByAuthor">View books by author</a><br>' page+='<a href="frenchByTitle">Version francaise</a>' page+="</body></html>" return page def frenchByTitle(self): page="<html><body>" byTitleData=self.getBookListByTitleData() page+=self.bookListInFrenchMask(byTitleData) page+='<a href="frenchByAuthor">Voir les livres par auteur</a><br>' page+='<a href="englishByTitle">English version</a>' page+="</body></html>" return page def englishByAuthor(self): page="<html><body>" byTitleData=self.getBookListByAuthorData() page+=self.bookListInEnglishMask(byTitleData) page+='<a href="englishByTitle">View books by title</a><br>' page+='<a href="frenchByAuthor">Version francaise</a>' page+="</body></html>" return page def frenchByAuthor(self): page="<html><body>" byTitleData=self.getBookListByAuthorData() page+=self.bookListInFrenchMask(byTitleData) page+='<a href="frenchByTitle">Voir les livres par titre</a><br>' page+='<a href="englishByAuthor">English version</a>' page+="</body></html>" return page def index(self): # By default, display books by title in English return self.englishByTitle()
Alternativamente, avremmo potuto risparmiare alcune lnee di codice passando la lingua (Francese o Inglese) e il tipo di lista (titolo o autore) come parametri. In questo modo, non avremmo avuto bisogno di usare view, e le maschere avrebbero potuto essere chiamata direttamente...
Editate Hello.cpy ed inserite le seguenti righe di codice:
CherryClass Root: function: def prepareTableData(self, N, C): # Prepare data that will be rendered in the table # Example, for N=10 and C=3, it will return: # [[1,2,3], # [4,5,6], # [7,8,9], # [10]] N=int(N) C=int(C) tableData=[] i=1 while 1: rowData=[] for c in range(C): rowData.append(i) i+=1 if i>N: break tableData.append(rowData) if i>N: break return tableData view: def viewResult(self, N, C): tableData=self.prepareTableData(N,C) return self.renderTableData(tableData) mask: def renderTableData(self, tableData): # Renders tableData in a table <html><body> <table border=1> <div py-for="rowData in tableData"> <tr> <div py-for="columnValue in rowData"> <td py-eval="columnValue"></td> </div> </tr> </div> </table> </body></html> def index(self): <html><body> <form py-attr="request.base+'/viewResult'" action=""> Integer between 20 and 50: <input type=text name=N><br> Number of columns between 2 and 10: <input type=text name=C><br> <input type=submit> </form> </body></html>
Come funziona?
La maschera index è facile da capire ed è usata solo per inserire N e C.
La funzione prepareTableData è usata per processare N e C e per calcolare una lista di liste che sarà pronta per la visualizzazione. La maschera renderTableData prende in ingresso il ritorno di prepareTableData e lo visualizza. La view viewResult è un collegamento tra i due. Basilarmente diciamo di calcolare il risulato di una funzione e applicarvi una maschera.
Ora cosa possiamo fare se volessimo visualizzare gli interi per colonna invece che per riga?
Dobbiamo creare una nuova maschera e aggiornare la view in modo da applicare la nuova maschera ai dati.
Modifichiamo Hello.cpy come segue:
CherryClass Root: function: def prepareTableData(self, N, C): N=int(N) C=int(C) tableData=[] i=1 while 1: rowData=[] for c in range(C): rowData.append(i) i+=1 if i>N: break tableData.append(rowData) if i>N: break return tableData view: def viewResult(self, N, C, displayBy): tableData=self.prepareTableData(N,C) if displayBy=="line": mask=self.renderTableDataByLine else: mask=self.renderTableDataByColumn return mask(tableData) mask: def renderTableDataByLine(self, tableData): <html><body> <table border=1> <div py-for="rowData in tableData"> <tr> <div py-for="columnValue in rowData"> <td py-eval="columnValue"></td> </div> </tr> </div> </table> </body></html> def renderTableDataByColumn(self, tableData): <html><body> <table border=1> <tr> <div py-for="rowData in tableData"> <td valign=top> <div py-for="columnValue in rowData"> <div py-eval="columnValue"></div><br> </div> </td> </div> </tr> </table> </body></html>
def index(self): <html><body> <form py-attr="request.base+'/viewResult'" action=""> Integer between 20 and 50: <input type=text name=N><br> Number of columns (or lines) between 2 and 10: <input type=text name=C><br> Display result by: <select name=displayBy> <option>line</option> <option>column</option> </select><br> <input type=submit> </form> </body></html>
Abbiamo rinominato la maschera renderTableData in renderTableDataByLine, abbiamo quindi chiamato renderTableDataByColumn. viewResult ha adesso il parametro displayBy che è immesso dall'utente. Basandosi su questo, viewResult seleziona la maschera da applicare al risultato della funzione prepareTableData (che non è cambiata).
Facciamo ora alcuni test: nel nostro browser digitiamo l'URL: http://localhost:8000/prepareTableData?N=30&C=5
Dovreste ricevere il seguente errore:
CherryError: CherryClass "root" doesn't have any view or mask function called "prepareTableData"
Cosa abbiamo imparato:
Nota: all'interno di una dichiarazione di una CherryClass, le sezioni differenti (function, mask o view) possono apparire in qualsiasi ordine, il numero di volte che vogliamo.
Nel prossimo capitolo vedremo come CherryPy determina che metodo chiamare in base all'URL...
See About this document... for information on suggesting changes.