Faruk Akgul

A Python Development Story: Why web.py?

July 13, 2012 | View Comments

web.py is a Python framework which was started by Aaron Swartz and was the framework that ran reddit for years.

The major features of web.py is; simplicity, minimalism, doesn't get in your way and plays well with other libraries.

Why web.py?

If you're after:

  • simplicity
  • freedom
  • don't want to shoot yourself in the foot
  • writing clean code
  • minimalism
  • a solid web framework

then web.py is the web framework you're looking for.

A quote from web.py's mailing list that explains web.py quite well:

I don't want to be forced to abstract my database into objects and compartmentalize my code into MVC. I don't wanna have four technologies (html/css/js/php) intertwined all in one file. I don't want to compile all my code every time I change one line just to test it. Some of my objects actually have sub-classes six levels deep. Web.py empowered me to start working immediately.

Getting Started

Skeleton! Your web application needs a skeleton. I had created a sample skeleton for web.py and put it on GitHub. You could have a look at it if you wish.

Here's my web.py skeleton and what each element does.

  • doc: Here goes the project's documentation files.
  • licenses: That's where I put the licenses (GPL, MIT, BSD etc.) of the project and each library I use for the application.
  • requirements: Required 3rd party libraries for the project.
  • sh: Bash script files of the project.
  • www: The project itself.
    • app: Contains the application modules.
      • controllers: The project itself is based on MVC architecture so this module contains the handler modules of controllers package.
      • tools: Tools that is used for the project.
      • views: Template files.
      • models: Database models of the application. It's 120% chance that these are SQLAlchemy files.
      • bridge: Sometimes you need to communicate with a server that's written in another language. That's where I put all those "communication" files.
    • lib: These are libraries I develop for the project but aren't really tools. Most likely these are written in C (Python modules though). You know, sometimes you need to "put the pedal to the metal". The difference between a lib and a tools is; the modules in libs could be used in another projects whereas tools are dependent to project itself and can't be used elsewhere.
    • log: Application's log files. I log everything. EVERYTHING.
    • public: These folder contains the minimized, compiled CSS, Javascript, CoffeeScript files and images so the files in this folder are production ready and can't be used in development.
      • css: Production ready CSS files which are minimized and compiled (from LESS).
      • js: Generated Javascript files from CoffeeScript and minimized and compiled using Google Closure.
      • img: Minimized images files. These are compressed using unusual techniques.
    • static: Contains the development CSS, CoffeeScript, Javascript, and images files of the project.
      • css: LESS/CSS files for development. For this project we didn't use LESS though.
      • cs: CoffeeScript files.
      • js: Javascript files in development mode.
      • img: Images of the application.
    • test: Test files of the application. These are, as you could guess easily, automated by using Selenium and nose.
    • tmp: Garbage files basically.
    • main.py: The only files that's executed directly by the server (WSGI, FastCGI or whatever it's).
    • main_development.py: This is the main executed file in development mode.
    • settings.py: Global constants and settings of the application. This is somehow similar to Django's settings file.
    • urls.py: Contains the URLs of the application.

High Level Diagram

Here's the high level diagram of this skeleton. I think it's self explanatory. The skeleton is based on MVC (model-view-controller) architecture.


Package diagram - Architecture is based on MVC.


High Level Package Diagram


High Level Diagram

web.py features

Database

web.py has very interesting features. It comes with a db package which lets you use various of different databases. However, it's not an ORM. It's somehow similar to sqlite3 package of Python but is capable of connecting to multiple databases (This feature was missing in Django for a quite long time but it was a feature of web.py from day 1). This is a compelling reason for people who are good at SQL and don't like to use an ORM.

As I said, the main thing about web.py is simplicity. If you're doing something simple this package is very suitable. However, I'm a big fan of SQLAlchemy and the last thing I did was not really a simple application so I didn't use web.db.

The best thing about this web.py module is you could just take it and use it independently. Here, I've created a small Flask application which uses web.py's database module web.db.

from web import database
from flask import Flask, render_template, abort, flash, redirect, url_for, request

DATABASE = '/tmp/flaskr.db'
SECRET_KEY = 'what'
DEBUG = True

app = Flask(__name__)
app.config.from_object(__name__)

conn = database(dbn='sqlite', db=DATABASE)

@app.route('/')
def index():
    entries = conn.select('entries',
                          what='id, title, text',
                          limit=20,
                          order='id DESC')
    return render_template('index.html', entries=entries)

@app.route('/add', methods=['GET', 'POST'])
def add():
    if request.method == 'POST':
        conn.insert('entries',
                    title=request.form['title'],
                    text=request.form['text'])
        flash('a new entry')
        return redirect(url_for('index'))
    else:
        return render_template('add.html')

@app.route('/entry/<int:entry_id>')
def show_entry(entry_id):
    entry = conn.select('entries',
                        what='id, title, text',
                        where='id = $id',
                        vars={'id': entry_id},
                        limit=1)
    try:
        return render_template('entry.html', entry=entry[0])
    except IndexError:
        return abort(404)

def a():
    q = """
create table entries(
id integer primary key autoincrement,
title string not null,
text string not null
);
"""

if __name__ == '__main__':
    app.run(debug=DEBUG)

web.py is so flexible and has flexible modules that you could wipe out whatever you want from it and use it with another web framework. The above code is proof of that. Entire application is on GitHub.

Forms

web.py comes with a forms package which let's you create forms and validators. Ironically, it doesn't have built-in protection against CSRF. You want to create a login form? You could use forms package to create yours. Here's a simple example;

class IFormLogin(object):
  @staticmethod
  def render_login_form(token):
    login_form = Form(
      form.Textbox('username', form.Validator('Please write your username',
                                              lambda
                                                  x: USERNAME_MAX_LENGTH >
                                                     len(x)) >
                                                     USERNAME_MIN_LENGTH),
                   description='Username:',
                   id='username',
                   class_='span4'),
      form.Password('password', form.Validator('Please write your password',
                                               lambda
                                                   x: PASSWORD_MAX_LENGTH
                                                      > len(x)) >
                                                      PASSWORD_MIN_LENGTH),
                    description='Password:',
                    id='password',
                    class_='span4'),
                form.Hidden('token', id='token', value=token),
      validators=[form.Validator('Username or password is wrong',
                                 Member.exists)]
    )
    return login_form

Related Posts

  1. A Python Development Story: Part 1 Jenkins-CI meets web.py and PyCharm
  2. A Python Development Story: Part 2
Share:Tweet · reddit

blog comments powered by Disqus