Tags

, , ,

Share it

During the last few months, I’ve been trying to grok Python. I almost instantly fell in love with the language while reading Programming Collective Intelligence some years ago, but it’s not until now that I’ve begun to immerse myself in all things Python.

I talked some about Bottle in my previous post. Bottle is simplicity and power all in the same package and it rocks for developing websites and APIs. Then I came across Cork. When you have a bottle, you must have some kind of lid on it to keep things inside. Cork provides role based authentication for Bottle and it has some useful extra features, like sending password reset emails and other things that you might expect from an authentication framework.

I also have been evaluating various databases and ORMs and wrappers. For some time, I’ve wanted to experiment with CouchDb, but the javascript query interface seemed just too cumbersome. It didn’t help that the roadmap for CouchDb got some wrinkles, and I’ve finally dropped it by the wayside. I also looked at Riak and Redis, but also at PostgresSQL and Mysql. All fine database systems, each serving their niches (or broad array of purposes). But for some reason, I have never considered MongoDb. Each time I read something about it, I blocked it out. Probably something I had once read put MongoDb in a dark corner.

Until about three weeks ago. On my current project, I need spatial indexing features and this has separated the wheat from the chaff. Postgres should handle my needs fine. I’ve used PostgresSQL once (briefly). I’m sure I could get used to it if I had to, just like one can get used to an old car. You can even love it even though many things are broken on it, but it gets you from A to B, more or less, while faster, newer and more comfortable cars pass you by. But PostgresSQL just didn’t feel good from the start. Fortunately, MongoDb reared it’s head again! It was time to have a more serious look. On a recent Tuesday, I brought my MacBook on a 14 hour train trip and went through MongoDb at 100 mph, literally speaking.

Now, three weeks later, I’m still completely in love. There is no question about what I will be using from now on to store data (given suitable requirements). MongoDb together with Bottle are two corner stones of a stack that’s a pleasure to work with.

There was just one thing that was bugging me. Cork exclusively uses JSON files on the file system to store its data. JSON is great and for a proof of concept, an MVP or any low traffic website that would work just fine. But what if my project gets popular and I get 1M users. I wouldn’t want to keep all those in memory and write gigantic json files every time someone logs in. Realising that addressing this weak point was probably in the category of premature optimisation, I felt that Cork needed to use MongoDb as a backend no matter what.

So here is where I temporarily got out of my comfort zone. There are many things I haven’t done yet with Python and I’d only used Github so far for copying learning and getting inspiration.
I forked Cork at github and wrote a MongoDb backend. I subclassed dict and I learned how to use nose testing in Python. I pushed and pulled and now my branch is ready for a pull request on my fork! This was quite an adventure for me, spending so much time polishing and testing code that will hopefully be scrutinised by many others. In a way, it has opened new perspectives for me, just like the iPhone has done for me in 2007. (the iPhone was the catalyst that helped me cut my chains with Microsoft).

I really hope that my contribution will be accepted and that it will help to make Cork more popular so that we may see additional contributions, like authorization decorators and a more flexible mailer.

Here is how to use Cork with MongoDb:

# Get a connection to the MongoDb database
# This is instead of the built-in JsonBackend
backend = MongoDbBackend(
   server = "localhost",
   port = 27017,
   database = "sample_webapp",
   initialize=False,
   users_store="users",
   roles_store="roles",
   pending_regs_store="register",
)

# Cork uses Beaker for sessions. 
# Define how we want our sessions
session_opts = {
   'session.type': 'cookie',
   'session.validate_key': True,
   'session.cookie_expires': True,
   'session.timeout': 3600 * 24, # 1 day
   'session.encrypt_key': 'please use a random key and keep it secret!',
}

# Start Cork!
# Cork will use the default Bottle app automatically
aaa = Cork(backend, email_sender='email@example.com', smtp_server='mysmtpserver')

# Hook Beaker sessions up to our default Bottle app
app = bottle.app()
app = SessionMiddleware(app, session_opts)

# run application on port 8080
bottle.run(app=app)

Peasy!

Now how would one use Cork to authenticate a user? It’s quite easy:

@bottle.post('/login')
def login():
    """Authenticate users"""
    username = post_get('username')
    password = post_get('password')
    aaa.login(username, password, success_redirect='/', fail_redirect='/login')

So when a user clicks the Login button after entering their user name and password, the form is submitted to the application (HTTP POST). Via the route ‘/login’, the login() function is called. Cork handles two possible cases. Either the credentials match: the user is logged in and redirected to the home page. Or the user entered the wrong user name or password and is redirected to the Login page.

Currently, Cork does not have decorators. This means all the logic needs to be written out inside the functions.
It would be nice if we could do this (note the @authenticated decorator):

@bottle.route('/secure_page')
@authenticated
@view('secure_page')
def secure_page(mongodb_col):
    # get some data out of MongoDb
    docs = [{'json':json.dumps(doc, default=json_util.default)} for doc in mongodb_col.find()]
    # pass the data to views/secure_page.tpl template
    return dict(docs=docs)

See how clean the function body is? No cruft, just the good stuff (and 50% comments)!
And that’s how I feel about Python and Mongo the whole time. It’s just the good stuff.

Now I wouldn’t use Cork to secure an API. That will perhaps be a topic for a future post, how to secure an API made with Bottle.