Project

General

Profile

CompleteTutorial » History » Version 5

Lacey Powers, 06/18/2009 04:47 PM

1 1 bford -
2 5 Lacey Powers
h2. Creating your first "Hello World" Project in Pylons.
3
4
5 1 bford -
This is a tutorial to show you the basics of creating a small project within Pylons, with Simpycity.
6
This tutorial assumes that you have already set up a Linux development environment for Pylons. 
7
Preferably on Ubuntu, though these directions should be general enough for any
8
other Linux environment, meaning that you have installed Apache2, Python,
9 5 Lacey Powers
mod-wsgi, and [[PostgreSQL]] 8.3.
10 1 bford -
11
12
Choose a working directory for your project. 
13
14
For this example, I am using "project" for my working directory.
15
16
Create the working directory, if it doesn't already exist.
17
18 5 Lacey Powers
<pre>
19 1 bford -
mkdir project
20 5 Lacey Powers
</pre>
21 1 bford -
22
Move into the working directory.
23
24 5 Lacey Powers
<pre>
25 1 bford -
cd project
26 5 Lacey Powers
</pre>
27 1 bford -
28
Create your project:
29
30 5 Lacey Powers
<pre>
31 1 bford -
paster create -t pylons helloworld
32 5 Lacey Powers
</pre>
33 1 bford -
34
This should give you a set of choices.
35
36 5 Lacey Powers
<pre>
37 1 bford -
Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']: genshi
38 5 Lacey Powers
</pre>
39 1 bford -
40
For templating, choose "genshi".
41
42 5 Lacey Powers
<pre>
43 1 bford -
Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]: False
44 5 Lacey Powers
</pre>
45 1 bford -
46
Include sqlalchemy. "False".
47
48
We will be using Simpycity instead.
49
50
You should have a directory that looks like this:
51
52 5 Lacey Powers
<pre>
53 1 bford -
lacey@blinky:~/project$ ls -l
54
total 4.0K
55
drwxr-xr-x 5 lacey lacey 4.0K 2009-06-05 08:46 helloworld
56
lacey@blinky:~/project$
57 5 Lacey Powers
</pre>
58 1 bford -
59
cd into the helloworld project.
60
61 5 Lacey Powers
<pre>
62 1 bford -
cd helloworld
63 5 Lacey Powers
</pre>
64 1 bford -
65
The base directory should look like this:
66
67 5 Lacey Powers
<pre>
68 1 bford -
lacey@blinky:~/project/helloworld$ ls -l
69
total 48K
70
-rw-r--r-- 1 lacey lacey 1.6K 2009-06-05 08:46 development.ini
71
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 docs
72
-rw-r--r-- 1 lacey lacey 9.5K 2009-06-05 08:46 ez_setup.py
73
drwxr-xr-x 9 lacey lacey 4.0K 2009-06-05 08:46 helloworld
74
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 helloworld.egg-info
75
-rw-r--r-- 1 lacey lacey  125 2009-06-05 08:46 MANIFEST.in
76
-rw-r--r-- 1 lacey lacey  463 2009-06-05 08:46 README.txt
77
-rw-r--r-- 1 lacey lacey  597 2009-06-05 08:46 setup.cfg
78
-rw-r--r-- 1 lacey lacey  959 2009-06-05 08:46 setup.py
79
-rw-r--r-- 1 lacey lacey  509 2009-06-05 08:46 test.ini
80
lacey@blinky:~/project/helloworld$
81 5 Lacey Powers
</pre>
82 1 bford -
83
This is the basic enviroment for your "Hello World" project. 
84
85
There are several important files and directories here. To avoid confusion, I
86
will only explain the most important, and most used files within the project.
87
88
setup.py   -- A command and control file for the project. With setup.py, you
89
can run unit tests, update the packages associated with the project, and
90
package it into a Python .egg. 
91
92
test.ini   -- The test configuration file for the project. This file mostly
93
inherits from the development.ini file, but if you want to have testing
94
specific settings for this project, you put them here. For now, this will just
95
inherit from the development.ini file.
96
97
development.ini  --  This file contains basic configuration of the project.
98
This is where you toggle debugging settings, add database configuration
99
information, and so forth.
100
101
We need to customize this environment to use Simpycity, a simple and effective
102
ORM based on stored procedures and queries. 
103
104
First, we edit the "setup.py" file. You can use your preferred text editor to
105
open this file, e.g. Vim, Emacs, Joe, Nano, Gedit, ect. For this example, we
106
will use the basic editor "nano".
107
108 5 Lacey Powers
<pre>
109 1 bford -
nano setup.py
110 5 Lacey Powers
</pre>
111 1 bford -
112
There are only a few options that need to be modified in the base setup.py
113
file.
114
115
Note the section that looks like this: 
116
117 5 Lacey Powers
<pre>
118 1 bford -
    install_requires=[
119
        "Pylons>=0.9.7",
120
        "Genshi>=0.4",
121
    ],
122 5 Lacey Powers
</pre>
123 1 bford -
124
This section governs the requirements for your particular project. This currently 
125
shows that a version of Pylons of 0.9.7 or greater, and a version of Genshi of 0.4 
126
or greater is required to for this project. 
127
128
Since we are using Simpycity for our ORM, we need to add a line for Simpycity.
129
130 5 Lacey Powers
<pre>
131 1 bford -
    install_requires=[
132
        "Pylons>=0.9.7",
133
        "Genshi>=0.4",
134 3 Lacey Powers
        "Simpycity>=0.2.1"
135 1 bford -
    ],
136 5 Lacey Powers
</pre>
137 1 bford -
138
The number 0.2.1 is the current version of Simpycity. 
139
140 2 Lacey Powers
Since new features and bug fixes are added to Simpycity regularly, you should check for updated versions regularly. 
141
142
To check that the version of Simpycity is up to date, please go to:
143
144 1 bford -
https://projects.commandprompt.com/public/simpycity
145
146
Down in the middle of the page, there is an area marked "Download". 
147
148
The first item in the download area is the current version of Simpycity.
149 3 Lacey Powers
(Which at the time of writing this tutorial is 0.2.1, so be sure to check.)
150 1 bford -
151 5 Lacey Powers
<pre>
152 1 bford -
The current release version of Simpycity is 0.2.1, which can be downloaded
153
from here 
154 5 Lacey Powers
</pre>
155 2 Lacey Powers
156 1 bford -
All that you need to do to keep Simpycity up to date is copy that version number, and paste over the version number in the install_requires stanza in setup.py.
157 2 Lacey Powers
158 1 bford -
After that, there is one last thing we need to add to the setup.py file
159
160
Near the bottom, before the closing parenthesis, we need to add another snippet
161
of code.
162
163 5 Lacey Powers
<pre>
164 1 bford -
    dependency_links=[
165
        "https://projects.commandprompt.com/public/simpycity/repo/dist/"
166
    ] 
167 5 Lacey Powers
</pre>
168 2 Lacey Powers
169 1 bford -
This is the link to the Simpycity repository at Command Prompt. 
170
171 3 Lacey Powers
easy_install will use that URI, combined with the version number (in this case 0.2.1) to locate and install the proper Simpycity .egg file.
172 1 bford -
173 2 Lacey Powers
Now, we save, and exit from setup.py.
174
175
Following the insertion of those lines, we will do one last check. 
176
177
Sometimes, the newest python packages with easy_install require that they be built on the machine before they are deployed.
178
179 1 bford -
This is the case with newer versions of psycopg2. To ensure that all of the build requirements are present, run the following command:
180
181 5 Lacey Powers
<pre>
182 2 Lacey Powers
aptitude search postgresql-server-dev-8.3 python2.5-dev build-essential
183 5 Lacey Powers
</pre>
184 2 Lacey Powers
185 1 bford -
Which should return like this:
186 2 Lacey Powers
187 5 Lacey Powers
<pre>
188 1 bford -
lacey@blinky:~/project/helloworld$ aptitude search postgresql-server-dev-8.3 python2.5-dev build-essential
189
i A build-essential                                                                     - Informational list of build-essential packages                                                
190 5 Lacey Powers
i   postgresql-server-dev-8.3                                                           - development files for [[PostgreSQL]] 8.3 server-side programming                                  
191 1 bford -
i   python2.5-dev                                                                       - Header files and a static library for Python (v2.5)                                           
192 2 Lacey Powers
lacey@blinky:~/project/helloworld$
193 5 Lacey Powers
</pre>
194 2 Lacey Powers
195
If it does not, then running the following command:
196
197 5 Lacey Powers
<pre>
198 2 Lacey Powers
sudo aptitude install postgresql-server-dev-8.3 python2.5-dev build-essential
199 5 Lacey Powers
</pre>
200 2 Lacey Powers
201
Will install the essential libraries for you.
202
203
Now, we run the following command:
204 1 bford -
205 5 Lacey Powers
<pre>
206 1 bford -
sudo python setup.py develop
207 5 Lacey Powers
</pre>
208 1 bford -
209
This will check and process all of the dependencies for your project, including
210
Simpycity. This process will install the dependencies required for your
211
project, so that they may be used within the project.
212
213
Now that we have Simpycity installed, we pause to do a bit of database setup.
214
215
With sudo become the postgres user.
216
217 5 Lacey Powers
<pre>
218 1 bford -
sudo su - postgres
219 5 Lacey Powers
</pre>
220 1 bford -
221
It will ask you for your password.
222
223
Success should look something like this.
224
225 5 Lacey Powers
<pre>
226 1 bford -
lacey@blinky:~/project/helloworld$ sudo su - postgres
227
[sudo] password for lacey: 
228
postgres@blinky:~$
229 5 Lacey Powers
</pre>
230 1 bford -
231 5 Lacey Powers
Now, we run a [[PostgreSQL]] command to create a user.
232 1 bford -
233 5 Lacey Powers
<pre>
234 1 bford -
postgres@blinky:~$ psql -U postgres
235 5 Lacey Powers
Welcome to psql 8.3.7, the [[PostgreSQL]] interactive terminal.
236 1 bford -
237
Type:  \copyright for distribution terms
238
       \h for help with SQL commands
239
       \? for help with psql commands
240
       \g or terminate with semicolon to execute query
241
       \q to quit
242
243
postgres=# 
244 5 Lacey Powers
</pre>
245 1 bford -
246
You are now on the postgresql command line.
247
248
While there, run the following commands.
249
250 5 Lacey Powers
<pre>
251 1 bford -
CREATE USER helloworld WITH ENCRYPTED PASSWORD '12345';
252
CREATE DATABASE helloworld WITH OWNER helloworld;
253 5 Lacey Powers
</pre>
254 1 bford -
255
The successful execution of these two commands should look like this:
256
257 5 Lacey Powers
<pre>
258 1 bford -
postgres=# CREATE USER helloworld WITH ENCRYPTED PASSWORD '12345';
259
CREATE ROLE
260
postgres=# CREATE DATABASE helloworld WITH OWNER helloworld;
261
CREATE DATABASE
262
postgres=#
263 5 Lacey Powers
</pre>
264 1 bford -
265 5 Lacey Powers
Exit [[PostgreSQL]] with the \q command. This should drop you back to the command
266 1 bford -
line.
267
268 5 Lacey Powers
<pre>
269 1 bford -
postgres=# \q
270
postgres@blinky:~$
271 5 Lacey Powers
</pre>
272 1 bford -
273
If you haven't already, take a moment to modify your pg_hba.conf so you can
274
easily log in. 
275
276 5 Lacey Powers
<pre>
277 1 bford -
nano /etc/postgresql/8.3/main/pg_hba.conf
278 5 Lacey Powers
</pre>
279 1 bford -
280
Near the bottom, there should be a line that looks like *exactly* like this.
281
282 5 Lacey Powers
<pre>
283 1 bford -
local   all         all                               ident sameuser
284 5 Lacey Powers
</pre>
285 1 bford -
286
The very last part, "ident sameuser" should be changed to md5, so that the line
287
looks like this:
288
289 5 Lacey Powers
<pre>
290 1 bford -
local   all         all                               md5
291 5 Lacey Powers
</pre>
292 1 bford -
293
Save and quit.
294
295
Now, execute the command:
296 5 Lacey Powers
<pre>
297 1 bford -
/etc/init.d/postgresql-8.3 force-reload
298 5 Lacey Powers
</pre>
299 1 bford -
300
This will reload the settings that you changed.
301
302 5 Lacey Powers
<pre>
303 1 bford -
postgres@blinky:~$ /etc/init.d/postgresql-8.3 force-reload
304 5 Lacey Powers
* Reloading [[PostgreSQL]] 8.3 database server [ OK ] 
305 1 bford -
postgres@blinky:~$
306 5 Lacey Powers
</pre>
307 1 bford -
308
309
Exit the postgres user environment with the exit command.
310
311 5 Lacey Powers
<pre>
312 1 bford -
postgres@blinky:~$ exit
313
logout
314
lacey@blinky:~/project/helloworld$
315 5 Lacey Powers
</pre>
316 1 bford -
317
Now we should be back in the "helloworld" project directory.
318
319
Next, as a convenience, we will create a .pgpass file.
320
321
cd to your home directory. 
322
323 5 Lacey Powers
<pre>
324 1 bford -
cd ~
325 5 Lacey Powers
</pre>
326 1 bford -
327 5 Lacey Powers
<pre>
328 1 bford -
nano .pgpass
329 5 Lacey Powers
</pre>
330 1 bford -
331
When the editor window comes up, add the following line.
332
333 5 Lacey Powers
<pre>
334 1 bford -
localhost:5432:*:helloworld:12345
335 5 Lacey Powers
</pre>
336 1 bford -
337
Save and quit.
338
339
Following that, execute the following command.
340
341 5 Lacey Powers
<pre>
342 1 bford -
chmod 600 .pgpass
343 5 Lacey Powers
</pre>
344 1 bford -
345
This will appropriately set the permissions for your user.
346
347
You can check them with the following command.
348
349 5 Lacey Powers
<pre>
350 1 bford -
lacey@blinky:~$ ls -l .pgpass
351
-rw------- 1 lacey lacey 135 2009-06-05 12:10 .pgpass
352
lacey@blinky:~$
353 5 Lacey Powers
</pre>
354 1 bford -
355
Now, there is the final test. 
356
357
Execute the following command.
358
359 5 Lacey Powers
<pre>
360 1 bford -
psql -U helloworld
361 5 Lacey Powers
</pre>
362 1 bford -
363
If everything has been successfully set up, your terminal screen should look
364
like this:
365
366 5 Lacey Powers
<pre>
367 1 bford -
lacey@blinky:~$ psql -U helloworld
368 5 Lacey Powers
Welcome to psql 8.3.7, the [[PostgreSQL]] interactive terminal.
369 1 bford -
370
Type:  \copyright for distribution terms
371
       \h for help with SQL commands
372
       \? for help with psql commands
373
       \g or terminate with semicolon to execute query
374
       \q to quit
375
376
helloworld=>
377 5 Lacey Powers
</pre>
378 1 bford -
379
With the command logging you in without issue, or a password prompt.
380
381 5 Lacey Powers
While we are here, in [[PostgreSQL]], we should issue the following command:
382 1 bford -
383 5 Lacey Powers
<pre>
384 1 bford -
CREATE LANGUAGE plpgsql;
385 5 Lacey Powers
</pre>
386 1 bford -
387
Again, success should look like this:
388
389 5 Lacey Powers
<pre>
390 1 bford -
helloworld=> CREATE LANGUAGE plpgsql;
391
CREATE LANGUAGE
392
helloworld=>
393 5 Lacey Powers
</pre>
394 1 bford -
395
And again, we exit with "\q".
396
397 5 Lacey Powers
<pre>
398 1 bford -
helloworld=> \q
399
lacey@blinky:~$
400 5 Lacey Powers
</pre>
401 1 bford -
402
Now, we return to the project directory with the following command.
403
404 5 Lacey Powers
<pre>
405 1 bford -
cd project/helloworld/
406 5 Lacey Powers
</pre>
407 1 bford -
408
In this directory, we edit development.ini
409
410 5 Lacey Powers
<pre>
411 1 bford -
nano development.ini
412 5 Lacey Powers
</pre>
413 1 bford -
414
At line 36, uncomment the line set debug = false, and add the following lines
415
so that the section of the configuration file looks like this:
416
417 5 Lacey Powers
<pre>
418 1 bford -
db.database = helloworld
419
db.user     = helloworld
420
db.host     = localhost
421
db.port     = 5432
422
db.password = 12345
423 5 Lacey Powers
</pre>
424 1 bford -
425
These are the values that Simpycity requires for the database connections.
426
database = a database that belongs to the user.
427
user = the user that you are connecting as.
428
host = the server being connected to. localhost for a development machine.
429 5 Lacey Powers
port = the port that [[PostgreSQL]] expects you to connect on.
430 1 bford -
password = the password for the user.
431
432
Before we go on, a little more explanation is needed, so that you better
433
understand what is going on in later steps.
434
435
You may have noticed the "helloworld" directory within the project earlier.
436
437 5 Lacey Powers
<pre>
438 1 bford -
lacey@blinky:~/project/helloworld$ ls -l
439
total 48K 
440
-rw-r--r-- 1 lacey lacey 1.6K 2009-06-05 08:46 development.ini
441
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 docs
442
-rw-r--r-- 1 lacey lacey 9.5K 2009-06-05 08:46 ez_setup.py
443
drwxr-xr-x 9 lacey lacey 4.0K 2009-06-05 08:46 helloworld
444
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 helloworld.egg-info
445
-rw-r--r-- 1 lacey lacey  125 2009-06-05 08:46 MANIFEST.in
446
-rw-r--r-- 1 lacey lacey  463 2009-06-05 08:46 README.txt
447
-rw-r--r-- 1 lacey lacey  597 2009-06-05 08:46 setup.cfg
448
-rw-r--r-- 1 lacey lacey  959 2009-06-05 08:46 setup.py
449
-rw-r--r-- 1 lacey lacey  509 2009-06-05 08:46 test.ini
450
lacey@blinky:~/project/helloworld$
451 5 Lacey Powers
</pre>
452 1 bford -
453
This directory contains all of your project specific code is placed.
454
455
If you issue the following commands:
456
457 5 Lacey Powers
<pre>
458 1 bford -
cd helloworld
459
ls -l
460 5 Lacey Powers
</pre>
461 1 bford -
462
You will note that the directory has the following structure: 
463
464 5 Lacey Powers
<pre>
465 1 bford -
lacey@blinky:~/project/helloworld/helloworld$ ls -l
466
total 32K
467
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 config
468
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 controllers
469 5 Lacey Powers
-rw-r--r-- 1 lacey lacey    0 2009-06-05 08:46 +init+.py
470 1 bford -
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 lib
471
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 model
472
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 public
473
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 templates
474
drwxr-xr-x 3 lacey lacey 4.0K 2009-06-05 08:46 tests
475
-rw-r--r-- 1 lacey lacey  296 2009-06-05 08:46 websetup.py
476
lacey@blinky:~/project/helloworld/helloworld$ 
477 5 Lacey Powers
</pre>
478 1 bford -
479
The directories here break down as follows: 
480
481
   config -- Configuration files for your application as a whole.
482
483
   controllers -- This is where most of the application logic goes. The files here
484
control what you display, and how you display it.
485
486
   lib -- This is where miscellaneous files that are necessary for your
487
application, but have no distinct place go.
488
489
   model -- This is where the files related to the ORM go. This will be where most
490
of the files related to the database and Simpycity will live.
491
492
   public -- This is where static html, docs, css, and such live. 
493
494
   templates -- This is where Genshi will create the dynamic html templates for
495
the dynamic pages that you will use.
496
497 5 Lacey Powers
   +init+.py -- This is a helper file for packaging your application. That
498 1 bford -
will be covered later in a different tutorial.
499
500
   tests -- This is where your unit tests live. These test controllers and other
501
application functionality.
502
503
   websetup.py -- This is another helper file for packaging your application. It
504
too will be covered later in a different tutorial.
505
506
507
Now that you have an overview of the directories within a Pylons project, we
508
can start making some changes within this directory and its child directories.
509
510
First, we should make an important edit to lib/base.py, related to Simpycity.
511
512 5 Lacey Powers
<pre>
513 1 bford -
nano lib/base.py
514 5 Lacey Powers
</pre>
515 1 bford -
516
The contents of the file will look like this.
517
518 5 Lacey Powers
<pre>
519 1 bford -
"""The base Controller API
520
521 5 Lacey Powers
Provides the [[BaseController]] class for subclassing.
522 1 bford -
"""
523
from pylons.controllers import WSGIController
524
from pylons.templating import render_genshi as render
525
526 5 Lacey Powers
class [[BaseController]](WSGIController):
527 1 bford -
528 5 Lacey Powers
    def +call+(self, environ, start_response):
529 1 bford -
        """Invoke the Controller"""
530 5 Lacey Powers
        # WSGIController.+call+ dispatches to the Controller method
531 1 bford -
        # the request is routed to. This routing information is
532
        # available in environ['pylons.routes_dict']
533 5 Lacey Powers
        return WSGIController.+call+(self, environ, start_response)
534
</pre>
535 1 bford -
536
We will add the following lines, to fix a troublesome issue before it starts.
537
538
For the curious, more detail can be found here:
539
http://www.commandprompt.com/blogs/aurynn_shaw/2009/05/long_running_request_handlers_and_python_garbage_collection/
540
541
For purposes of this tutorial, it should be sufficient to say that this will
542
allow Pylons and mod_wsgi to properly close and clean up connections to the database, so
543
that connections are not left hanging idle in transaction.
544
545
So this is very important to include in any project.
546
547 5 Lacey Powers
<pre>
548 1 bford -
        import psycopg2
549
        import psycopg2.extensions
550
        psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
551
        from simpycity.handle import Manager
552
            
553
        m = Manager()
554
        try:
555 5 Lacey Powers
            return WSGIController.+call+(self, environ, start_response)
556 1 bford -
        finally:
557
            # Shut down *all* the extant handles.
558
            m.close()
559 5 Lacey Powers
</pre>
560 1 bford -
561
We paste this in so that the controller code will look like this:
562
563 5 Lacey Powers
<pre>
564 1 bford -
"""The base Controller API
565
566 5 Lacey Powers
Provides the [[BaseController]] class for subclassing.
567 1 bford -
"""
568
from pylons.controllers import WSGIController
569
from pylons.templating import render_genshi as render
570
571 5 Lacey Powers
class [[BaseController]](WSGIController):
572 1 bford -
573 5 Lacey Powers
    def +call+(self, environ, start_response):
574 1 bford -
        """Invoke the Controller"""
575 5 Lacey Powers
        # WSGIController.+call+ dispatches to the Controller method
576 1 bford -
        # the request is routed to. This routing information is
577
        # available in environ['pylons.routes_dict']
578 5 Lacey Powers
        #return WSGIController.+call+(self, environ, start_response)
579 1 bford -
        import psycopg2
580
        import psycopg2.extensions
581
        psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
582
        from simpycity.handle import Manager
583
                
584
        m = Manager()
585
        try:
586 5 Lacey Powers
            return WSGIController.+call+(self, environ, start_response)
587 1 bford -
        finally:
588
            # Shut down *all* the extant handles.
589
            m.close()
590 5 Lacey Powers
</pre>
591 1 bford -
592
Save, and exit nano.
593
594
Next we edit environment.py
595
596 5 Lacey Powers
<pre>
597 1 bford -
nano config/environment.py
598 5 Lacey Powers
</pre>
599 1 bford -
600
Within this file, at the very end, below the lines:
601
602 5 Lacey Powers
<pre>
603 1 bford -
# CONFIGURATION OPTIONS HERE (note: all config options will override
604
# any Pylons config options)
605 5 Lacey Powers
</pre>
606 1 bford -
607
we add the follwing lines, for Simpycity setup. 
608
609 5 Lacey Powers
<pre>
610 1 bford -
app_conf = config['app_conf']
611
612
db_config.port = app_conf['db.port']
613
db_config.database= app_conf['db.database']
614
db_config.host= app_conf['db.host']
615
db_config.user = app_conf['db.user']
616
db_config.password = app_conf['db.password']
617
db_config.debug = False
618 5 Lacey Powers
</pre>
619 1 bford -
620
And at the top of environment.py, with the rest of the imports, we add the
621
following line.
622
623 5 Lacey Powers
<pre>
624 1 bford -
from simpycity import config as db_config
625 5 Lacey Powers
</pre>
626 1 bford -
627
So that the proper configuration options are read.
628
629
It should be noted here, if you need to see the debugging options from
630
Simpycity itself, you should set db_config.debug to True.
631
632
Having added the proper configuration parameters to base.py and environment.py,
633
we can start looking at how to serve content from Pylons. There are two basic
634
ways to serve content through Pylons: 
635
636
   1. To server static content from the public/ directory. 
637
   2. Use a controller, which contains the logic for what to control and how to
638
   display it.
639
640
The trivially easy case is static content.
641
642
Execute the following command.
643
644 5 Lacey Powers
<pre>
645 1 bford -
nano helloworld/public/hello.html
646 5 Lacey Powers
</pre>
647 1 bford -
648
Paste the following content. 
649
650 5 Lacey Powers
<pre>
651 1 bford -
<html>
652
   <body>
653
      Hello World!
654
   </body>
655
</html>
656 5 Lacey Powers
</pre>
657 1 bford -
658
Save and exit.
659
660
This creates a basic static html file.
661
662
Now, from our current directory, execute the following command
663
664 5 Lacey Powers
<pre>
665 1 bford -
paster serve --reload development.ini
666 5 Lacey Powers
</pre>
667 1 bford -
668
This command starts the Pylons server, and is great for a bit of quick
669
debugging, or if you haven't got Apache installed or configured. 
670
671
Entering the following URI into your web browser:
672
673 5 Lacey Powers
<pre>
674 1 bford -
http://localhost:5000/hello.html
675 5 Lacey Powers
</pre>
676 1 bford -
677
You should see a simple page with the words "Hello World!".
678
679
Now, stop the server on the command line with <ctrl>-<c>.
680
681
We are moving on to the more interesting and complicated case of using a
682
controller to serve content within Pylons.
683
684
Execute the following command:
685
686 5 Lacey Powers
<pre>
687 1 bford -
paster controller hello
688 5 Lacey Powers
</pre>
689 1 bford -
690
Successful execution of this command should look something like this:
691
692 5 Lacey Powers
<pre>
693 1 bford -
lacey@blinky:~/project/helloworld$ paster controller hello
694
Creating /home/lacey/project/helloworld/helloworld/controllers/hello.py
695
Creating /home/lacey/project/helloworld/helloworld/tests/functional/test_hello.py
696 5 Lacey Powers
</pre>
697 1 bford -
698
This is an interesting and useful command. As expected, it created a file
699
called hello.py in the helloworld/controllers directory, but it also did a
700
wonderful thing for us, and created a file in helloworld/tests/functional/test_hello.py
701
to contain the specific unit tests for the hello.py controller.
702
703
Executing the following command:
704
705 5 Lacey Powers
<pre>
706 1 bford -
nano helloworld/controller/hello.py
707 5 Lacey Powers
</pre>
708 1 bford -
709
Shows us the contents of the hello.py controller, which are very basic.
710
711 5 Lacey Powers
<pre>
712 1 bford -
import logging
713
714
from pylons import request, response, session, tmpl_context as c
715
from pylons.controllers.util import abort, redirect_to
716
717 5 Lacey Powers
from helloworld.lib.base import [[BaseController]], render
718 1 bford -
719 5 Lacey Powers
log = logging.getLogger(+name+)
720 1 bford -
721 5 Lacey Powers
class [[HelloController]](BaseController):
722 1 bford -
723
    def index(self):
724
        # Return a rendered template
725
        #return render('/hello.mako')
726
        # or, return a response
727
        return 'Hello World'
728 5 Lacey Powers
</pre>
729 1 bford -
730
Exiting from Nano, we can also have a look at test_hello.py by executing a similar command.
731
732 5 Lacey Powers
<pre>
733 1 bford -
nano helloworld/tests/functional/test_hello.py
734 5 Lacey Powers
</pre>
735 1 bford -
736
The contents of which are:
737
738 5 Lacey Powers
<pre>
739 1 bford -
from helloworld.tests import *
740
741 5 Lacey Powers
class [[TestHelloController]](TestController):
742 1 bford -
743
    def test_index(self):
744
        response = self.app.get(url(controller='hello', action='index'))
745
        # Test response...
746 5 Lacey Powers
</pre>
747 1 bford -
748
The paster command has created a basic framework for a unit test of the
749
hello.py controller. We will come back to this later in the document.
750
751
For right now, we want to see the controller in action. 
752
753
But there is a bit more setup that we need to do first. 
754
755
Execute the following command:
756
757 5 Lacey Powers
<pre>
758 1 bford -
nano helloworld/config/routes.py
759 5 Lacey Powers
</pre>
760 1 bford -
761
This should bring up a file that looks like this:
762
763 5 Lacey Powers
<pre>
764 1 bford -
"""Routes configuration
765
766
The more specific and detailed routes should be defined first so they
767
may take precedent over the more generic routes. For more information
768
refer to the routes manual at http://routes.groovie.org/docs/
769
"""
770
from pylons import config
771
from routes import Mapper
772
773
def make_map():
774
    """Create, configure and return the routes Mapper"""
775
    map = Mapper(directory=config['pylons.paths']['controllers'],
776
                 always_scan=config['debug'])
777
    map.minimization = False
778
779 5 Lacey Powers
    # The [[ErrorController]] route (handles 404/500 error pages); it should
780 1 bford -
    # likely stay at the top, ensuring it can always be resolved
781
    map.connect('/error/{action}', controller='error')
782
    map.connect('/error/{action}/{id}', controller='error')
783
784
    # CUSTOM ROUTES HERE
785
786
    map.connect('/{controller}/{action}')
787
    map.connect('/{controller}/{action}/{id}')
788
789
    return map
790 5 Lacey Powers
</pre>
791 1 bford -
792
This is another relatively spartan, but highly important file.
793
Within this file, the routes for the application are defined. Routes are what
794
translates the URI within the browser into directions in the application, which
795
point data at a specific controller, for manipulation.
796
797
This may sound a bit obtuse right now, but at the end of this particular
798
example, it should be clear.
799
800
Currently, if you were to restart Pylons, with the aformentioned command....
801
802 5 Lacey Powers
<pre>
803 1 bford -
paster serve --reload development.ini
804 5 Lacey Powers
</pre>
805 1 bford -
806
And attempted to browse to the URI:
807
808 5 Lacey Powers
<pre>
809 1 bford -
http://localhost:5000/hello
810 5 Lacey Powers
</pre>
811 1 bford -
812
You would be presented with a very cheery orange-yellow, and black 404 page.
813
814
This is because you haven't specified the route. Without a route, Pylons has no
815
idea what you want. So, to let it know that we want to see what is within the
816 5 Lacey Powers
[[HelloController]] (located in helloworld/controller/hello.py) we will add the
817 1 bford -
following line to helloworld/config/routes.py :
818
819 5 Lacey Powers
<pre>
820 1 bford -
map.connect('hello', '/hello', controller='hello', action='index')
821 5 Lacey Powers
</pre>
822 1 bford -
823
The first item in map.connect gives the route the name "hello" (this will be
824
handy later). The second part is what maps the part of the URI that you put in
825
the search bar as in:
826
827 5 Lacey Powers
<pre>
828 1 bford -
http://localhost:5000/hello
829 5 Lacey Powers
</pre>
830 1 bford -
831
Now, when we run:
832
833 5 Lacey Powers
<pre>
834 1 bford -
paster serve --reload development.ini
835 5 Lacey Powers
</pre>
836 1 bford -
837
And attempted to browse to the URI:
838
839 5 Lacey Powers
<pre>
840 1 bford -
http://localhost:5000/hello
841 5 Lacey Powers
</pre>
842 1 bford -
843
We will see the words "Hello World".
844
845
Note that this is different from our static html entry.
846
847 5 Lacey Powers
<pre>
848 1 bford -
http://localhost:5000/hello.html
849 5 Lacey Powers
</pre>
850 1 bford -
851
Which says "Hello World!".
852
853
And both are available.
854
855
Moving to a more generic route....
856
857 5 Lacey Powers
<pre>
858 1 bford -
http://localhost:5000
859 5 Lacey Powers
</pre>
860 1 bford -
861
Will show you a very cheery welcome page, with the same orange-yellow and black
862
color scheme, with links. 
863
864
This is served from helloworld/public/public.html
865
866
Now, that is all fine for an example, but in an actual production application, you don't
867
want someone to see "Welcome To Pylons" when they browse to your root URI.
868
869
So, lets fix this.
870
871
First, stop the server on the command line with <ctrl>-<c>.
872
873
Execute the command:
874
875 5 Lacey Powers
<pre>
876 1 bford -
nano helloworld/config/routes.py
877 5 Lacey Powers
</pre>
878 1 bford -
879
At the bottom, just before "return map", we will add the following line.
880
881 5 Lacey Powers
<pre>
882 1 bford -
map.connect('root', '/', controller='hello', action='index')
883 5 Lacey Powers
</pre>
884 1 bford -
885
Why do we add this way down there, you might ask? Because the Pylons developers
886
left us with a handy comment at the top of this file.
887
888 5 Lacey Powers
<pre>
889 1 bford -
The more specific and detailed routes should be defined first so they
890
may take precedent over the more generic routes.
891 5 Lacey Powers
</pre>
892 1 bford -
893
Taking heed of this advice, and realizing that '/' is the most generic route
894
that you can have, we place this at the very bottom so it is chosen last in the
895
evaluation of routes.
896
897
Again, we restart the server, from the ~/projects/hellworld directory.
898
899 5 Lacey Powers
<pre>
900 1 bford -
paster serve --reload development.ini
901 5 Lacey Powers
</pre>
902 1 bford -
903
And browse to:
904
905 5 Lacey Powers
<pre>
906 1 bford -
http://localhost:5000
907 5 Lacey Powers
</pre>
908 1 bford -
909
....We still see the cheery orange-yellow and black welcome page.
910
911
(Be sure to shut down the server with <ctrl>-<c>)
912
913
Why is that!?!?
914
915
Well, there's an interesting thing about Pylons. It will evaluate the static
916
content in helloworld/public first. In this case, it is index.html. 
917
918
If we poke around a bit, we find the culprit in helloworld/config/middleware.py
919
on line 67.
920
921 5 Lacey Powers
<pre>
922 1 bford -
app = Cascade([static_app, app])
923 5 Lacey Powers
</pre>
924 1 bford -
925
This line basically says that, look for static content first, and evaluate that
926
over the dynamic application content. 
927
928
There are two ways to solve this particular issue.
929
930
1. Execute the following command:
931 5 Lacey Powers
<pre>
932 1 bford -
mv helloworld/public/index.html helloworld/public/index.html.dont_evaluate_me_pylons
933 5 Lacey Powers
</pre>
934 1 bford -
935
If you do that, Pylons won't be able to find it, and will default to the route
936
set in routes.py. 
937
938
In this case, if you restart the server, and browse to the aformentioned URI,
939
you will see Hello World (without the exclamation point).
940
941
2. Swap the order of Cascade. 
942
943
Since the Cascade command defines whether or not you evaluate static content or
944
dynamic content first, swapping the order will mean that dynamic content is
945
evaluated first, and static content second.
946
947
So, we execute the following command:
948
949 5 Lacey Powers
<pre>
950 1 bford -
mv helloworld/public/index.html.dont_evaluate_me_pylons helloworld/public/index.html 
951 5 Lacey Powers
</pre>
952 1 bford -
953
To return the file back to its original state. If we are incorrect, this will
954
show us by displaying that cheery welcome page again. 
955
956
So, we execute the following command:
957
958 5 Lacey Powers
<pre>
959 1 bford -
nano helloworld/config/middleware.py
960 5 Lacey Powers
</pre>
961 1 bford -
962
Once there, we will copy line 67, and paste it directly below itself. Then we
963
will comment out line 67i (we have the original line for reference), so that the file 
964
now looks like this:
965
966 5 Lacey Powers
<pre>
967 1 bford -
#app = Cascade([static_app, app])
968
app = Cascade([app, static_app])
969 5 Lacey Powers
</pre>
970 1 bford -
971
With the order of app, and static_app swapped. 
972
973
In this case, if you restart the server, and browse to the aformentioned URI,
974
you will see Hello World (without the exclamation point).
975
976
And index.html is still happily sitting in helloworld/public
977
978 5 Lacey Powers
<pre>
979 1 bford -
lacey@blinky:~/project/helloworld$ ls -l helloworld/public/index.html 
980
-rw-r--r-- 1 lacey lacey 4.6K 2009-06-05 08:46 helloworld/public/index.html
981 5 Lacey Powers
</pre>
982 1 bford -
983 5 Lacey Powers
Now, even though we're using the [[HelloController]] in hello.py, this is still
984 1 bford -
little better than a static application. So, now, we will work on making
985
hello.py dynamic, letting it read and write to and from the database by using
986
Simpycity.
987
988 5 Lacey Powers
First, we will need a simple set of tables and functions for use in [[PostgreSQL]].
989 1 bford -
990
We are currently still in the ~/project/helloworld directory. We are going to
991
move one directory back.
992 5 Lacey Powers
<pre>
993 1 bford -
cd ..
994 5 Lacey Powers
</pre>
995 1 bford -
996
And make a new directory called 'sql'.
997
998 5 Lacey Powers
<pre>
999 1 bford -
mkdir sql
1000 5 Lacey Powers
</pre>
1001 1 bford -
1002
Now, we consider the following table and functions, already designed and provided for you to use. 
1003
These will be the examples that we use for the rest of the tutorial.
1004
1005
This is a very simple table, that contains ways to say "Hello" in different languages.
1006
1007 5 Lacey Powers
<pre>
1008 1 bford -
CREATE TABLE hello
1009
(
1010
  salutation text not null primary key,
1011
  language text not null
1012
);
1013
1014
-- Various formal ways of saying hello or good morning.
1015
INSERT INTO hello (salutation, language) VALUES ('hello','English');
1016
INSERT INTO hello (salutation, language) VALUES ('hola','Spanish');
1017
INSERT INTO hello (salutation, language) VALUES ('bonjour','French');
1018
INSERT INTO hello (salutation, language) VALUES ('merhaba selam','Turkish');
1019
INSERT INTO hello (salutation, language) VALUES ('zdravstvuyte','Russian');
1020
INSERT INTO hello (salutation, language) VALUES ('buon giorno','Italian');
1021
1022
1023
CREATE OR REPLACE FUNCTION add_salutation(in_salutation text, in_language text)
1024
RETURNS boolean AS $BODY$
1025
  DECLARE
1026
  BEGIN
1027
    INSERT INTO hello (salutation, language) VALUES (in_salutation, in_language);
1028
    RETURN TRUE;
1029
  END;
1030
$BODY$ LANGUAGE PLPGSQL;
1031
1032
1033
CREATE OR REPLACE FUNCTION remove_salutation(in_salutation text, in_language text)
1034
RETURNS boolean AS $BODY$
1035
  DECLARE
1036
  BEGIN
1037
    DELETE FROM hello WHERE salutation = in_salutation AND language = in_language;
1038
    RETURN TRUE;
1039
  END;
1040
$BODY$ LANGUAGE PLPGSQL;
1041
1042
1043
CREATE OR REPLACE FUNCTION update_salutation(in_old_salutation text, in_new_salutation text)
1044
RETURNS boolean AS $BODY$
1045
  DECLARE
1046
  BEGIN
1047
    UPDATE hello SET salutation = in_new_salutation WHERE salutation = in_old_salutation;
1048
    RETURN TRUE;
1049
  END;
1050
$BODY$ LANGUAGE PLPGSQL;
1051
1052 5 Lacey Powers
</pre>
1053 1 bford -
1054
Execute the following command, in your ~/project/sql directory.
1055
1056 5 Lacey Powers
<pre>
1057 1 bford -
  nano hello.sql
1058 5 Lacey Powers
</pre>
1059 1 bford -
1060
Paste the above SQL code into that file, save and quit.
1061
1062
Then run the following command.
1063
1064 5 Lacey Powers
<pre>
1065 1 bford -
psql -U helloworld helloworld -f hello.sql
1066 5 Lacey Powers
</pre>
1067 1 bford -
1068
Success should look like this.
1069
1070 5 Lacey Powers
<pre>
1071 1 bford -
lacey@blinky:~/project/sql$ psql -U helloworld helloworld -f hello.sql 
1072
psql:hello.sql:5: NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "hello_pkey" for table "hello"
1073
CREATE TABLE
1074
INSERT 0 1
1075
INSERT 0 1
1076
INSERT 0 1
1077
INSERT 0 1
1078
INSERT 0 1
1079
INSERT 0 1
1080
CREATE FUNCTION
1081
CREATE FUNCTION
1082
CREATE FUNCTION
1083
lacey@blinky:~/project/sql$
1084 5 Lacey Powers
</pre>
1085 1 bford -
1086 5 Lacey Powers
Now that the necessary tables, functions, and data are in place in [[PostgreSQL]], we can move on to making our hello model.
1087 1 bford -
1088
Execute the following command to move back into your helloworld project directory.
1089
1090 5 Lacey Powers
<pre>
1091 1 bford -
cd ../helloworld/helloworld
1092 5 Lacey Powers
</pre>
1093 1 bford -
1094
The directory you are in should look like this, if you execute the command ls -l :
1095
1096 5 Lacey Powers
<pre>
1097 1 bford -
lacey@blinky:~/project/helloworld/helloworld$ ls -l
1098
total 36K
1099
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 16:52 config
1100
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-06 18:57 controllers
1101 5 Lacey Powers
-rw-r--r-- 1 lacey lacey    0 2009-06-05 08:46 +init+.py
1102
-rw-r--r-- 1 lacey lacey  140 2009-06-05 15:07 +init+.pyc
1103 1 bford -
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 15:26 lib
1104
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-06 19:35 model
1105
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 16:40 public
1106
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 templates
1107
drwxr-xr-x 3 lacey lacey 4.0K 2009-06-05 08:46 tests
1108
-rw-r--r-- 1 lacey lacey  296 2009-06-05 08:46 websetup.py
1109
lacey@blinky:~/project/helloworld/helloworld$ 
1110 5 Lacey Powers
</pre>
1111 1 bford -
1112
Recall that earlier, when discussing the structure of the projects, that the "model" directory was where the files related to your ORM are placed.
1113
1114
We are going to create our model for our Hello World project. 
1115
1116
Execute the following command:
1117
1118 5 Lacey Powers
<pre>
1119 1 bford -
nano model/hello_model.py
1120 5 Lacey Powers
</pre>
1121 1 bford -
1122
And paste the following code into it:
1123
1124 5 Lacey Powers
<pre>
1125 1 bford -
from simpycity.core import Function, Raw 
1126 5 Lacey Powers
from simpycity.model import [[SimpleModel]], Function, Construct
1127 1 bford -
from simpycity.core import Raw 
1128
1129 5 Lacey Powers
class [[HelloModel]](SimpleModel):
1130 1 bford -
1131
   # Getters
1132
   get_salutation = Raw("SELECT salutation FROM public.hello ORDER BY random() LIMIT 1");
1133
1134
   # Setters
1135
   add_salutation = Function("public.add_salutation",['salutation','language']);
1136
   upd_salutation = Function("public.update_salutation",['old_salutation','new_salutation']);
1137
1138
   # Deleters
1139
   del_salutation = Function("public.remove_salutation",['salutation','language']);                                                                          
1140 5 Lacey Powers
</pre>
1141 1 bford -
1142
This code sets up Simpycity for use in our application.
1143
1144
At the top, are the required imports from Simpycity.
1145
1146 5 Lacey Powers
There are two basic ways that Simpycity interacts with the [[PostgreSQL]] database beneath it.
1147 1 bford -
1148
Function is how Simpycity maps Python to the stored procedures in the database.
1149
1150
Looking at this line:
1151
1152 5 Lacey Powers
<pre>
1153 1 bford -
add_salutation = Function("public.add_salutation",['salutation','language']);
1154 5 Lacey Powers
</pre>
1155 1 bford -
1156
The double-quoted portion of this function, "public.add_salutation", is the schema qualified name of the stored procedure we are looking to map to.
1157
1158
The bracketed portion of this function, ['salutation','language'], map the arguments to the function.
1159
1160
For example, when we call this function:
1161
1162 5 Lacey Powers
return_status = [[HelloModel]].add_salutation("sawadee ka","Thai")
1163 1 bford -
1164
Will map to "SELECT * FROM public.add_salutation('sawadee ka','Thai');", which will insert the salutation "namaste" and the language "hindi" into the database, and return true, which sets the value of "return_status" to true.
1165
1166
Raw is the Simpycity way of mapping straight SQL to the database.
1167
1168
get_salutation = Raw("SELECT salutation FROM public.hello ORDER BY random() LIMIT 1");
1169
1170
For example, calling this function: 
1171
1172 5 Lacey Powers
return_item = [[HelloModel]].get_salutation()
1173 1 bford -
1174
Raw simply executes the provided query, which in this example, is:
1175
1176 5 Lacey Powers
<pre>
1177 1 bford -
SELECT salutation FROM public.hello ORDER BY random() LIMIT 1;
1178 5 Lacey Powers
</pre>
1179 1 bford -
1180
Returning a random way to say hello into the variable "return_item".
1181
1182
Now, we need to modify the Hello Controller.
1183
1184
Execute the command:
1185
1186 5 Lacey Powers
<pre>
1187 1 bford -
nano controller/hello.py
1188 5 Lacey Powers
</pre>
1189 1 bford -
1190
The contents of which are:
1191
1192 5 Lacey Powers
<pre>
1193 1 bford -
import logging
1194
1195
from pylons import request, response, session, tmpl_context as c
1196
from pylons.controllers.util import abort, redirect_to
1197
1198 5 Lacey Powers
from helloworld.lib.base import [[BaseController]], render
1199 1 bford -
1200 5 Lacey Powers
log = logging.getLogger(+name+)
1201 1 bford -
1202 5 Lacey Powers
class [[HelloController]](BaseController):
1203 1 bford -
1204
    def index(self):
1205
        # Return a rendered template
1206
        #return render('/hello.mako')
1207
        # or, return a response
1208
        return 'Hello World'
1209 5 Lacey Powers
</pre>
1210 1 bford -
1211
We will add:
1212
1213 5 Lacey Powers
<pre>
1214
from helloworld.model.hello_model import [[HelloModel]]
1215
</pre>
1216 1 bford -
1217
and:
1218
1219 5 Lacey Powers
<pre>
1220
salutation = [[HelloModel]].get_salutation(options=dict(fold_output=True))
1221 1 bford -
return salutation
1222 5 Lacey Powers
</pre>
1223 1 bford -
1224
To the hello controller, and we will comment out the line "return 'Hello World'", so that our controller now looks like this:
1225
1226 5 Lacey Powers
<pre>
1227 1 bford -
import logging
1228
1229
from pylons import request, response, session, tmpl_context as c
1230
from pylons.controllers.util import abort, redirect_to
1231
1232 5 Lacey Powers
from helloworld.lib.base import [[BaseController]], render
1233 1 bford -
1234 5 Lacey Powers
from helloworld.model.hello_model import [[HelloModel]]
1235 1 bford -
1236 5 Lacey Powers
log = logging.getLogger(+name+)
1237 1 bford -
1238 5 Lacey Powers
class [[HelloController]](BaseController):
1239 1 bford -
1240
    def index(self):
1241
        # Return a rendered template
1242
        #return render('/hello.mako')
1243
        # or, return a response
1244
        #return 'Hello World'
1245 5 Lacey Powers
        salutation = [[HelloModel]].get_salutation(options=dict(fold_output=True))
1246 1 bford -
        return salutation
1247 5 Lacey Powers
</pre>
1248 1 bford -
1249 5 Lacey Powers
Now, combining the database functions we created, the [[HelloModel]] that we created, and this code, we will be able to query the database.
1250 1 bford -
1251
To see the code in action, all we have to do is start the server again.
1252
1253
So we execute the command
1254 5 Lacey Powers
<pre>
1255 1 bford -
cd ..
1256 5 Lacey Powers
</pre>
1257 1 bford -
1258
And then execute: 
1259
1260 5 Lacey Powers
<pre>
1261 1 bford -
paster serve --reload development.ini
1262 5 Lacey Powers
</pre>
1263 1 bford -
1264
And open our browsers to http://localhost:5000/
1265
1266
This should show you a randomly chosen way to say hello.
1267
1268
Pressing the refresh button will show you another, and another.
1269
1270
Now, let's add a bit to it with a Genshi Template.
1271
1272
Press <ctrl>-<c> to stop your server.
1273
1274
Now, we refer back to the directory layout of the project.
1275
1276 5 Lacey Powers
<pre>
1277 1 bford -
lacey@blinky:~/project/helloworld/helloworld$ ls -l
1278
total 36K
1279
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 16:52 config
1280
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-06 18:57 controllers
1281 5 Lacey Powers
-rw-r--r-- 1 lacey lacey    0 2009-06-05 08:46 +init+.py
1282
-rw-r--r-- 1 lacey lacey  140 2009-06-05 15:07 +init+.pyc
1283 1 bford -
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 15:26 lib
1284
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-06 19:35 model
1285
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 16:40 public
1286
drwxr-xr-x 2 lacey lacey 4.0K 2009-06-05 08:46 templates
1287
drwxr-xr-x 3 lacey lacey 4.0K 2009-06-05 08:46 tests
1288
-rw-r--r-- 1 lacey lacey  296 2009-06-05 08:46 websetup.py
1289
lacey@blinky:~/project/helloworld/helloworld$ 
1290 5 Lacey Powers
</pre>
1291 1 bford -
1292
The templates directory is where all of our dynamically generated page templates (created by Genshi, as you recall from the very beginning of this tutorial).
1293
1294
So we should execute the following command.
1295
1296 5 Lacey Powers
<pre>
1297 1 bford -
cd helloworld/templates
1298 5 Lacey Powers
</pre>
1299 1 bford -
1300
Now, we are going to look at the contents of this directory. 
1301
1302 5 Lacey Powers
<pre>
1303 1 bford -
lacey@blinky:~/project/helloworld/helloworld/templates$ ls -l
1304
total 0
1305 5 Lacey Powers
-rw-r--r-- 1 lacey lacey    0 2009-06-05 08:46 +init+.py
1306 1 bford -
lacey@blinky:~/project/helloworld/helloworld/templates$ 
1307 5 Lacey Powers
</pre>
1308 1 bford -
1309 5 Lacey Powers
This only contains a blank +init+.py file. This is only because the Genshi templating engine within Pylons will ignore any template directories without an +init+.py in them.
1310 1 bford -
1311
Armed with that knowledge, we will create our own directory.
1312
1313 5 Lacey Powers
<pre>
1314 1 bford -
mkdir hello
1315 5 Lacey Powers
</pre>
1316 1 bford -
1317
We will move into it:
1318 5 Lacey Powers
<pre>
1319 1 bford -
cd hello
1320 5 Lacey Powers
</pre>
1321 1 bford -
1322 5 Lacey Powers
and we will make a blank +init+.py file.
1323 1 bford -
1324 5 Lacey Powers
<pre>
1325
touch +init+.py
1326
</pre>
1327 1 bford -
1328
We will also make another file.
1329
1330 5 Lacey Powers
<pre>
1331 1 bford -
nano hello.html
1332 5 Lacey Powers
</pre>
1333 1 bford -
1334
And within that we will paste the following contents.
1335
1336 5 Lacey Powers
<pre>
1337 1 bford -
 <html xmlns="http://www.w3.org/1999/xhtml">
1338
  <head>
1339
    <title>${c.hello}</title>
1340
  </head>
1341
  <body class="index">
1342
    <div id="header">
1343
      <h1>${c.hello}</h1>
1344
    </div>
1345
    <p>That is hello in: ${c.language}</p>
1346
  </body>
1347
</html>
1348 5 Lacey Powers
</pre>
1349 1 bford -
1350
Save and quit.
1351
1352
This is a very basic xHTML Genshi template. 
1353
1354
Aside from having syntax for dynamically setting variables, Genshi templates can be treated the same as any other xHTML page.
1355
1356
Within this template are dynamically setting the title, a header, a small bit of content.
1357
1358
${c.hello} And ${c.language} are variables within the template, that we will set within our controller.
1359
1360
For more information on the Genshi templates, please consult the documentation, found here:
1361
1362
http://genshi.edgewall.org/wiki/Documentation
1363
1364
But before we render this page, we need to make a few more modifications.
1365
1366
We move out of this directory, into the model directory.
1367
1368 5 Lacey Powers
<pre>
1369 1 bford -
cd ../../model
1370 5 Lacey Powers
</pre>
1371 1 bford -
1372
And open the file hello_model.py. We will add the following content. 
1373
1374 5 Lacey Powers
<pre>
1375 1 bford -
get_language = Raw("SELECT language FROM public.hello WHERE salutation = %s", ['salutation']);
1376 5 Lacey Powers
</pre>
1377 1 bford -
1378
This is another variation of the Raw function, in which we are able to substitute variables into the query.
1379
1380
Save and quit.
1381
1382
Lastly, we move to the controller directory, and modify our controller.
1383
1384 5 Lacey Powers
<pre>
1385 1 bford -
cd ../controller
1386 5 Lacey Powers
</pre>
1387 1 bford -
1388 5 Lacey Powers
<pre>
1389 1 bford -
nano hello.py
1390 5 Lacey Powers
</pre>
1391 1 bford -
1392
We will add the following import:
1393
1394 5 Lacey Powers
<pre>
1395 1 bford -
from pylons.templating import render_genshi as render
1396 5 Lacey Powers
</pre>
1397 1 bford -
1398
Which now brings the render_genshi function into our controller, and aliases it to render.
1399
1400
We will also comment out "return salutation"
1401
1402 5 Lacey Powers
<pre>
1403 1 bford -
#return salutation
1404 5 Lacey Powers
</pre>
1405 1 bford -
1406
We will add the following lines:
1407
1408 5 Lacey Powers
<pre>
1409
c.language = [[HelloModel]].get_language(salutation, options=dict(fold_output=True))
1410 1 bford -
c.hello = salutation
1411
return render("hello/hello.html");
1412 5 Lacey Powers
</pre>
1413 1 bford -
1414
This should leave the file looking like this:
1415
1416 5 Lacey Powers
<pre>
1417 1 bford -
import logging
1418
1419
from pylons import request, response, session, tmpl_context as c
1420
from pylons.controllers.util import abort, redirect_to
1421
1422 5 Lacey Powers
from helloworld.lib.base import [[BaseController]], render
1423 1 bford -
1424 5 Lacey Powers
from helloworld.model.hello_model import [[HelloModel]]
1425 1 bford -
from pylons.templating import render_genshi as render
1426
1427 5 Lacey Powers
log = logging.getLogger(+name+)
1428 1 bford -
1429 5 Lacey Powers
class [[HelloController]](BaseController):
1430 1 bford -
1431
    def index(self):
1432
        # Return a rendered template
1433
        #return render('/hello.mako')
1434
        # or, return a response
1435
        #return 'Hello World'
1436 5 Lacey Powers
        salutation = [[HelloModel]].get_salutation(options=dict(fold_output=True))
1437
        c.language = [[HelloModel]].get_language(salutation, options=dict(fold_output=True))
1438 1 bford -
        c.hello = salutation
1439
        return render("hello/hello.html");
1440
        #return salutation
1441 5 Lacey Powers
</pre>
1442 1 bford -
1443
The c. prefix to the variables are special to Genshi, and let it know what variables it should be rendering.
1444
1445 5 Lacey Powers
We are still getting a salutation from the [[HelloModel]].get_salutation() function.
1446
Now, we are taking that result, and using it to get the language for the saluation, using the [[HelloModel]].get_language() function.
1447 1 bford -
1448
We are then setting the c.hello variable to the same value as the salutation variable.
1449 5 Lacey Powers
And the c.language variable is set to the return value of the [[HelloModel]].get_language() function.
1450 1 bford -
1451
return render("hello/hello.html") tells Genshi where the file we want to render is in relation to the template directory.
1452
1453
If hello.html was simply in the template directory, the return statement would be:
1454
1455
return render("hello.html")
1456
1457
But since it is in the hello directory, we have to represent the rest of the path to the xHTML template.
1458
1459 5 Lacey Powers
And for the [[HelloModel]] Simpycity functions, we add new parameter. options=dict(fold_output=True)
1460 1 bford -
1461
Since we know these are going to return a single row only, we are collapsing the output of the query, causing it to return a value into the variable that is ready to be used by Python.
1462
1463
Now, to test.
1464
1465
Again, we back out of this directory to the outermost helloworld directory, in order to start the server.
1466
1467 5 Lacey Powers
<pre>
1468 1 bford -
cd ../..
1469 5 Lacey Powers
</pre>
1470 1 bford -
1471 5 Lacey Powers
<pre>
1472 1 bford -
paster serve --reload development.ini
1473 5 Lacey Powers
</pre>
1474 1 bford -
1475
And again, we browse to the page http:://localhost:5000 in our browser.
1476
1477
Which should now look something like this: 
1478
1479 5 Lacey Powers
<pre>
1480 1 bford -
bonjour
1481
1482
That is hello in: French
1483 5 Lacey Powers
</pre>
1484 1 bford -
1485
Now, displaying dynamic content is only one part of dealing with the database.
1486
1487
We need to be able to manipulate the content. 
1488
1489
Deleting and updating content within the database are both crucial parts. 
1490
1491
So, we're going to add the remaining parts to make this a well-rounded example.
1492
1493
So we stop the server (<ctrl>-<c>, again), and we move to the helloworld/public directory.
1494 5 Lacey Powers
<pre>
1495 1 bford -
cd helloworld/public
1496 5 Lacey Powers
</pre>
1497 1 bford -
1498
And we copy/paste the following files:
1499
1500 5 Lacey Powers
<pre>
1501 1 bford -
nano add_salutation.html
1502 5 Lacey Powers
</pre>
1503 1 bford -
1504
And we paste the following code.
1505
1506 5 Lacey Powers
<pre>
1507 1 bford -
<html>
1508
   <body>
1509
    <form action="/hello/add_salutation" method="POST">
1510
       <p> 
1511
          Add Salutation:<br/>
1512
          Salutation: <input type="text" name="salutation"><br/>
1513
          Language: <input type="text" name="language"><br/>
1514
          <input type="submit" value="submit">
1515
       </p>
1516
    </form>
1517
   </body>
1518
</html>
1519 5 Lacey Powers
</pre>
1520 1 bford -
1521
Save and close. 
1522
1523 5 Lacey Powers
<pre>
1524 1 bford -
nano modify_salutation.html
1525 5 Lacey Powers
</pre>
1526 1 bford -
1527
And we paste the following code.
1528
1529 5 Lacey Powers
<pre>
1530 1 bford -
<html>
1531
   <body>
1532
    <form action="/hello/modify_salutation" method="POST">
1533
       <p> 
1534
          Modify Salutation:<br/>
1535
          Old Salutation: <input type="text" name="old_salutation"><br/>
1536
          New Salutation: <input type="text" name="new_salutation"><br/>
1537
          <input type="submit" value="submit">
1538
       </p>
1539
    </form>
1540
   </body>
1541
</html>
1542 5 Lacey Powers
</pre>
1543 1 bford -
1544
Save and close.
1545
1546 5 Lacey Powers
<pre>
1547 1 bford -
nano remove_salutation.html
1548 5 Lacey Powers
</pre>
1549 1 bford -
1550
And we paste the following code.
1551
1552 5 Lacey Powers
<pre>
1553 1 bford -
<html>
1554
   <body>
1555
    <form action="/hello/remove_salutation" method="POST">
1556
       <p> 
1557
          Remove Salutation:<br/>
1558
          Salutation: <input type="text" name="salutation"><br/>
1559
          Language: <input type="text" name="language"><br/>
1560
          <input type="submit" value="submit">
1561
       </p>
1562
    </form>
1563
   </body>
1564
</html>
1565 5 Lacey Powers
</pre>
1566 1 bford -
1567
Save and close.
1568
1569
Each of these files are simple, static pages, that submit forms to different actions in the hello controller. 
1570
Each of the forms has two fields in which to specify the language and salutation. 
1571
In the case of modify_salutation.html, we specify an old and new salutation.
1572
Plus, the ever helpful submit button.
1573
1574
Currently, though, the hello controller only contains the index action. So we will need to modify that.
1575
1576
We navigate to the controllers directory.
1577
1578 5 Lacey Powers
<pre>
1579 1 bford -
cd ../controllers
1580 5 Lacey Powers
</pre>
1581 1 bford -
1582
And open hello.py for editing
1583
1584 5 Lacey Powers
<pre>
1585 1 bford -
nano hello.py
1586 5 Lacey Powers
</pre>
1587 1 bford -
1588
We will add the following functions.
1589 5 Lacey Powers
<pre>
1590 1 bford -
    def add_salutation(self):
1591
       if 'salutation' in request.params:
1592
         if 'language' in request.params:
1593
1594
            salutation = request.params['salutation']
1595
            language = request.params['language']
1596
1597 5 Lacey Powers
            rs = [[HelloModel]].add_salutation(salutation, language)
1598
            return_status = rs.next()r0
1599 1 bford -
            rs.commit()
1600
1601
            if return_status == True:
1602
               c.language = language
1603
               c.hello = salutation
1604
               response.status = '200 OK'
1605
               return render("hello/added_salutation.html")
1606
            else:
1607
               c.language = language
1608
               c.hello = salutation
1609
               return render("hello/error.html")
1610
1611
         
1612
    def remove_salutation(self):
1613
      if 'salutation' in request.params:
1614
         if 'language' in request.params:
1615
            salutation = request.params['salutation']
1616
            language = request.params['language']
1617
1618 5 Lacey Powers
            rs = [[HelloModel]].del_salutation(salutation, language)
1619
            return_status = rs.next()r0
1620 1 bford -
            rs.commit()
1621
1622
            if return_status == True:
1623
               c.hello = salutation
1624
               c.language = language
1625
               response.status = '200 OK'
1626
               return render("hello/removed_salutation.html")
1627
            else:
1628
               c.hello = salutation
1629
               c.language = language
1630
               return render("hello/error.html")
1631
1632
1633
    def modify_salutation(self):
1634
      if 'old_salutation' in request.params:
1635
         if 'old_salutation' in request.params:
1636
            old_salutation = request.params['old_salutation']
1637
            new_salutation = request.params['new_salutation']
1638 5 Lacey Powers
            rs = [[HelloModel]].upd_salutation(old_salutation, new_salutation)
1639
            return_status = rs.next()r0
1640 1 bford -
            rs.commit()
1641
1642
            if return_status == True:
1643
               c.old = old_salutation
1644
               c.new = new_salutation
1645
               response.status = '200 OK'
1646
               return render("hello/modified_salutation.html")
1647
            else:
1648
               c.old = old_salutation
1649
               c.new = new_salutation
1650
               return render("hello/error.html")
1651 5 Lacey Powers
</pre>
1652 1 bford -
1653
Each of these functions does basically the same thing, with small variations. 
1654
They read the request parameters, and check for essential parameters in the request dict.
1655
Then they pull the parameters out, use them to insert, update, or delete fields in the database, and check for success.
1656
If the change is successful, there is a page showing what the change was, and if it was not successful, it gives you a very basic error message.
1657
Both pages redirect you back to the main page that shows the various salutations and languages.
1658
1659
Now, in each of these functions, there are references to rendered pages that are returned. We will go construct those now.
1660
1661 5 Lacey Powers
<pre>
1662 1 bford -
cd ../templates/hello
1663 5 Lacey Powers
</pre>
1664 1 bford -
1665
And we will create several files here as well:
1666
1667 5 Lacey Powers
<pre>
1668 1 bford -
nano added_salutation.html
1669 5 Lacey Powers
</pre>
1670 1 bford -
1671
And we will paste the following code.
1672
1673 5 Lacey Powers
<pre>
1674 1 bford -
<html xmlns="http://www.w3.org/1999/xhtml">
1675
  <head>
1676
    <title>SUCCESS</title>
1677
  </head>
1678
  <body class="index">
1679
    <div id="header">
1680
      <h1>SUCCESS: Added:</h1>
1681
    </div>
1682
    <p>${c.hello}</p>
1683
    <p>${c.language}</p>
1684
    <p/>
1685
    <a href="/hello">Return home</a>
1686
  </body>
1687
</html>
1688 5 Lacey Powers
</pre>
1689 1 bford -
1690
Save and quit.
1691
1692 5 Lacey Powers
<pre>
1693 1 bford -
nano modified_salutation.html
1694 5 Lacey Powers
</pre>
1695 1 bford -
1696
And we will paste the following code.
1697
1698 5 Lacey Powers
<pre>
1699 1 bford -
<html xmlns="http://www.w3.org/1999/xhtml">
1700
  <head>
1701
    <title>SUCCESS</title>
1702
  </head>
1703
  <body class="index">
1704
    <div id="header">
1705
      <h1>SUCCESS: Modified:</h1>
1706
    </div>
1707
    <p>${c.old}</p>
1708
    <p>${c.new}</p>
1709
    <p/>
1710
    <a href="/hello">Return home</a>
1711
  </body>
1712
</html>
1713 5 Lacey Powers
</pre>
1714 1 bford -
1715
Save and quit.
1716
1717 5 Lacey Powers
<pre>
1718 1 bford -
nano removed_salutation.html
1719 5 Lacey Powers
</pre>
1720 1 bford -
1721
And we will paste the following code.
1722
1723 5 Lacey Powers
<pre>
1724 1 bford -
<html xmlns="http://www.w3.org/1999/xhtml">
1725
  <head>
1726
    <title>SUCCESS</title>
1727
  </head>
1728
  <body class="index">
1729
    <div id="header">
1730
      <h1>SUCCESS: Removed:</h1>
1731
    </div>
1732
    <p>${c.hello}</p>
1733
    <p>${c.language}</p>
1734
    <p/>
1735
    <a href="/hello">Return home</a>
1736
  </body>
1737
</html>
1738 5 Lacey Powers
</pre>
1739 1 bford -
1740
Save and quit.
1741
1742
We will also add a few links to hello.html
1743
1744 5 Lacey Powers
<pre>
1745 1 bford -
nano hello.html
1746 5 Lacey Powers
</pre>
1747 1 bford -
1748 5 Lacey Powers
<pre>
1749 1 bford -
<html xmlns="http://www.w3.org/1999/xhtml">
1750
  <head>
1751
    <title>${c.hello}</title>
1752
  </head>
1753
  <body class="index">
1754
    <div id="header">
1755
      <h1>${c.hello}</h1>
1756
    </div>
1757
    <p>That is hello in: ${c.language}</p>
1758
    <p/>
1759
    <a href="add_salutation.html">Add a salutation</a><br/>
1760
    <a href="remove_salutation.html">Remove a salutation</a><br/>
1761
    <a href="modify_salutation.html">Modify a salutation</a>
1762
  </body>
1763
</html>
1764 5 Lacey Powers
</pre>
1765 1 bford -
1766
Save and quit.
1767
1768
We will also add the following code to a new file called error.html
1769
1770 5 Lacey Powers
<pre>
1771 1 bford -
nano error.html
1772 5 Lacey Powers
</pre>
1773 1 bford -
1774
And we will paste the following code.
1775
1776 5 Lacey Powers
<pre>
1777 1 bford -
<html xmlns="http://www.w3.org/1999/xhtml">
1778
  <head>
1779
    <title>ERROR</title>
1780
  </head>
1781
  <body class="index">
1782
    <div id="header">
1783
      <h1>ERROR: Failed to add/modify/delete</h1>
1784
    </div>
1785
    <p>${c.hello}</p>
1786
    <p>${c.language}</p>
1787
    <p/>
1788
    <a href="/hello">Return home</a>
1789
  </body>
1790
</html>
1791 5 Lacey Powers
</pre>
1792 1 bford -
1793
Save and quit.
1794
1795
Finally, we add a couple of lines to hello.html
1796
1797 5 Lacey Powers
<pre>
1798 1 bford -
nano hello.html
1799 5 Lacey Powers
</pre>
1800 1 bford -
1801 5 Lacey Powers
<pre>
1802 1 bford -
 <html xmlns="http://www.w3.org/1999/xhtml">
1803
  <head>
1804
    <title>${c.hello}</title>
1805
  </head>
1806
  <body class="index">
1807
    <div id="header">
1808
      <h1>${c.hello}</h1>
1809
    </div>
1810
    <p>That is hello in: ${c.language}</p>
1811
    <p/>
1812
    <a href="add_salutation.html">Add a salutation</a><br/>
1813
    <a href="remove_salutation.html">Remove a salutation</a><br/>
1814
    <a href="modify_salutation.html">Modify a salutation</a>
1815
  </body>
1816
</html>
1817 5 Lacey Powers
</pre>
1818 1 bford -
1819
This allows us to access the static pages that are in helloworld/public.
1820
1821
Now, if you recall from eariler, Pylons needs to know how to point these pages at the appropriate controller and action.
1822
1823
And it does that through the routes defined in routing.py.
1824
1825
So we now go modify that.
1826
1827 5 Lacey Powers
<pre>
1828 1 bford -
cd ../../config
1829 5 Lacey Powers
</pre>
1830 1 bford -
1831 5 Lacey Powers
<pre>
1832 1 bford -
nano routing.py
1833 5 Lacey Powers
</pre>
1834 1 bford -
1835
And we add the following lines
1836
1837 5 Lacey Powers
<pre>
1838 1 bford -
    map.connect('add_salutation', '/hello/add_salutation', controller='hello', action='add_salutation', conditions=dict(method=['POST']))
1839
    map.connect('remove_salutation', '/hello/remove_salutation', controller='hello', action='remove_salutation', conditions=dict(method=['POST']))
1840
    map.connect('modify_salutation', '/hello/modify_salutation', controller='hello', action='modify_salutation', conditions=dict(method=['POST']))
1841 5 Lacey Powers
</pre>
1842 1 bford -
1843
These routes will, point the pages at the appropriate controllers and actions, as advertised. The additional conditional dict specifies what sort of HTTP method (GET,POST,DELETE, ect) are supported for this URI. Here we are specifiying POST because all of the form methods associated with these URIs are POST.
1844
1845
And this should be the last set of modifications that we need to do to the application.
1846
1847
Now, we test our new code.
1848
1849 5 Lacey Powers
<pre>
1850 1 bford -
cd ../..
1851 5 Lacey Powers
</pre>
1852 1 bford -
1853
Which should put us into the outermost helloworld directory, with development.ini, so that we can start the server.
1854
1855 5 Lacey Powers
<pre>
1856 1 bford -
paster serve --reload development.ini
1857 5 Lacey Powers
</pre>
1858 1 bford -
1859
Now, browsing to 
1860
1861 5 Lacey Powers
<pre>
1862 1 bford -
http://localhost:5000/hello
1863 5 Lacey Powers
</pre>
1864 1 bford -
1865
Should show you a page like this:
1866
1867 5 Lacey Powers
<pre>
1868 1 bford -
1869
hello
1870
1871
That is hello in: English
1872
1873
Add a salutation
1874
Remove a salutation
1875
Modify a salutation
1876 5 Lacey Powers
</pre>
1877 1 bford -
1878
With the bottom text being HTML links.
1879
1880
Refreshing should take you through various incarnations of "hello" in different languages. 
1881
1882
Now, we will add an additional greeting.
1883
1884
Click the link for Add a salutation.
1885
1886
This should take you to another very simple page with a form and a submit button.
1887
1888
Here, we are going to add hello for the Thai language.
1889
1890
Saluation: sawadee ka
1891
1892
Language: Thai
1893
1894
So we copy those words into the appropriate form boxes, and click "Submit".
1895
1896
We should be taken to a page that looks like this.
1897
1898 5 Lacey Powers
<pre>
1899 1 bford -
1900
SUCCESS: Added:
1901
1902
sawadee ka
1903
1904
Thai
1905
1906
Return home
1907 5 Lacey Powers
</pre>
1908 1 bford -
1909
And if we were to look in the database at this time, we would see the entry has been successfully entered into the database.
1910
1911 5 Lacey Powers
<pre>
1912 1 bford -
lacey@blinky:~/project/sql$ psql -U helloworld
1913 5 Lacey Powers
Welcome to psql 8.3.7, the [[PostgreSQL]] interactive terminal.
1914 1 bford -
1915
Type:  \copyright for distribution terms
1916
       \h for help with SQL commands
1917
       \? for help with psql commands
1918
       \g or terminate with semicolon to execute query
1919
       \q to quit
1920
1921
helloworld=> SELECT * FROM hello;
1922
  salutation   | language 
1923
---------------+----------
1924
 hello         | English
1925
 hola          | Spanish
1926
 bonjour       | French
1927
 merhaba selam | Turkish
1928
 zdravstvuyte  | Russian
1929
 buon giorno   | Italian
1930
 sawadee ka    | Thai
1931
(7 rows)
1932
1933
helloworld=> 
1934 5 Lacey Powers
</pre>
1935 1 bford -
1936
The chain of events is as follows. 
1937 5 Lacey Powers
<pre>
1938 1 bford -
  1. We type the words into the form.
1939
  2. We click submit.
1940
  3. The words are placed into the http request:
1941
1942 5 Lacey Powers
  webob._parsed_post_vars: (MultiDict([('salutation', 'sawadee ka'), ('language', 'Thai')]), <FakeCGIBody at 0x354b5d0 viewing [[MultiDict]]([('ol...R')])>)
1943 1 bford -
1944
  4. We are routed to the add_salutation function in the hello controller via routing.py
1945
  5. The add_salutation function parses the salutation and language out of the http request.
1946
  6. It then hands it off to Simpycity, which translates it into:
1947
1948
  SELECT * FROM public.add_salutation(E'sawadee ka',E'Thai')
1949
1950
  7. Which then inserts it into the database, returning true.
1951
  8. We come back to the add_salutation function, which checks the return, which is True.
1952
  9. We are routed to the success page. Here, we can click to go back to the /hello page.
1953 5 Lacey Powers
</pre>
1954 1 bford -
  
1955
We can now click to see our newly added salutation and language. (which may take a few tries, since it is randomly selected.)
1956
1957 5 Lacey Powers
<pre>
1958 1 bford -
1959
sawadee ka
1960
1961
That is hello in: Thai
1962
1963
Add a salutation
1964
Remove a salutation
1965
Modify a salutation
1966 5 Lacey Powers
</pre>
1967 1 bford -
1968
We can modify a salutation as well.
1969
1970
Click the modify salutation link, which will bring us to the modify salutation page.
1971
1972
We will add capitalization to the Thai greeting that we just added.
1973
1974
Type the following into the form pages:
1975
1976
Old Salutation: sawadee ka
1977
1978
New Salutation: Sawadee Ka
1979
1980
And click submit.
1981
1982
Again, you will be greeted by a page that looks like this.
1983
1984 5 Lacey Powers
<pre>
1985 1 bford -
1986
SUCCESS: Modified:
1987
1988
sawadee ka
1989
1990
Sawadee Ka
1991
1992
Return home
1993 5 Lacey Powers
</pre>
1994 1 bford -
1995
And if we check the database here as well...
1996
1997 5 Lacey Powers
<pre>
1998 1 bford -
lacey@blinky:~/project/helloworld/helloworld/model$ psql -U helloworld
1999 5 Lacey Powers
Welcome to psql 8.3.7, the [[PostgreSQL]] interactive terminal.
2000 1 bford -
2001
Type:  \copyright for distribution terms
2002
       \h for help with SQL commands
2003
       \? for help with psql commands
2004
       \g or terminate with semicolon to execute query
2005
       \q to quit
2006
2007
helloworld=> SELECT * FROM hello;
2008
  salutation   | language 
2009
---------------+----------
2010
 hello         | English
2011
 hola          | Spanish
2012
 bonjour       | French
2013
 merhaba selam | Turkish
2014
 zdravstvuyte  | Russian
2015
 buon giorno   | Italian
2016
 Sawadee Ka    | Thai
2017
(7 rows)
2018
2019
helloworld=> 
2020 5 Lacey Powers
</pre>
2021 1 bford -
2022
We note that the salutation is now capitalized.
2023
2024
The lifecycle of this event is almost exactly the same as the one noted above, except in two places. 
2025
2026
The routing points at the modify_salutation function, and the upd_salutation function is translated into 
2027 5 Lacey Powers
<pre>
2028 1 bford -
SELECT * FROM public.update_salutation(E'sawadee ka',E'Sawadee Ka')
2029 5 Lacey Powers
</pre>
2030 1 bford -
2031
Finally, if we decide that we no longer want the salutation Sawadee Ka in the database, we can delete with a very similar lifecycle.
2032
2033
Click on the Remove a Salutation Link.
2034
2035
Salutation: Sawadee Ka (note that it has to be capitalized now)
2036
Language: Thai
2037
2038 5 Lacey Powers
<pre>
2039 1 bford -
2040
SUCCESS: Removed:
2041
2042
Sawadee Ka
2043
2044
Thai
2045 5 Lacey Powers
</pre>
2046 1 bford -
2047
Checking the database, we note that it is indeed removed.
2048
2049 5 Lacey Powers
<pre>
2050 1 bford -
lacey@blinky:~/project/helloworld/helloworld/model$ psql -U helloworld
2051 5 Lacey Powers
Welcome to psql 8.3.7, the [[PostgreSQL]] interactive terminal.
2052 1 bford -
2053
Type:  \copyright for distribution terms
2054
       \h for help with SQL commands
2055
       \? for help with psql commands
2056
       \g or terminate with semicolon to execute query
2057
       \q to quit
2058
2059
helloworld=> SELECT * FROM hello;
2060
  salutation   | language 
2061
---------------+----------
2062
 hello         | English
2063
 hola          | Spanish
2064
 bonjour       | French
2065
 merhaba selam | Turkish
2066
 zdravstvuyte  | Russian
2067
 buon giorno   | Italian
2068
(6 rows)
2069
2070
helloworld=> 
2071 5 Lacey Powers
</pre>
2072 1 bford -
2073
And as noted above, the lifecycle is the same.
2074
2075
The routing points at the remove_salutation function, and the del_salutation function is translated into 
2076
2077 5 Lacey Powers
<pre>
2078 1 bford -
SELECT * FROM public.remove_salutation(E'Sawadee Ka',E'Thai')
2079 5 Lacey Powers
</pre>
2080 1 bford -
2081
Thus, removing it from the database.
2082
2083
This now completes our small web application.
2084
2085 5 Lacey Powers
Combining Simpycity, Pylons and [[PostgreSQL]], we have made an app that displays database content, and creates, updates, and deletes that content, which is the core functionality of any database driven web application. 
2086 1 bford -
2087 5 Lacey Powers
And this also concludes our [[PostgreSQL]]->Simpycity->Pylons tutorial. 
2088 1 bford -
2089
Thank you for following along. =)