File: node8.html

package info (click to toggle)
cherrypy 0.10-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 10,324 kB
  • ctags: 1,759
  • sloc: python: 14,411; sh: 6,915; perl: 2,472; makefile: 76
file content (528 lines) | stat: -rw-r--r-- 20,171 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>6. Views, functions and more</title>
<META NAME="description" CONTENT="6. Views, functions and more">
<META NAME="keywords" CONTENT="tut">
<META NAME="resource-type" CONTENT="document">
<META NAME="distribution" CONTENT="global">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="STYLESHEET" href="tut.css" type='text/css'>
<link rel="first" href="tut.html">
<link rel="contents" href="contents.html" title="Contents">

<LINK REL="next" HREF="node9.html">
<LINK REL="previous" HREF="node7.html">
<LINK REL="up" HREF="tut.html">
<LINK REL="next" HREF="node9.html">
<meta name='aesop' content='information'>
</head>
<body>
<DIV CLASS="navigation">
<table align="center" width="100%" cellpadding="0" cellspacing="2">
<tr>
<td><A HREF="node7.html"><img src="../icons/previous.gif"
  border="0" height="32"
  alt="Previous Page" width="32"></A></td>
<td><A HREF="tut.html"><img src="../icons/up.gif"
  border="0" height="32"
  alt="Up One Level" width="32"></A></td>
<td><A HREF="node9.html"><img src="../icons/next.gif"
  border="0" height="32"
  alt="Next Page" width="32"></A></td>
<td align="center" width="100%">CherryPy Tutorial</td>
<td><A HREF="node2.html"><img src="../icons/contents.gif"
  border="0" height="32"
  alt="Contents" width="32"></A></td>
<td><img src="../icons/blank.gif"
  border="0" height="32"
  alt="" width="32"></td>
<td><img src="../icons/blank.gif"
  border="0" height="32"
  alt="" width="32"></td>
</tr></table>
<b class="navlabel">Previous:</b> <a class="sectref" HREF="node7.html">5. Templating languages: CHTL</A>
<b class="navlabel">Up:</b> <a class="sectref" HREF="tut.html">CherryPy Tutorial</A>
<b class="navlabel">Next:</b> <a class="sectref" HREF="node9.html">7. Class, instance, method</A>
<br><hr>
</DIV>
<!--End of Navigation Panel-->
<!--Table of Child-Links-->
<A NAME="CHILD_LINKS"><STRONG>Subsections</STRONG></a>

<UL CLASS="ChildLinks">
<LI><A href="node8.html#SECTION008100000000000000000">6.1 Different architectures for a web site source code</a>
<UL>
<LI><A href="node8.html#SECTION008110000000000000000">6.1.1 First example: straightforward architecture</a>
<LI><A href="node8.html#SECTION008120000000000000000">6.1.2 Second example: more elegant architecture for more complex web sites</a>
</ul>
<LI><A href="node8.html#SECTION008200000000000000000">6.2 More examples on using functions, masks and views together</a>
</ul>
<!--End of Table of Child-Links-->
<HR>

<H1><A NAME="SECTION008000000000000000000">
6. Views, functions and more</A>
</H1>

<P>
So far, we've only used one kind of method: masks. We are going to learn how to use views and functions.

<P>

<H1><A NAME="SECTION008100000000000000000">
6.1 Different architectures for a web site source code</A>
</H1>
We'll take two examples to show you two different ways to design the architecture of your code.

<P>

<H2><A NAME="SECTION008110000000000000000">
6.1.1 First example: straightforward architecture</A>
</H2>
Let's assume that you want to build a very simple web site where people can look for books and see the details about
one perticular book. The web site is made of two kinds of pages:

<UL>
<LI>The main page that displays the list of books. Each book name is a link.
</LI>
<LI>A page that displays the informations about a perticular book. This page is displayed then the user clicks on a book name
</LI>
</UL>
To implement this web site, we'll just use 2 functions and 2 masks:

<UL>
<LI>One function called <b>getBookListData</b> that returns a list of book names
</LI>
<LI>One function called <b>getBookData</b> that returns the detailed informations about a perticular book
</LI>
<LI>One mask called <b>index</b> that displays the list of book names.
</LI>
<LI>One mask called <b>displayBook</b> that displays the detailed informations about a perticular book
</LI>
</UL>
The code of the web site is:
<div class="verbatim"><pre>
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):
        &lt;html&gt;&lt;body&gt;
            Hi, choose a book from the list below:&lt;br&gt;
            &lt;py-for="title, dummy, dummy in self.getBookListData()"&gt;
                &lt;a py-attr="'displayBook?id=%s'%_index" href="" py-eval="title"&gt;&lt;/a&gt;&lt;br&gt;
            &lt;/py-for&gt;
        &lt;/body&gt;&lt;/html&gt;
    def displayBook(self, id):
        &lt;html&gt;&lt;body&gt;
            &lt;py-exec="title, author, price=self.getBookData(int(id))"&gt;
            Details about the book:&lt;br&gt;
            Title: &lt;py-eval="title"&gt;&lt;br&gt;
            Author: &lt;py-eval="author"&gt;&lt;br&gt;
            Price: &lt;py-eval="price"&gt;&lt;br&gt;
        &lt;/body&gt;&lt;/html&gt;
</pre></div>
As you can see, the code for this "mini" web site is pretty straightforward: each mask corresponds to a page
type. Since we have 2 types of pages, we use 2 masks.

<P>
Let's take a slightly more complicated example ...

<H2><A NAME="SECTION008120000000000000000">
6.1.2 Second example: more elegant architecture for more complex web sites</A>
</H2>

<P>
In this example, we'll add a few more features to our web site:

<UL>
<LI>This time, we want our web site to come in two languages: English and French
</LI>
<LI>In addition to being able to browse the books by title, we also want to be able to browse them by author
</LI>
</UL>

<P>
This now means that we have six types of pages:

<UL>
<LI>1. View book list in English broken up by title
</LI>
<LI>2. View book list in French broken up by title
</LI>
<LI>3. View book list in English broken up by author
</LI>
<LI>4. View book list in French broken up by author
</LI>
<LI>5. View book details in English
</LI>
<LI>6. View book details in French
</LI>
</UL>

<P>
If we were to keep the same architecture as the first example, we would have to write 6 masks (plus the functions). Let's try to do better than that ...

<P>
There isn't much we can do about the last 2 types of pages (5 and 6). But for the first four, we can in fact use 2 functions
and 2 masks. By combining each function with each mask, we have our 4 combinations (2 times 2). We'll use the
following:

<P>

<UL>
<LI>One function called <b>getBookListByTitleData</b> that returns a list of book broken up by title
</LI>
<LI>One function called <b>getBookListByAuthorData</b> that returns a list of book broken up by author
</LI>
<LI>One mask called <b>bookListInEnglishMask</b> that displays a list of books in English (it doesn't matter if the books
are broken up by title or by author).
</LI>
<LI>One mask called <b>bookListInFrenchMask</b> that displays a list of books in French.
</LI>
</UL>

<P>
In order to "link" a mask with a function, we'll use a view. This means that we have 4 views, one for each combination.
Each view will have a very simple code: <b>apply this mask to the result of that function</b>

<P>
The code for our web site looks like this:
<div class="verbatim"><pre>
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:&lt;br&gt;
            &lt;py-for="data in myBookListData"&gt;
                &lt;a py-attr="'displayBookInEnglish?id=%s'%_index" href="" py-eval="data"&gt;&lt;/a&gt;&lt;br&gt;
            &lt;/py-for&gt;
            &lt;br&gt;
    def bookListInFrenchMask(self, myBookListData):
            Bonjour, choisissez un livre de la liste:&lt;br&gt;
            &lt;py-for="data in myBookListData"&gt;
                &lt;a py-attr="'displayBookInFrench?id=%s'%_index" href="" py-eval="data"&gt;&lt;/a&gt;&lt;br&gt;
            &lt;/py-for&gt;
            &lt;br&gt;
    def displayBookInEnglish(self, id):
        &lt;html&gt;&lt;body&gt;
            &lt;py-exec="title, author, price=self.getBookData(int(id))"&gt;
            Details about the book:&lt;br&gt;
            Title: &lt;py-eval="title"&gt;&lt;br&gt;
            Author: &lt;py-eval="author"&gt;&lt;br&gt;
            Price: &lt;py-eval="price"&gt;&lt;br&gt;
            &lt;br&gt;
            &lt;a py-attr="'displayBookInFrench?id=%s'%id" href=""&gt;Version francaise&lt;/a&gt;
        &lt;/body&gt;&lt;/html&gt;
    def displayBookInFrench(self, id):
        &lt;html&gt;&lt;body&gt;
            &lt;py-exec="title, author, price=self.getBookData(int(id))"&gt;
            Details du livre:&lt;br&gt;
            Titre: &lt;py-eval="title"&gt;&lt;br&gt;
            Auteur: &lt;py-eval="author"&gt;&lt;br&gt;
            Prix: &lt;py-eval="price"&gt;&lt;br&gt;
            &lt;br&gt;
            &lt;a py-attr="'displayBookInEnglish?id=%s'%id" href=""&gt;English version&lt;/a&gt;
        &lt;/body&gt;&lt;/html&gt;
view:
    def englishByTitle(self):
        page="&lt;html&gt;&lt;body&gt;"
        byTitleData=self.getBookListByTitleData()
        page+=self.bookListInEnglishMask(byTitleData)
        page+='&lt;a href="englishByAuthor"&gt;View books by author&lt;/a&gt;&lt;br&gt;'
        page+='&lt;a href="frenchByTitle"&gt;Version francaise&lt;/a&gt;'
        page+="&lt;/body&gt;&lt;/html&gt;"
        return page
    def frenchByTitle(self):
        page="&lt;html&gt;&lt;body&gt;"
        byTitleData=self.getBookListByTitleData()
        page+=self.bookListInFrenchMask(byTitleData)
        page+='&lt;a href="frenchByAuthor"&gt;Voir les livres par auteur&lt;/a&gt;&lt;br&gt;'
        page+='&lt;a href="englishByTitle"&gt;English version&lt;/a&gt;'
        page+="&lt;/body&gt;&lt;/html&gt;"
        return page
    def englishByAuthor(self):
        page="&lt;html&gt;&lt;body&gt;"
        byTitleData=self.getBookListByAuthorData()
        page+=self.bookListInEnglishMask(byTitleData)
        page+='&lt;a href="englishByTitle"&gt;View books by title&lt;/a&gt;&lt;br&gt;'
        page+='&lt;a href="frenchByAuthor"&gt;Version francaise&lt;/a&gt;'
        page+="&lt;/body&gt;&lt;/html&gt;"
        return page
    def frenchByAuthor(self):
        page="&lt;html&gt;&lt;body&gt;"
        byTitleData=self.getBookListByAuthorData()
        page+=self.bookListInFrenchMask(byTitleData)
        page+='&lt;a href="frenchByTitle"&gt;Voir les livres par titre&lt;/a&gt;&lt;br&gt;'
        page+='&lt;a href="englishByAuthor"&gt;English version&lt;/a&gt;'
        page+="&lt;/body&gt;&lt;/html&gt;"
        return page
    def index(self):
        # By default, display books by title in English
        return self.englishByTitle()
</pre></div>

<P>
Alternatively, we could save even more lines of code by passing the language (French or English) and the type of list
(title or author) as parameters. This way, we wouldn't need to use views, and the masks could be called directly...

<P>

<H1><A NAME="SECTION008200000000000000000">
6.2 More examples on using functions, masks and views together</A>
</H1>
In this section, we'll build a small website thats prompts the user for an integer N between 20 and 50, and for
a number of columns C between 2 and 10. Then it will display integers from 1 to N in a table of C columns.

<P>
Edit <span class="file">Hello.cpy</span> and enter the following code:
<div class="verbatim"><pre>
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&gt;N: break
            tableData.append(rowData)
            if i&gt;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
        &lt;html&gt;&lt;body&gt;
        &lt;table border=1&gt;
            &lt;div py-for="rowData in tableData"&gt;
                &lt;tr&gt;
                    &lt;div py-for="columnValue in rowData"&gt;
                        &lt;td py-eval="columnValue"&gt;&lt;/td&gt;
                    &lt;/div&gt;
                &lt;/tr&gt;
            &lt;/div&gt;
        &lt;/table&gt;
        &lt;/body&gt;&lt;/html&gt;

    def index(self):
        &lt;html&gt;&lt;body&gt;
            &lt;form py-attr="request.base+'/viewResult'" action=""&gt;
                Integer between 20 and 50: &lt;input type=text name=N&gt;&lt;br&gt;
                Number of columns between 2 and 10: &lt;input type=text name=C&gt;&lt;br&gt;
                &lt;input type=submit&gt;
            &lt;/form&gt;
        &lt;/body&gt;&lt;/html&gt;
</pre></div>

<P>
How does it work ?

<P>
The <var>index</var> mask is easy to understand and is only used to input N and C.

<P>
The <var>prepareTableData</var> function is used to process N and C and to compute a list
of list that will be ready to render. The <var>renderTableData</var> mask takes as an input the
return value of <var>prepareTableData</var> and renders it. The <var>viewResult</var> view is the link
between the two. It basically says to compute the result of a function and to apply a
mask to it.

<P>
Now, what if we want to display the integers by column instead of displaying them by line ?

<P>
Well, we just need to create a new mask, and to update the view in order to apply the new
mask to the data.

<P>
Modify <span class="file">Hello.cpy</span> as follows:
<div class="verbatim"><pre>
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&gt;N: break
            tableData.append(rowData)
            if i&gt;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):
        &lt;html&gt;&lt;body&gt;
        &lt;table border=1&gt;
            &lt;div py-for="rowData in tableData"&gt;
                &lt;tr&gt;
                    &lt;div py-for="columnValue in rowData"&gt;
                        &lt;td py-eval="columnValue"&gt;&lt;/td&gt;
                    &lt;/div&gt;
                &lt;/tr&gt;
            &lt;/div&gt;
        &lt;/table&gt;
        &lt;/body&gt;&lt;/html&gt;
    def renderTableDataByColumn(self, tableData):
        &lt;html&gt;&lt;body&gt;
        &lt;table border=1&gt;
            &lt;tr&gt;
                &lt;div py-for="rowData in tableData"&gt;
                    &lt;td valign=top&gt;
                        &lt;div py-for="columnValue in rowData"&gt;
                            &lt;div py-eval="columnValue"&gt;&lt;/div&gt;&lt;br&gt;
                        &lt;/div&gt;
                    &lt;/td&gt;
                &lt;/div&gt;
            &lt;/tr&gt;
        &lt;/table&gt;
        &lt;/body&gt;&lt;/html&gt;

    def index(self):
        &lt;html&gt;&lt;body&gt;
            &lt;form py-attr="request.base+'/viewResult'" action=""&gt;
                Integer between 20 and 50: &lt;input type=text name=N&gt;&lt;br&gt;
                Number of columns (or lines) between 2 and 10: &lt;input type=text name=C&gt;&lt;br&gt;
                Display result by: &lt;select name=displayBy&gt;
                    &lt;option&gt;line&lt;/option&gt;
                    &lt;option&gt;column&lt;/option&gt;
                &lt;/select&gt;&lt;br&gt;
                &lt;input type=submit&gt;
            &lt;/form&gt;
        &lt;/body&gt;&lt;/html&gt;
</pre></div>

<P>
We've rename the <var>renderTableData</var> mask into <var>renderTableDataByLine</var>, and we've added a new mask
called <var>renderTableDataByColumn</var>. <var>viewResult</var> now has a <var>displayBy</var> parameter, that's entered by
the user. Based on that, <var>viewResult</var> selects which mask to apply and applies it to the result of the
<var>prepareTableData</var> function (which hasn't changed).

<P>
Now, try one more test: In your browser, enter the URL: <a class="url" href="http://localhost:8000/prepareTableData?N=30&amp;C=5">http://localhost:8000/prepareTableData?N=30&amp;C=5</a>
<P>
You should see the following error:
<div class="verbatim"><pre>
CherryError: CherryClass "root" doesn't have any view or mask function called "prepareTableData"
</pre></div>
This means that a function cannot be "called" directly from a browser. Only views and masks, which return some
rendered data, can be "called" directly.

<P>
What we've learned:

<UL>
<LI>CherryPy allows true separation of content and presentation by using functions, masks and views
</LI>
<LI>Functions process some data and return some data. A function implementing an algorithm can be reused on
all kinds of data.
</LI>
<LI>Masks take some data as input and render it. A mask can be reused for all kinds of data
</LI>
<LI>Views are the link between functions and masks.
</LI>
</UL>

<P>
Note: inside a CherryClass declaration, the different sections (function, mask or view) can appear in any order, as many times as you want.

<P>
In the next chapter, we'll see how CherryPy determines which method to call based on the URL...

<P>

<DIV CLASS="navigation">
<p><hr>
<table align="center" width="100%" cellpadding="0" cellspacing="2">
<tr>
<td><A HREF="node7.html"><img src="../icons/previous.gif"
  border="0" height="32"
  alt="Previous Page" width="32"></A></td>
<td><A HREF="tut.html"><img src="../icons/up.gif"
  border="0" height="32"
  alt="Up One Level" width="32"></A></td>
<td><A HREF="node9.html"><img src="../icons/next.gif"
  border="0" height="32"
  alt="Next Page" width="32"></A></td>
<td align="center" width="100%">CherryPy Tutorial</td>
<td><A HREF="node2.html"><img src="../icons/contents.gif"
  border="0" height="32"
  alt="Contents" width="32"></A></td>
<td><img src="../icons/blank.gif"
  border="0" height="32"
  alt="" width="32"></td>
<td><img src="../icons/blank.gif"
  border="0" height="32"
  alt="" width="32"></td>
</tr></table>
<b class="navlabel">Previous:</b> <a class="sectref" HREF="node7.html">5. Templating languages: CHTL</A>
<b class="navlabel">Up:</b> <a class="sectref" HREF="tut.html">CherryPy Tutorial</A>
<b class="navlabel">Next:</b> <a class="sectref" HREF="node9.html">7. Class, instance, method</A>
<hr>
<span class="release-info">Release 0.10, documentation updated on 19 March 2004.</span>
</DIV>
<!--End of Navigation Panel-->
<ADDRESS>
See <i><a href="about.html">About this document...</a></i> for information on suggesting changes.
</ADDRESS>
</BODY>
</HTML>