TurboGears and AJAX

Many beginner web developers ask questions like “how do i do something without refreshing page?” or “how can i load another page in my page?” etc. The answer is AJAX. AJAX stands for “Asynchronous JavaScript and XML”. With AJAX, you can retrieve or send data to the server in the background using an “XMLHttpRequest.”

Troll Cat

Before going further, i would like to take your time and advise you to learn JavaScript. If you are a web developer, no matter if you are back-end or front-end one, at some point in your career you will have to use JavaScript. In order to be ready for those times, go and learn JavasSript. There are awesome resources.

Do not be one of those people who think learning jQuery is enough and how you don’t need to know JavaScript. jQuery is a JS framework. It’s a great one and that’s all. Learn JavaScript.

There are many techniques to use AJAX in a web application. In these examples we are going to use jQuery framework and it’s powerful “$.get” and “$.post” methods.

Open up your terminal, cd into your TurboGears environment, activate it, upgrade it to the most recent version which is 2.1.5 with:

easy_install -U -i http://tg.gy/current tg.devtools

Go to your controllers/root.py and change the index action like this:

@expose('ideas.templates.index')
def index(self):
    """Handle the front-page."""
    projects = DBSession.query(Project).all()
    return dict(page='index', projects=projects)

And this is our new “templates/index.mak”:

<%inherit file="local:templates.master"/>

<%def name="title()">
  Welcome to IDEAS.
</%def>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
    $("a.project_link").click(function(){
        var context = this;
        $.get(this.href, function(response){
            $(context).parent().next().html(response);
        });
        return false;
    });
});
</script>

<div style="margin-bottom: 50px;">
    <h1>Projects</h1>
    % for project in projects:
        <div class="project">
            <div style="margin-bottom: 5px;"><a class="project_link" href="${url('/show_tasks/%s' % project.id)}">${project.project_name}</a></div>
        <div class="tasks"></div>
    % endfor
</div>

<%def name="sidebar_bottom()"></%def>

In our “show_tasks.mak” template:

  • We are listing the projects in a for loop.
  • We create a “div” element with the class “tasks” to use in our JavaScript.
  • We include jQuery framework via Google CDN.

So whenever we click to a project’s link, its tasks will be listed under the “tasks” div.

Let me walk you through the simple JavaScript codes we wrote.

In the first line, we tell jQuery “when the DOM is ready”, use all these child scripts of yours. So it attaches the event listener, changes css classes, anything we want to do.

In the second line, we tell, whenever an “a” element with a “project_link” class is clicked, create an AJAX request and append the result to the closest div with the class “tasks” to the “a” element’s parent.

In JavaScript, whenever a new scope is opened like the second line, the keyword “this” changes meaning. In line 3 of our script, it references to the clicked “a” element however in the line 5 it references the response object. This is why I have created the “context” variable and mapped it to “this” which is the clicked “a” element. This way, I am able to use the clicked a element.

We are missing two things here. The corresponding TurboGears action and it’s template. So let’s create them. In your RootController, add the following action:

@expose('ideas.templates.show_tasks')
def show_tasks(self, project_id):
    tasks = DBSession.query(Task).filter_by(project_id=project_id).all()
    return dict(tasks=tasks)

And the following goes by “show_tasks.mak” under the “templates” directory.

% if tasks:
    % for task in tasks:
        <div>${task.task_name}</div>
    % endfor
% else:
    <div>This project does not have any tasks, yet.</div>
% endif

That’s all. Run your server and open it up in your browser. Let’s see how it goes.

In the next post, I will show you how to use the $.post method and deal with JSON responses from the server when using AJAX. I hope you guys enjoyed all these. Please do not hesitate to ask any question. You are more than welcome.

Posted in AJAX, JavaScript, jQuery, TurboGears | Tagged , , , , , | Leave a comment

Querying The Models

If you remember the post that we created the models in, you will also remember that TurboGears uses SQLAlchemy for the model part of MVC.

Now, we are going to do some practice on querying the models. In the meanwhile, you will discover the TurboGears console. Let’s get into our project’s home dir and start the console.

cd ~/projects/tg2env/ideas
source ../bin/activate
paster shell development.ini

Let’s import our models and the DBSession object from ideas/model. At first, we are going to create some projects and tasks. When using queries, you will see which queries are running.

from ideas.model import DBSession, Project, Task
p1 = Project(project_name="TurboGears Shell Project")
p2 = Project(project_name="TurboGears Blog Project")
DBSession.add(p1)
DBSession.add(p2)
DBSession.flush()
t1 = Task(project_id=p1.id, task_name="Extend the shell", status="open")
t2 = Task(project_id=p2.id, task_name="Create the models", status="closed")
t3 = Task(project_id=p2.id, task_name="Use Twitter Bootstrap in the templates", status="open")
DBSession.add(t1)
DBSession.add(t2)
DBSession.add(t3)
DBSession.flush()

We have created two projects, the first project has 1 open task, second project has 2 tasks; 1 open and 1 closed. We are going to start with the basics.

As the first step, let’s query all projects.

DBSession.query(Project).all()
# output:
00:30:09,385 INFO  [sqlalchemy.engine.base.Engine] SELECT projects.id AS projects_id, projects.project_name AS projects_project_name, projects.project_description AS projects_project_description 
FROM projects
00:30:09,385 INFO  [sqlalchemy.engine.base.Engine] ()
[<ideas.model.project.Project object at 0x4161bd0>, <ideas.model.project.Project object at 0x40982d0>, <ideas.model.project.Project object at 0x40a42d0>]

I had 3 projects in my “ideas” app so it returned all 3.

Let’s order the projects by their name in the ascending order.

projects = DBSession.query(Project).order_by(Project.project_name.asc()).all()

>>> for p in projects: print p.project_name
... 
TurboGears Blog Project
TurboGears Project Management
TurboGears Shell Project

Now we are going to list all open tasks of “TurboGears Blog Project”.

DBSession.query(Task).filter(Task.project_id == p2.id).filter(Task.status == "open").all()
>>> for task in tasks: print task.task_name
... 
Use Twitter Bootstrap in the template

SQLAlchemy has an expression called “and_” so we can write the above query like this:

>>> from sqlalchemy.sql.expression import and_
>>> DBSession.query(Task).filter(and_(Task.project_id == p2.id, Task.status == "open")).all()
>>> for task in tasks: print task.task_name
... 
Use Twitter Bootstrap in the templates

And we are going to update the “Use Twitter Bootstrap in the template” task and set its status to “closed”.

DBSession.query(Task).filter(Task.id == t3.id).update({"status": "closed"})
00:45:53,126 INFO  [sqlalchemy.engine.base.Engine] UPDATE tasks SET status=? WHERE tasks.id = ?
00:45:53,126 INFO  [sqlalchemy.engine.base.Engine] ('closed', 4)
>>> t3.status
'closed'

And let's delete all the tasks of "TurboGears Shell Project".

DBSession.query(Task).filter_by(project_id=t1.id).delete()
00:59:01,585 INFO  [sqlalchemy.engine.base.Engine] DELETE FROM tasks WHERE tasks.project_id = ?
00:59:01,585 INFO  [sqlalchemy.engine.base.Engine] (2,)

For more on SQLAlchemy, please go read on http://docs.sqlalchemy.org/en/latest/orm/query.html and you can do the Object Relation Tutorial.

Posted in Python, SQLAlchemy, TurboGears | Tagged , , , , , | Leave a comment

A Simple Project Management Tool With TurboGears – Templates

We have created our models, created our controller actions and forms. Now we are going to set up our templates. Let’s take a look at the controller action “new_project”:


@expose('ideas.templates.new_project')
def new_project(self):
    """ Displays the project form. """
    return dict(project_form=project_form)

In this action, we tell TurboGears that when new_project action is called, we are going to render it with “new_project.mak” file in the “templates” folder. We also pass “project_form” variable to the template.

Let’s create our templates. This is the content of new_project.mak:

<!-- ideas/templates/new_project.mak -->
<%inherit file="local:templates.master"/>

<%def name="title()">Project Form</%def>

${project_form()|n}

This is the content of new_task.mak:

<!-- ideas/templates/new_task.mak -->
<%inherit file="local:templates.master"/>

<%def name="title()">Task Form</%def>

${task_form()|n}

And this is the content of show_project.mak:

<!-- ideas/templates/show_project.mak -->
<%inherit file="local:templates.master"/>

<%def name="title()">${project.project_name}</%def>

<h1>${project.project_name}</h1>
<div>${project.project_description}</div>

<h2>Tasks</h2>
<ul>
    % for task in tasks:
        <li>${task.task_name}</li>
    % endfor 
</ul>

Now start your web server and visit “http://127.0.0.1:8080/new_project” and see how the project creation form looks like:

Project Creation Form

And let’s head to task creation page at “http://127.0.0.1:8080/new_task“:

Task Creation Form

Go ahead and play with it. Do something yourself. For more on the templates you can visit mako documentation.

Posted in Mako, Python, Templates, TurboGears | Tagged , , , , , , | 1 Comment

A Simple Project Management Tool With TurboGears – Setting Up the Forms

In the previous post, we have created our Project and Task models. Now we have to create our forms.

It is very easy to create forms with TurboGears. In this example we are going to use ToscaWidgets 1 however keep in mind that in the next major TurboGears release, TurboGears will switch to ToscaWidgets 2.

HTML forms consist of a form element and its child elements called input. In these examples, we are going to create forms in Pytho thon with the help of ToscaWidgets library and render these forms in our templates.

This is a simple example of creating a form.


# ideas/ideas/forms/project_form.py
# -*- coding: utf-8 -*-
"""Project form module"""

from tw.api import WidgetsList
import tw.forms as forms
from ideas.model.project import Project

class ProjectForm(forms.TableForm):
    class fields(WidgetsList):
        project_name = forms.TextField(validator=forms.validators.NotEmpty)
        project_description = forms.TextArea(validator=forms.validators.NotEmpty)

class TaskForm(forms.TableForm):
    class fields(WidgetsList):
        project_id = forms.SingleSelectField(options=Project.get_projects_for_dropdown())
        task_name = forms.TextField(validator=forms.validators.NotEmpty)
        task_description = forms.TextArea(validator=forms.validators.NotEmpty)
        status = forms.RadioButtonList(options=(('open', 'Open'), ('closed', 'Closed')))

project_form = ProjectForm(action="create_project")
task_form = TaskForm(action="create_task")

1) We have created two classes called ProjectForm and TaskForm inheriting ToscaWidgets’ TableForm class. When we call the project_form and task_form in our templates, it will render the form elements in a table layout.

2) In the ProjectForm, we have a text field (<input type="text">) and a textarea (<textarea>) field.

3) In the TaskForm, we have a dropdown box with the options of all our Projects, a text input for the task name and a textarea field for the task description.

Let’s go to our controllers/root.py file. Let’s add these 4 actions there.


# these lines go before RootController class definition.
from ideas.model.project import Project, Task
from ideas.forms.project_form import project_form, task_form

# these lines go in the RootController class.
@expose('ideas.templates.new_project')
def new_project(self):
    """ Displays the project form. """
    return dict(project_form=project_form)

@expose('ideas.templates.new_task')
def new_task(self):
    """ Displays the task form. """
    return dict(task_form=task_form)

@expose()
def create_project(self, project_name, project_description):
    """
    Creates a project with the given name and description. 
    Redirects to the project itself.
    """
    project = Project(project_name=project_name, project_description=project_description)
    DBSession.add(project)
    DBSession.flush()
    return redirect("/show_project/%s" % project.id)

@expose()
def create_task(self, project_id, task_name, task_description, status):
    """
    Creates a task for the given project with the given information.
    Redirects back to the project.
    """
    task = Task(project_id=project_id, task_name=task_name, task_description=task_description, status=status)
    DBSession.add(task)
    DBSession.flush()
    return redirect("/show_project/%s" % project_id)

@expose('ideas.templates.show_project')
def show_project(self, project_id):
    """
    Shows the project with the given id and lists its tasks as well.
    Redirects to the new project page if the project does not exist. 
    """
    project = DBSession.query(Project).filter_by(id=project_id).first()
    if not project:
        return redirect("/new_project")
    tasks = DBSession.query(Task).filter_by(project_id=project_id).all()
    return dict(project=project, tasks=tasks)

Now you can create a project at the “http://127.0.0.1:8080/new_project” address. Actually, you can’t create a project yet. Because we haven’t set up the templates. Let’s set up the templates.

Posted in Python, ToscaWidgets, TurboGears | Tagged , , , , | Leave a comment

A Simple Project Management Tool With TurboGears – Setting Up the Models

I want you to love TurboGears, understand how to use TurboGears and how to become fully productive with it. For example, I can develop a fully fledged application that would take 3 weeks with something else in 1-1.5 weeks with TurboGears. For that, I think a project management tool fits best here. It requires working with a database, creating forms, authentication and authorization and so on.

Now, go ahead and create a project called “ideas”. What we are going to do is a simple, a very simple project management tool. We are going to create a Project model, a Task model.

cd tg2env
source bin/activate
paster quickstart -m ideas
# answer yes to authentication/authorization question.

We will “cd” into the “ideas/ideas” directory and create a forms module and create a project_form.py file while in there.


cd ideas/ideas
mkdir forms
touch forms/__init__.py
touch forms/project_form.py

Next, we are going to create a “project” module in ideas/model/ folder.

cd model && touch project.py
# project.py
# -*- coding: utf-8 -*-
"""Post model module."""

from ideas.model import DBSession, DeclarativeBase
from sqlalchemy import Column, ForeignKey
from sqlalchemy.types import Integer, Unicode, UnicodeText

class Project(DeclarativeBase):
    __tablename__ = 'projects'

    id = Column(Integer, primary_key=True)
    project_name = Column(Unicode(255))
    project_description = Column(UnicodeText)

    @classmethod
    def get_projects_for_dropdown(self):
        projects = DBSession.query(Project).all()
        project_list = [(project.id, project.project_name) for project in projects]
        return project_list

class Task(DeclarativeBase):
    __tablename__ = 'tasks'

    id = Column(Integer, primary_key=True)
    project_id = Column(ForeignKey("projects.id"))
    task_name = Column(Unicode(255))
    task_description = Column(UnicodeText)
    status = Column(Unicode(6))

What have we done?
We have created two models, 1) Project and 2) Task. We have imported the necessary modules from both our project and SQLAlchemy in order to be able to setup our models correctly. The very next step is to create the tables we have declared in the models. Go and open ideas/model/__init__.py file. Go to the end of file. (Here is a “vim” trick for you. When you press ESC and then type “:$”, you will go to the end of file.) Add the following line there:

from ideas.model.project import Project, Task

Run the following commands:


# in the top level ideas/ project directory
python setup.py develop
paster setup-app development.ini

“paster setup-app development.ini” will create all our tables, including the authentication tables. It will use the database backend defined in the development.ini file, line: 52. By default, it is SQLite.

In the next post, we will create the forms. Until then, take care and check out ToscaWidgets.

Posted in Python, SQLAlchemy, TurboGears | Tagged , , , , , , | 2 Comments

First Steps With TurboGears

Now that you have learnt what is TurboGears and how to install it, you want to get your hands dirty with it. This is really nice. I’m glad you’ve decided to do that. You won’t regret this decision. TurboGears is like that guitar you have always wanted to buy. Now that you have it, you can start to rock.

Let’s go into our virtual environment and quickstart a project.


cd ~/projects/tg2env
source bin/activate
paster quickstart -x -n -m example
cd example
python setup.py develop
paster serve development.ini

What have we done:

  • Went into our virtual environment and activated it.
  • Created a project called “example” with no sqlalchemy, no authentication and authorization and with mako templates.
  • We have initiated the project and ran the server.

Let’s see how “http://127.0.0.1:8080/&#8221; looks like.

Now go ahead and play along with it. Discover TurboGears. You can ask anything via the comments.

Posted in Python, TurboGears | Tagged , | 3 Comments

Let’s Install TurboGears

In order to install TurboGears we need Python setuptools and in order to create an isolated environment we need virtualenv package.  Why an isolated environment? With a virtual environment we don’t need to think about our packages clashing.

Install requirements:

 sudo apt-get install build-essential python-dev python-setuptools python-virtualenv 

Those installation requirements are not specific to TurboGears. If you are working with Python anyway and tried to use any Python package management that means you already have these packages.

The standard installation

$ virtualenv --no-site-packages tg2env 
$ cd tg2env/ 
$ source bin/activate 
(tg2env)$ easy_install -i http://tg.gy/current/index/ tg.devtools 
(tg2env)$ deactivate 

You may get errors depending on your system and your system’s requirements. If you have not seen any errors, congratulations, you have installed TurboGears 2.1.4 (the current stable release as of this writing) and its dependencies successfully.

The pip way

You have tried to install TurboGears with the standard way however you are curious. How to install with pip as well? Fear not, young TGer. I will feed your hunger.

pip install -i http://tg.gy/current tg.devtools

Let’s create an example TurboGears project.

Posted in Python, TurboGears | Tagged , , , , , , , | 13 Comments