From bit to a heart beat

insane innovations from a sane insanity

Sun Jan 17

Activerecord in Python: Do it like Rails!

I’ve searched around the net for a nice, clean implementation of activerecord in Python but I found none. There are many better libraries for database abstraction out there in Python but I think that Rails’ “activerecord” way is a breeze and a fun way to access a database.

So, my weekend project was to see whether I could hack together a small activerecord for Python.

The first thing I reeeeaally wanted to do, is to create dynamic queries. What dynamic queries are you ask.

Let’s say you have a database and a table with some fruits fields.

Table: People

Fields: id, name, sex

Using ActiveRecord you can do this:

user = People.find_by_name(“Robert”)

and you’ll get all the People with the name Robert. The magic thing is that you haven’t defined anywhere the function find_by_name. Activerecord has only the function find_by and depending on your search it creates automatically a new function called find_by_name (if the ‘name’ field exists on your table). This is all thanks to Ruby’s method_missing function.

Ruby’s method_missing is called automatically from ruby’s VM everytime a function you tried to call was not found. So, when you called find_by_name, ruby couldn’t find the function and after all the search it went to method_missing. From there, activerecord took control and done it’s magic.

That are many different ways to do it in Python but only one comes close to Rails’ implementation.

Behold: __getattr__

Everytime you call a class attribute, this function is called. So, in Python:

user = User.find_by_name(“Robert”)

calls __getatr__ and if the attribute does not exist, it throws an ‘AttributeError’ exception. That’s the place we are going to put our code and create the dynamic queries.

The most difficult part was this: The function __getattr__ gets one parameter (the attribute) so in this example it was the find_by_name. I had to find a way to pass to __getattr__ the query’s argument.

Behold: Lambda

I know a hobby project of mine is awesome when I have to use lambda (old habits from C and Lisp). Using

lambda value: do_smth(value),

you open up the way for another parameter to join the game (hint: value). So, we have this:

dynamic_query = {

"find_by_": lambda value: self._find(field, value)

}[query]

This is a dictionary emulating a switch/case in Python with lambdas. If you want to add more functions (insert,update) or more logic (find_by_name_and_sex), this is the place to hack.

Check it online at my bitbucket account and push your changes.

Comments (View)
blog comments powered by Disqus