 | Legacy Document - Outdated and Potentially Broken Content
This document belongs to Pylons 0.9.5 or earlier.
Please do not use this tutorial if you are new to Pylons, as version 0.9.6rc1 is now the latest stable release.
The tutorial is written for Pylons 0.9.5 (or earlier instances where appropriate). A new (0.9.6rc1) friendly version will appear shortly.
Leaving document for legacy purposes. (At least for now)
The new version is under construction at: Making a Pylons Blog
If you require assistance building a basic blog and a new document does not exist to replace this, please seek other documentation which has been marked as updated or seek assistance through the mailinglists or IRC (#pylons on freenode).
Nota Bene:
- SQLObject uses pylons.database (in depreciation cycle) and should not be used.
- Myghty is no longer the default template (as of 0.9.6rc1).
For those still wishing to use it:
- Please note there are other ORMs available for database work (SQLAlchemy or Storm).
|
| Name |
Space |
Section |
Page |
Version |
Status |
Curator |
Reviewed |
Author(s) |
| How to write a basic blog with Pylons |
Pylons CookBook |
|
How to write a basic blog with Pylons |
1.0 |
Draft |
Graham Higgins |
False |
|
How to write a basic blog with Pylons
This tutorial will demonstrate how to produce a blog with very basic functionality... nothing fancy and definitely not to be used in real life. The goal is just to demonstrate some of pylons' principles. Feel free to comment or modify parts of it, since I am just a newbie myself and there may be mistakes in here. However, I think it is important to keep everything simple to make it easier to understand for other beginners. Enough said, let's start.
The tutorial is written with the following setup in mind:
- MySQL as relational database (although there is hardly any difference when you switch to another database like SQLite for example).
- SQLObject as ORM (object relational mapper) to your SQL-database. Remember to install this as well, because it is not included in the pylons package.
- Myghty as template language
- Pylons between these two elements as the controller/middleware
1) Create a new project
This can be done easily using the paster script in pylons. Simply type:
paster create --template=pylons blogtutorial
This command will set up a directory structure and some reasonable defaults for your application, which makes it easy to simply start writing code. See Getting Started
for more information.
2) Creating the database and schema
Create a database called "blogtutorial". Then, create a table called "blog_posts" with:
1
2
3
4
5
6
7
8 | CREATE TABLE blogtutorial.blog_posts
( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
headline VARCHAR(255) NOT NULL DEFAULT '',
text TEXT NOT NULL DEFAULT '',
timestamp TIMESTAMP NOT NULL DEFAULT 0,
PRIMARY KEY(id)
)
ENGINE = InnoDB;
|
The above command varies when you use a different database than MySQL. Consult your database manual for advice on how to do this.
3) The Model: Integrating MySQL and SQLObject
a) Add the line below to your development.ini in ordet to set up the connection from SQLObject to your database:
1 | sqlobject.dburi = mysql://MyUser:MyPassword@localhost:3306/blogtutorial
|
If you have a different database like for example SQLite, this line has to be modified. Please refer to the SQLObject documentation
for the details:
b) Next, we will set up a class with SQLObject that you will use in order to "talk to the database":
The "models"-subdirectory is used for declaring SQLObject classes. Therefore, create a file called myfirstmodel.py in this directory and add the following lines to the file:
1
2
3
4
5
6
7
8
9 | from sqlobject import *
from pylons.database import PackageHub
hub = PackageHub("blogtutorial")
__connection__ = hub
class BlogPosts(SQLObject):
headline = StringCol(length=255)
text = StringCol()
timestamp = DateTimeCol()
|
Look closely: Unlike "headline", "text" and "timestamp", the attribute "id" is not defined, although it is used in the SQL-table as primary key. That is because SQLObject automatically assumes that your primary-key-column is called "id", so you do not have to define it in your class.
Furthermore, the fact that our class is named "BlogPosts" is no coincidence either. Actually, you have to choose this name in order to make it work here, because SQLObject automatically transforms the word "BlogPosts" to lowercase when addressing the database. "BlogPosts" thus will be transformed to "blog_posts", which is exactly the name of our SQL-table. Perfect.
By the way: It is also possible to extract the table data from the database automatically, so you don't have to declare each "BlogPosts"-attribute like "headline" or "text" manually. For the sake of better understandability, I chose the explicit declaration for this tutorial.
In order to actually be able to use our class, we have to import it in the file _init.py in the subdirectory "models". That is because the init_.py is parsed each time that you start your application. So, add the following line at the end of this file:
from myfirstmodel import BlogPosts
What do you get as a result: Now, whenever you want to access the database in your project, you can simply do it like this:
followed by the according SQLObject commands. For example:
will access and read the row with primary key value 7.
The nice thing is: You do not need to write SQL queries anymore, because SQLObject automatically translates calls to your "BlogPosts" (or according object instances) to SQL queries. So, for you it is simple class and object usage... SQLObject does the rest. (There is also another object relational mapper called SQLAlchemy
which offers the same functionality and which is recommended by lots of people as well.)
4) The view: Creating templates in Myghty
You have to create some templates that will define what your blog will look like when accessed by a browser. Therefore, we use the template language called Myghty, which allows you to write standard HTML-code and then you can mix in the variable parts in a Python-like syntax. The best folder to place these template files is the "template" folder in your project tree. Let's try to have a clean structure: So, do not simply dump all your template files in the "template"-directory but create sub-directories for your different parts. Now, we have only one part: a blog. So, create a sub-folder called "blog".
a) First, we need a "list"-view, that will show an overview of all written blog entries.
Create a file "list.myt" (.myt represents Myghty-templates) in "template/blog/" with the content below:
1
2
3
4
5
6
7
8
9
10
11 | % for post in c.blogPosts:
<p>
<h2> <% post.headline %> </h2>
<% post.text %> <br/>
<% h.link_to('Show', url=h.url(action='detail', id=post.id)) %>
<% h.link_to('Edit', url=h.url(action='edit',id=post.id)) %>
<% h.link_to('Destroy', url=h.url(action='destroy',id=post.id), confirm='Are you sure?') %>
</p>
% #endfor
<% h.link_to('New blog post', url=h.url(action='new')) %>
|
Some remarks:
- Please see the Myghty-documentation
for details on the template syntax. For now, it should be enough to realize that we used 2 different ways to implement Python in the template:
- complete lines beginning with a %
- Python mix-in statements (in between HTML) using a <% Python-code %> syntax.
- All our blog posts will be taken from the list called "c.blogPosts". Later, you will see how to assign content to this variable.
- Everything between the "for"-statement at the beginning and the "endfor"-comment close to the end will be repeated for every blog post. The "endfor" is not really necessary: a blank line beginning with a %-sign would have been enough to end the loop, but I think "#endfor" improves readability.
b) We also need a template that will show us the details of a single blog post. Let's call this "detail.myt" and place it in the "template/blog/"-directory as well. The content could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <p>
<b>id:</b>
<% c.blogPost.id %> <br/>
<b>Headline:</b>
<% c.blogPost.headline %><br/>
<b>Text:</b>
<% c.blogPost.text %> <br/>
<b>Creation time:</b>
<% c.blogPost.timestamp %>
</p>
<% h.link_to('Edit', url=h.url(action='edit', id=c.blogPost.id)) %> |
<% h.link_to('Back', url=h.url(action='list', id=None)) %> |
<% h.link_to('Destroy', url=h.url(action='destroy', id=c.blogPost.id), confirm='Are you sure?') %>
|
As you perhaps have noticed in list.myt already, it is not necessary to write links and URLs manually. There is a module called "Webhelpers" which can be accessed in your template via the "h"-class. The 'Edit'-link above, for example, will point to an URL that corresponds to
- the action='edit' and
- a special id-value: c.blogPost.id
Don't worry...the meaning of actions and ids will be explained below.
c) What is a blog without the possibility to create new posts? So, here is the template for new posts which I suggest you call "new.myt" in your "template/blog/" directory.
1
2
3
4
5
6
7
8
9
10
11 | <h1>Write new blog post</h1>
<% h.start_form(h.url(action='create')) %>
Headline: <br/>
<% h.text_field('headline') %> <br/>
Text: <br/>
<% h.text_area('text', '', size="30x10") %> <br/>
<% h.submit(value='Create') %>
<% h.end_form() %>
<% h.link_to('Back', h.url(action='list', id=None)) %>
|
d) It would also be nice to be able to edit files. So, create a file "edit.myt" (in /template/blog/ again):
1
2
3
4
5
6
7
8
9
10
11 | <h1>Update blog post</h1>
<% h.start_form(h.url(action='update', id=c.blogPost.id)) %>
Headline: <br/>
<% h.text_field('headline', value=c.blogPost.headline) %> <br/>
Text: <br/>
<% h.text_area('text', c.blogPost.text, size="30x10") %> <br/>
<% h.submit(value='Update') %>
<% h.end_form() %>
<% h.link_to('Back', h.url(action='list', id=None)) %>
|
5) The controller: the "core" of your blog
By now, you have specified a model using SQLObject and a view using Myghty but there is one thing missing: The controller in between which ties the rest of your work together. The controller contains the functions that will be executed when somebody clicks on a link in your blog. These functions are called "actions" and for each blog entry, there is an "id"-parameter, that will be passed to this function (since we specified it as parameter in the template). The nice thing is: you do not have to worry about mapping links/URLs to actions and ids, because there is a package in pylons called "Routes" which does all the work for you. We can simply create a controller called "blog.py" with the following command:
cd blogtutorial
paster controller blog
This will create the file in your "controllers/"-subdirectory and include a basic layout in it as well. Modify the content of this file according to the lines below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 | from blogtutorial.lib.base import *
import datetime
class BlogController(BaseController):
template_prefix = '/blog'
def index(self):
redirect_to(action='list')
def list(self):
c.blogPosts = list(model.BlogPosts.select())
return render_response(self.template_prefix + '/list.myt')
def new(self):
return render_response(self.template_prefix + '/new.myt')
def create(self):
objectDict = {}
objectDict['headline'] = request.params['headline']
objectDict['text'] = request.params['text']
objectDict['timestamp'] = datetime.datetime.now()
model.BlogPosts(**objectDict)
redirect_to(action='list')
def detail(self, id):
c.blogPost = model.BlogPosts.get(id)
return render_response(self.template_prefix + '/detail.myt')
def edit(self, id):
c.blogPost = model.BlogPosts.get(id)
return render_response(self.template_prefix + '/edit.myt')
def update(self, id):
blogPost = model.BlogPosts.get(id)
objectDict = {}
objectDict['headline'] = request.params['headline']
objectDict['text'] = request.params['text']
blogPost.set(**objectDict)
redirect_to(action='list')
def destroy(self, id):
model.BlogPosts.get(id).destroySelf()
redirect_to(action='list')
|
As you can see, the names of the action values in your template links are the same names as the functions in the controller above. When your blog is running, the following Myghty template line will be transformed in to a link for the browser:
1 | <% h.link_to('Back', url=h.url(action='list', id=None)) %>
|
Clicking on this link will result in the execution of the function/action called "list" in the controller above, without any id passed to the function (since we want to list all posts and not just a single one with a special id).
Let's take a closer look at the "list"-action:
1
2
3 | def list(self):
c.blogPosts = list(model.BlogPosts.select())
return render_response(self.template_prefix + '/list.myt')
|
In the first line, we access our BlogPosts model-class that we created in the beginning. The select() command means that we want to select all entries in the database and list causes the execution of the select()-command and returns a list, which we assign to c.blogPosts.
The second line returns a Response object, with the content of the Response being the template-file "list.myt" rendered via the Myghty interpreter. The rendered template (which will be plain HTML-code) returned via that Response object is what the browser gets to see. All the variables/values that we want to have availabe in the template file have to be assigned to the "c"-object (as it is done in the first line). Since we have assigned c.blogPosts in the controller, we can evaluate it in our "list"-template. Each time a new action is called, the c-object is reset, so you don't have to worry about previous values.
But, perhaps you are wondering: How do I access form-data that was POSTed, for example the headline and the text of a new blog post. Look at the "create"-action:
1
2
3
4 | objectDict['headline'] = request.params['headline']
objectDict['text'] = request.params['text']
objectDict['timestamp'] = datetime.datetime.now()
model.BlogPosts(**objectDict)
|
Text or data that was posted in forms will be available through "request.params", which is accessed like a dictionary. In a real application, you shouldn't simply take the results as I did above but validate the contents first (Is it empty or not? Is the headline short enough? ...). Then, you can pass the resulting dictionary with key-value pairs like ['headline']="My first headline" and ['text']="bla bla" and so on to model.BlogPosts(**dictionary). Doing this will store these values as a new row in your SQL-table. See? No SQL-syntax required; a simple call to model.BlogPosts() was enough.
So, two easy concepts to remember:
- passing variables/values to templates: assign them to the c-object
- getting posted content/values from forms: read it from the request.params dictionary
6) Start it, Try it
Pylons comes included with a little server-script as well, that you can use to try your blog. Go to the top directory of your project (where the file development.ini is as well) and type:
paster serve --reload development.ini
Now, simply point your browser to:
http://localhost:5000/blog
"Routes" maps this to the controller-file called blog.py (the one you created) and (by default) tries to call the action "index", which is redirected to "list". Now, you can see the the blog in action. Wheeyy... you're finished!
I hope that I was able to help you understand some basic principles of pylons... if you have any questions, feel free to ask them at:
http://groups.google.com/group/pylons-discuss
Cheers, Martin