Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

pyramid quick tutorial update, add source files

  • Loading branch information...
commit 79c4e865ec54632d9a187f829464a628165671d5 1 parent f1df55f
Blaise Laflamme authored April 18, 2011
372  docs/pyramid_quick_tutorial.rst
Source Rendered
@@ -4,23 +4,25 @@ Pyramid Quick Tutorial
4 4
 This tutorial is intended to give you a quick overview of the Pyramid Web 
5 5
 Application Framework. Because Pyramid has few opinions on how to 
6 6
 organize and develop your application, this tutorial focus on a minimal 
7  
-approach with common idioms to get a feel of basic Pyramid patterns. While 
8  
-those idioms and patterns are common, it is not suited to use this minimal 
9  
-approach to create a full fledged application, read more advanced tutorials 
10  
-for this purpose.
  7
+single file approach with common idioms to get a feel of basic Pyramid 
  8
+patterns. While those idioms and patterns are common, it is not suited to use 
  9
+this minimal approach to create a full fledged application. For this purpose 
  10
+Pyramid provides starter scaffolds, read more advanced documentation and 
  11
+tutorials.
11 12
 
12 13
 Here's what you'll get at the end of the tutorial, a minimal application to 
13 14
 view, insert and close tasks, backed by an SQLite database for storing your 
14  
-data, presented by mako to render your views and using the routes pattern to 
15  
-match your URLs to code functions.
  15
+data, presented by Mako Templates to render your views and using the routes 
  16
+pattern to match your URLs to code functions.
16 17
 
17 18
 .. image:: pyramid_quick_tutorial.png
18 19
 
19  
-Organizing The Project
20  
-----------------------
  20
+Step 1 - Organizing The Project
  21
+-------------------------------
21 22
 
22  
-Before getting started, create the directory hierarchy needed for the 
23  
-application layout:
  23
+Before getting started, create the directory hierarchy needed for our 
  24
+application layout. The tasks directory will not be used as a python package, 
  25
+it'll just serves as a container to put and organize our project files.
24 26
 
25 27
 .. code-block:: text
26 28
 
@@ -28,35 +30,11 @@ application layout:
28 30
         /static
29 31
         /templates
30 32
 
31  
-The tasks directory created will not be used as a python package, it'll just 
32  
-serves as a container to put and organize our project files.
33  
-
34  
-Database And Schema
35  
--------------------
36  
-
37  
-To make things simple and straightforward we'll use the widely installed 
38  
-SQLite database for our project. The schema for our tasks is also simple, 
39  
-an **id** to uniquely identify the task, a **name** not longer than 100 characters 
40  
-and a **closed** boolean to indicate if the task is closed or not.
41  
-
42  
-Add to the tasks directory a file named schema.sql with the following content:
43  
-
44  
-.. code-block:: sql
45  
-
46  
-    CREATE TABLE IF NOT EXISTS tasks (
47  
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
48  
-        name CHAR(100) NOT NULL,
49  
-        closed BOOL NOT NULL
50  
-    );
51  
-    
52  
-    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (1, 'Start learning Pyramid', 0);
53  
-    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (2, 'Do quick tutorial', 0);
54  
-    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (3, 'Have some beer!', 0);
55  
-
56  
-Application Setup
  33
+Step 2 - Application Setup
57 34
 -----------------
58 35
 
59  
-To begin with our application we'll add a few basic imports
  36
+To begin with our application we'll start by creating a file name tasks.py 
  37
+to the tasks directory and add a few basic imports to the newly created file:
60 38
 
61 39
 .. code-block:: python
62 40
 
@@ -67,7 +45,7 @@ To begin with our application we'll add a few basic imports
67 45
        
68 46
     from paste.httpserver import serve
69 47
 
70  
-Then setup logging and current working directory path
  48
+Then setup logging and current working directory path:
71 49
 
72 50
 .. code-block:: python
73 51
     
@@ -76,42 +54,71 @@ Then setup logging and current working directory path
76 54
     
77 55
     here = os.path.dirname(os.path.abspath(__file__))
78 56
 
79  
-Then configure the Pyramid application registry
  57
+Finally configure the Pyramid application registry, define the wsgi app 
  58
+and serve it.
80 59
 
81 60
 .. code-block:: python
82 61
     
83 62
     if __name__ == '__main__':
84 63
         # configuration settings
85 64
         settings = {}
  65
+        settings['reload_all'] = True
  66
+        settings['debug_all'] = True
86 67
         # configuration setup
87 68
         config = Configurator(settings=settings)
88  
-
89  
-And finally define the app and serve it.
90  
-
91  
-..code-block:: python
92 69
         # serve app
93 70
         app = config.make_wsgi_app()
94 71
         serve(app, host='0.0.0.0')
95 72
 
96  
-We now have the basic application layout to build our project by adding 
97  
-database support, routing, views and templates.
  73
+We now have the basic application layout needed to build our project by 
  74
+adding database support, routing, views and templates.
98 75
 
99  
-Creating The Database
100  
----------------------
  76
+Step 3 - Database And Schema
  77
+----------------------------
101 78
 
102  
-To make the process of creating the database a bit more friendly than 
103  
-executing the import manually with SQLite, we'll subscribe a function to a 
104  
-Pyramid system event for this purpose.
  79
+To make things simple and straightforward we'll use the widely installed 
  80
+SQLite database for our project. The schema for our tasks is also simple, 
  81
+an **id** to uniquely identify the task, a **name** not longer than 100 characters 
  82
+and a **closed** boolean to indicate if the task is closed or not.
  83
+
  84
+Add to the tasks directory a file named schema.sql with the following content:
  85
+
  86
+.. code-block:: sql
  87
+
  88
+    CREATE TABLE IF NOT EXISTS tasks (
  89
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
  90
+        name CHAR(100) NOT NULL,
  91
+        closed BOOL NOT NULL
  92
+    );
  93
+    
  94
+    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (1, 'Start learning Pyramid', 0);
  95
+    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (2, 'Do quick tutorial', 0);
  96
+    INSERT OR IGNORE INTO tasks (id, name, closed) VALUES (3, 'Have some beer!', 0);
  97
+
  98
+We'll continue by adding a few more imports to the tasks.py file:
105 99
 
106 100
 .. code-block:: python
107 101
 
108 102
     ...
  103
+    from pyramid.events import NewRequest
  104
+    from pyramid.events import subscriber
  105
+    from pyramid.events import ApplicationCreated
  106
+    ...
109 107
     from paste.httpserver import serve
110 108
     import sqlite3
111 109
     ...
  110
+
  111
+To make the process of creating the database a bit more friendly than 
  112
+executing the import manually with SQLite, we'll subscribe a function to a 
  113
+Pyramid system event for this purpose. By subscribing to the 
  114
+ApplicationCreated event, each time we'll start the application, our subscribed 
  115
+function will be executed and will create or update the database depending if 
  116
+it's the first time or not.
  117
+
  118
+.. code-block:: python
112 119
     
113 120
     @subscriber(ApplicationCreated)
114  
-    def db_init(event):
  121
+    def application_created_subscriber(event):
115 122
         log.warn('Initializing database...')
116 123
         f = open(os.path.join(here, 'schema.sql'), 'r')
117 124
         stmt = f.read()
@@ -121,23 +128,262 @@ Pyramid system event for this purpose.
121 128
         db.commit()
122 129
         f.close()
123 130
 
124  
-Making The Database Available
125  
------------------------------
  131
+We also need to make our database connection available to the application and 
  132
+a way to do it is through the application request. By subscribing to the 
  133
+Pyramid *NewRequest* event we'll initialize a connection to the database when 
  134
+the request begins and we'll manage to close it down by the end of its 
  135
+lifecycle using the *add_finished_callback* function.  
  136
+
  137
+.. code-block:: python
  138
+
  139
+    @subscriber(NewRequest)
  140
+    def new_request_subscriber(event):
  141
+        request = event.request
  142
+        settings = request.registry.settings
  143
+        request.db = sqlite3.connect(settings['db'])
  144
+        request.add_finished_callback(close_db_connection)
  145
+
  146
+    def close_db_connection(request):
  147
+        request.db.close()
  148
+
  149
+To make those changes working we have to specify where is the database 
  150
+location in the configuration settings and to make sure our @subscriber 
  151
+decorators are scanned by the application at runtime.
  152
+
  153
+.. code-block:: python
  154
+
  155
+    if __name__ == '__main__':
  156
+        ...
  157
+        settings['db'] = os.path.join(here, 'tasks.db')
  158
+        ...
  159
+        config.scan()
  160
+        ...
  161
+
  162
+We now have the basic mechanism in place to create and talk to the database 
  163
+in the application through *request.db*.
  164
+
  165
+Step 4 - Views Functions And Routes
  166
+-----------------------------------
  167
+
  168
+It's now time to provide a way for the application to expose some 
  169
+functionalities to the world. We'll start by adding a few imports and 
  170
+specially the @view_config decorator to let the application discover 
  171
+and register our upcoming views.
  172
+
  173
+.. code-block:: python
  174
+
  175
+    ...
  176
+    from pyramid.exceptions import NotFound
  177
+    from pyramid.httpexceptions import HTTPFound
  178
+    from pyramid.session import UnencryptedCookieSessionFactoryConfig
  179
+    from pyramid.view import view_config
  180
+    ...
  181
+
  182
+Then we'll add some functions to our application for listing, adding and 
  183
+closing todos. When using the @view_config decorator it's important to 
  184
+specify a route_name to match a defined route and a renderer file if the 
  185
+function is intended to return a template. The view function should then 
  186
+return a dictionary expected by the renderer to access variables.
  187
+
  188
+List View
  189
++++++++++
  190
+
  191
+This view is intended to show up all open entries, not closed ones according 
  192
+to our schema, from the database. It uses the *list.mako* template available 
  193
+under the templates directory and defined as the renderer in the *view_config* 
  194
+decorator. The results returned by the query are tuples but we might want to 
  195
+convert them into a dict for easier accessibility within the template. 
  196
+The view function will pass a dict defining *tasks* to the *list.mako* 
  197
+template.
  198
+
  199
+.. code-block:: python
  200
+
  201
+    @view_config(route_name='list', renderer='list.mako')
  202
+    def list_view(request):
  203
+        rs = request.db.execute("select id, name from tasks where closed = 0")
  204
+        tasks = [dict(id=row[0], name=row[1]) for row in rs.fetchall()]
  205
+        return {'tasks': tasks}
  206
+
  207
+New View
  208
+++++++++
  209
+
  210
+This view lets the user add new tasks to the application. If a *name* is 
  211
+provided to the form, a task is added to the database, an information 
  212
+message is flashed to be displayed on the next request and the request is 
  213
+redirected back to the *list_view*. If nothing is provided a warning message 
  214
+is flashed and the *new_view* is displayed again.
  215
+
  216
+.. code-block:: python
  217
+
  218
+    @view_config(route_name='new', renderer='new.mako')
  219
+    def new_view(request):
  220
+        if request.method == 'POST':
  221
+            if request.POST.get('name'):
  222
+                request.db.execute('insert into tasks (name, closed) values (?, ?)',
  223
+                                   [request.POST['name'], 0])
  224
+                request.db.commit()
  225
+                request.session.flash('New task was successfully added!')
  226
+                return HTTPFound(location=request.route_url('list'))
  227
+            else:
  228
+                request.session.flash('Please enter a name for the task!')
  229
+        return {}
  230
+
  231
+.. warning::
  232
+
  233
+    Be sure to use question marks when building SQL statements, otherwise 
  234
+    your application will be vulnerable to SQL injection when using string 
  235
+    formatting.
  236
+
  237
+Close View
  238
+++++++++++
  239
+
  240
+This view lets the user mark a task as closed, flsh a success message and 
  241
+redirects back to the *list_view* page.
  242
+
  243
+.. code-block:: python
  244
+
  245
+    @view_config(route_name='close')
  246
+    def close_view(request):
  247
+        task_id = int(request.matchdict['id'])
  248
+        request.db.execute("update tasks set closed = ? where id = ?", (1, task_id))
  249
+        request.db.commit()
  250
+        request.session.flash('Task was successfully closed!')
  251
+        return HTTPFound(location=request.route_url('list'))
  252
+
  253
+NotFound View
  254
++++++++++++++
  255
+
  256
+This view lets customize the default NotFound view provided by Pyramid by 
  257
+using your own template.
  258
+
  259
+.. code-block:: python
  260
+
  261
+    @view_config(context='pyramid.exceptions.NotFound',
  262
+                 renderer='notfound.mako')
  263
+    def notfound_view(self):
  264
+        return {}
  265
+
  266
+We finally need to add some routing elements in our application configuration 
  267
+if we want our view functions to be exposed.
  268
+
  269
+.. code-block:: python
  270
+
  271
+    ...
  272
+    # routes setup
  273
+    config.add_route('list', '/')
  274
+    config.add_route('new', '/new')
  275
+    config.add_route('close', '/close/{id}')
  276
+    ...
  277
+
  278
+We now have the basic mechanism in place to add functionality to the 
  279
+application by defining views and expose them through the routes system.
  280
+
  281
+Step 5 - View Templates
  282
+-----------------------
  283
+
  284
+Next step is to provide the application what web browser knows; **HTML**. 
  285
+To ease HTML development we'll use one of the default templating engines 
  286
+supported out of the box by Pyramid; *Mako Templates*.
  287
+
  288
+We'll also use template inheritance which makes it possible to reuse a 
  289
+generic layout across multiple templates, easing layout maintenance and 
  290
+uniformity.
  291
+
  292
+Create the following templates into the *templates* directory with their 
  293
+respective content:
  294
+
  295
+layout.mako
  296
++++++++++++
  297
+
  298
+This template contains the basic layout structure to be shared with other 
  299
+templates. Inside the body tag we have defined a block to display flash 
  300
+messages sent by the application and another block to display the content of 
  301
+the page inheriting this master layout using the mako directive 
  302
+*${next.body()}*.
  303
+
  304
+.. literalinclude:: pyramid_quick_tutorial/templates/layout.mako
  305
+   :language: html+mako
  306
+
  307
+list.mako
  308
++++++++++
  309
+
  310
+This template extends the master *layout.mako* template by providing a 
  311
+listing of tasks. The loop is done using the passed tasks dict sent from 
  312
+the *list_view* using the pythonic mako syntax. We also use the 
  313
+*request.route_url* function to generate a url based on a route name and its 
  314
+arguments instead of statically defining the url path.
  315
+
  316
+.. literalinclude:: pyramid_quick_tutorial/templates/list.mako
  317
+   :language: html+mako
  318
+
  319
+new.mako
  320
+++++++++
  321
+
  322
+This template extends the master *layout.mako* template by providing a basic 
  323
+form to add new tasks.
  324
+
  325
+.. literalinclude:: pyramid_quick_tutorial/templates/new.mako
  326
+   :language: html+mako
  327
+
  328
+notfound.mako
  329
++++++++++++++
  330
+
  331
+This template extends the master *layout.mako* template by customizing the 
  332
+default *NotFound* view provided by Pyramid.
  333
+
  334
+.. literalinclude:: pyramid_quick_tutorial/templates/notfound.mako
  335
+   :language: html+mako
  336
+
  337
+To make those templates working we now have to specify where are the 
  338
+templates to mako into the application configuration settings.
  339
+
  340
+.. code-block:: python
  341
+
  342
+    ...
  343
+    settings['mako.directories'] = os.path.join(here, 'templates')
  344
+    ...
  345
+
  346
+Step 6 - Styling Your Templates
  347
+-------------------------------
  348
+
  349
+It's now time to add some styling to the application templates by adding a 
  350
+**CSS** file named *style.css* to the *static* directory with the following 
  351
+content:
  352
+
  353
+.. literalinclude:: pyramid_quick_tutorial/static/style.css
  354
+   :language: css
  355
+
  356
+To make this static file served by the application we must add a static view 
  357
+directive to the application configuration:
  358
+
  359
+.. code-block:: python
  360
+
  361
+    ...
  362
+    config.add_static_view('static', os.path.join(here, 'static'))
  363
+    ...
126 364
 
127  
-text
  365
+Step 7 - Running The Application
  366
+--------------------------------
128 367
 
129  
-Routes And Views Functions
130  
---------------------------
  368
+We have now completed every steps needed to run the application in its final 
  369
+version. Before running it, here's the complete main code for *task.py* for 
  370
+review:
131 371
 
132  
-text
  372
+.. literalinclude:: pyramid_quick_tutorial/tasks.py
  373
+   :language: python
133 374
 
134  
-View Templates
135  
---------------
  375
+And now let's run *tasks*:
136 376
 
137  
-text
  377
+.. code-block:: bash
138 378
 
139  
-Styling Your Templates
140  
-----------------------
  379
+    $ python tasks.py 
  380
+    WARNING:tasks.py:Initializing database...
  381
+    serving on 0.0.0.0:8080 view at http://127.0.0.1:8080
141 382
 
142  
-text
  383
+Conclusion
  384
+----------
143 385
 
  386
+This introduction to Pyramid was inspired by **flask** and **bottle** 
  387
+tutorials with the same minimalistic approach in mind. Big thanks Chris 
  388
+McDonough, Carlos De La Guardia and Casey Duncan for their support and 
  389
+friendship.
9  docs/pyramid_quick_tutorial/schema.sql
... ...
@@ -0,0 +1,9 @@
  1
+create table if not exists tasks (
  2
+    id integer primary key autoincrement,
  3
+    name char(100) not null,
  4
+    closed bool not null
  5
+);
  6
+insert or ignore into tasks (id, name, closed) values (1, 'Start learning Pyramid', 0);
  7
+insert or ignore into tasks (id, name, closed) values (2, 'Do quick tutorial', 0);
  8
+insert or ignore into tasks (id, name, closed) values (3, 'Have some beer!', 0);
  9
+
75  docs/pyramid_quick_tutorial/static/style.css
... ...
@@ -0,0 +1,75 @@
  1
+body {
  2
+  font-family: sans-serif;
  3
+  font-size: 14px;
  4
+  color: #3e4349;
  5
+}
  6
+
  7
+h1, h2, h3, h4, h5, h6 {
  8
+  font-family: Georgia;
  9
+  color: #373839;
  10
+}
  11
+
  12
+a {
  13
+  color: #1b61d6;
  14
+  text-decoration: none;
  15
+}
  16
+
  17
+input {
  18
+  font-size: 14px;
  19
+  width: 400px;
  20
+  border: 1px solid #bbbbbb;
  21
+  padding: 5px;
  22
+}
  23
+
  24
+.button {
  25
+  font-size: 14px;
  26
+  font-weight: bold;
  27
+  width: auto;
  28
+  background: #eeeeee;
  29
+  padding: 5px 20px 5px 20px;
  30
+  border: 1px solid #bbbbbb;
  31
+  border-left: none;
  32
+  border-right: none;
  33
+}
  34
+
  35
+#flash, #notfound {
  36
+  font-size: 16px;
  37
+  width: 500px;
  38
+  text-align: center;
  39
+  background-color: #e1ecfe;
  40
+  border-top: 2px solid #7a9eec;
  41
+  border-bottom: 2px solid #7a9eec;
  42
+  padding: 10px 20px 10px 20px;
  43
+}
  44
+
  45
+#notfound {
  46
+  background-color: #fbe3e4;
  47
+  border-top: 2px solid #fbc2c4;
  48
+  border-bottom: 2px solid #fbc2c4;
  49
+  padding: 0 20px 30px 20px;
  50
+}
  51
+
  52
+#tasks {
  53
+  width: 500px;
  54
+}
  55
+
  56
+#tasks li {
  57
+  padding: 5px 0 5px 0;
  58
+  border-bottom: 1px solid #bbbbbb;
  59
+}
  60
+
  61
+#tasks li.last {
  62
+  border-bottom: none;
  63
+}
  64
+
  65
+#tasks .name {
  66
+  width: 400px;
  67
+  text-align: left;
  68
+  display: inline-block;
  69
+}
  70
+
  71
+#tasks .actions {
  72
+  width: 80px;
  73
+  text-align: right;
  74
+  display: inline-block;
  75
+}
96  docs/pyramid_quick_tutorial/tasks.py
... ...
@@ -0,0 +1,96 @@
  1
+import os
  2
+import logging
  3
+
  4
+from pyramid.config import Configurator
  5
+from pyramid.events import NewRequest
  6
+from pyramid.events import subscriber
  7
+from pyramid.events import ApplicationCreated
  8
+from pyramid.exceptions import NotFound
  9
+from pyramid.httpexceptions import HTTPFound
  10
+from pyramid.session import UnencryptedCookieSessionFactoryConfig
  11
+from pyramid.view import view_config
  12
+
  13
+from paste.httpserver import serve
  14
+import sqlite3
  15
+
  16
+logging.basicConfig()
  17
+log = logging.getLogger(__file__)
  18
+
  19
+here = os.path.dirname(os.path.abspath(__file__))
  20
+
  21
+# views
  22
+@view_config(route_name='list', renderer='list.mako')
  23
+def list_view(request):
  24
+    rs = request.db.execute("select id, name from tasks where closed = 0")
  25
+    tasks = [dict(id=row[0], name=row[1]) for row in rs.fetchall()]
  26
+    return {'tasks': tasks}
  27
+
  28
+@view_config(route_name='new', renderer='new.mako')
  29
+def new_view(request):
  30
+    if request.method == 'POST':
  31
+        if request.POST.get('name'):
  32
+            request.db.execute('insert into tasks (name, closed) values (?, ?)',
  33
+                               [request.POST['name'], 0])
  34
+            request.db.commit()
  35
+            request.session.flash('New task was successfully added!')
  36
+            return HTTPFound(location=request.route_url('list'))
  37
+        else:
  38
+            request.session.flash('Please enter a name for the task!')
  39
+    return {}
  40
+
  41
+@view_config(route_name='close')
  42
+def close_view(request):
  43
+    task_id = int(request.matchdict['id'])
  44
+    request.db.execute("update tasks set closed = ? where id = ?", (1, task_id))
  45
+    request.db.commit()
  46
+    request.session.flash('Task was successfully closed!')
  47
+    return HTTPFound(location=request.route_url('list'))
  48
+
  49
+@view_config(context='pyramid.exceptions.NotFound', renderer='notfound.mako')
  50
+def notfound_view(self):
  51
+    return {}
  52
+
  53
+# subscribers
  54
+@subscriber(NewRequest)
  55
+def new_request_subscriber(event):
  56
+    request = event.request
  57
+    settings = request.registry.settings
  58
+    request.db = sqlite3.connect(settings['db'])
  59
+    request.add_finished_callback(close_db_connection)
  60
+
  61
+def close_db_connection(request):
  62
+    request.db.close()
  63
+    
  64
+@subscriber(ApplicationCreated)
  65
+def application_created_subscriber(event):
  66
+    log.warn('Initializing database...')
  67
+    f = open(os.path.join(here, 'schema.sql'), 'r')
  68
+    stmt = f.read()
  69
+    settings = event.app.registry.settings
  70
+    db = sqlite3.connect(settings['db'])
  71
+    db.executescript(stmt)
  72
+    db.commit()
  73
+    f.close()
  74
+
  75
+if __name__ == '__main__':
  76
+    # configuration settings
  77
+    settings = {}
  78
+    settings['reload_all'] = True
  79
+    settings['debug_all'] = True
  80
+    settings['mako.directories'] = os.path.join(here, 'templates')
  81
+    settings['db'] = os.path.join(here, 'tasks.db')
  82
+    # session factory
  83
+    session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
  84
+    # configuration setup
  85
+    config = Configurator(settings=settings, session_factory=session_factory)
  86
+    # routes setup
  87
+    config.add_route('list', '/')
  88
+    config.add_route('new', '/new')
  89
+    config.add_route('close', '/close/{id}')
  90
+    # static view setup
  91
+    config.add_static_view('static', os.path.join(here, 'static'))
  92
+    # scan for @view_config and @subscriber decorators
  93
+    config.scan()
  94
+    # serve app
  95
+    app = config.make_wsgi_app()
  96
+    serve(app, host='0.0.0.0')
31  docs/pyramid_quick_tutorial/templates/layout.mako
... ...
@@ -0,0 +1,31 @@
  1
+# -*- coding: utf-8 -*- 
  2
+<!DOCTYPE html>  
  3
+<head>
  4
+	
  5
+  <meta charset="utf-8">
  6
+  <title>Pyramid Task's List Tutorial</title>
  7
+  <meta name="author" content="Pylons Project">
  8
+  <link rel="shortcut icon" href="/static/favicon.ico">
  9
+  <link rel="stylesheet" href="/static/style.css">
  10
+
  11
+</head>
  12
+
  13
+<body>
  14
+
  15
+  % if request.session.peek_flash():
  16
+  <div id="flash">
  17
+    <% flash = request.session.pop_flash() %>
  18
+	% for message in flash:
  19
+	${message}<br>
  20
+	% endfor
  21
+  </div>
  22
+  % endif
  23
+
  24
+  <div id="page">
  25
+    
  26
+    ${next.body()}
  27
+
  28
+  </div>
  29
+  
  30
+</body>
  31
+</html>
22  docs/pyramid_quick_tutorial/templates/list.mako
... ...
@@ -0,0 +1,22 @@
  1
+# -*- coding: utf-8 -*- 
  2
+<%inherit file="layout.mako"/>
  3
+
  4
+<h1>Task's List</h1>
  5
+
  6
+<ul id="tasks">
  7
+% if tasks:
  8
+  % for task in tasks:
  9
+  <li>
  10
+    <span class="name">${task['name']}</span>
  11
+    <span class="actions">
  12
+      [ <a href="${request.route_url('close', id=task['id'])}">close</a> ]
  13
+    </span>
  14
+  </li>
  15
+  % endfor
  16
+% else:
  17
+  <li>There are no open tasks</li>
  18
+% endif
  19
+  <li class="last">
  20
+    <a href="${request.route_url('new')}">Add a new task</a>
  21
+  </li>
  22
+</ul>
9  docs/pyramid_quick_tutorial/templates/new.mako
... ...
@@ -0,0 +1,9 @@
  1
+# -*- coding: utf-8 -*- 
  2
+<%inherit file="layout.mako"/>
  3
+
  4
+<h1>Add a new task</h1>
  5
+
  6
+<form action="${request.route_url('new')}" method="post">
  7
+  <input type="text" maxlength="100" name="name">
  8
+  <input type="submit" name="add" value="ADD" class="button">
  9
+</form>
7  docs/pyramid_quick_tutorial/templates/notfound.mako
... ...
@@ -0,0 +1,7 @@
  1
+# -*- coding: utf-8 -*- 
  2
+<%inherit file="layout.mako"/>
  3
+
  4
+<div id="notfound">
  5
+  <h1>404 - PAGE NOT FOUND</h1>
  6
+  The page you're looking for isn't here.
  7
+</div>

0 notes on commit 79c4e86

Please sign in to comment.
Something went wrong with that request. Please try again.