February 13, 2008 will

Writing a Facebook Application with Python Pt. II

For the first part series of post, click here for Part I.

Writing a Facebook application is in essence the same as writing any web application, only with an additional step where the output from your web app is processed and inserted in to a Facebook page. Although PHP seems the most popular choice (and is what Facebook itself is written in) you can use any of the Python frameworks to write an app. You could even roll your own if you were so inclined. I used Turbogears, but you could easily adapt this to another Framework.

Serving FBML

You can generate FBML with any Python templating system. I used Genshi which was quite happy to generate FBML for me as long as it was instructed to produce xml and not html, which you can do by adding format="xml" to the expose decorator. For example, here is the controller method for my stats page.

@expose(template="genshi:microbes.templates.stats", format="xml")
    def stats(self, *args, **kwargs):

        num_users = User.select().count()
        num_added_users = User.select(User.q.added==True).count()
        num_infections = Infection.select().count()

        return dict(num_users=num_users,
                    num_added_users=num_added_users,
                    num_infections=num_infections)

To see what that renders, take a look at the source of http://microbes.willmcgugan.com, which is naked FBML (not processed by Facebook).

Adding and Removing Users

The first page that the user requests probably wont be FBML at all. When the user adds your application, then Facebook will redirect to a URL defined in your app settings. If your app needs to store users then this is where you would add the users id to your database. You could then display a welcome page, or simply redirect to one of the pages in your application. Similarly, when a user removes your application the Facebook server requests another. This 'user removed' URL is never actually seen by the user so you don't need to return any meaningful content for it.

Using the Facebook API

For other pages within your application you need to do a little work to retrieve parameters from the page request that allow you to work with the API.

There are two Python modules to help with writing Facebook apps (that I know of). PyFacebook is a complete wrapper for the FB API and may be the obvious choice for writing a new application. However, the documentation was Django centric and I originally wanted to integrate an existing Turbogears application. So I decided to use minifb, which provides the necessary boiler-plate code for making calls to the Facebook API. If I write another application I would probably use PyFacebook to save a little time, but it turns out that doing it the hard way isn't that hard at all.

Before you use any parameters from a Facebook request, they need to be validated with a call to minifb.validate which takes your secret key (supplied by Facebook when you create an application) and a dictionary containing the request parameters. In Turbogears you would simply pass the keyword arguments from the controller method to minifb.validate. When a user has added your app, every call has at least two parameters supplied by the Facebook server; 'user' is the user id of the current user and 'session_key' is a string that identifies the current session which is needed for API calls. In the spirit of removing -- even minimal -- boilerplate I wrote a decorator to add to controller methods which does the validate step and optionally redirects to page if the user is not logged in.

_login_url = "http://apps.facebook.com/virtualmicrobes/"

def expose_fb(no_redirect=False):

    def decorate(f):

        def func(*args,  **kwargs):
            try:
                arguments = minifb.validate(FACEBOOK_SECRET_KEY, kwargs)
            except Exception, e:
                return str(e)

            if not no_redirect and "session_key" not in arguments:
                return '<fb :redirect url="%s" />'%_login_url

            ret = f(*args, **arguments)

            return ret

        func.__dict__.update(f.__dict__)
        return func

    return decorate

To use this decorator, just add it before Turbogears @expose decorator, which will make the controller appear just like any other request.

The minifb.call function can be used to make calls to the Facebook API. It takes the name of the API method you want to call, followed by your API key, secret key, session key and any additional parameters required by the method. It sends a POST request to the Facebook server and parses the returned JSON. The return value from minifb.call is a collection of basic Python data types, so it is very easy to work with. Generally API calls will return a list of integers / strings, or possibly a list of dictionaries.

Facebook Gotchas

The Facebook server imposes a timeout restriction on pages within your application. From my experiments this seems to be about 7 seconds, if the Facebook server doesn't receive a response from your web app within that time limit it will display 'page not found', or words to that affect. If your server is under strain and you are also making a lot of api calls then it is quite possible to go over the timeout. I initially had this problem, but it turned out to be due to a faulty DNS server which webfaction.com quickly fixed for me. So you may not experience this issue, but if you do I would suggest reducing the number of API calls you make if possible, and if you don't care about the return value of your API calls it would be worthwhile running them in a separate thread. I wrote the following helper function to do just that.

def fire_and_forget(callable, *args, **kwargs):

    def do_callable():
        try:
            callable(*args, **kwargs)
        except:
            pass

    thread = Thread(target=do_callable)
    thread.start()

If you wrap an API call with fire_and_forget it will run asynchronously and reduce the time to handle the request.

Cliffhanger

Hopefully I have convinced you that technology wise, writing a Facebook application is fairly straight-forward. Unfortunately there are other hardships which you will have to face when writing your app. I'll go over them in Part III.

Use Markdown for formatting
*Italic* **Bold** `inline code` Links to [Google](http://www.google.com) > This is a quote > ```python import this ```
your comment will be previewed here
gravatar
Pete

I posted a link to this article from the minifb google group.

http://groups.google.com/group/minifb/browse_thread/thread/2b7e0204eb5fe76b

gravatar
Guri

hey thanks for the nice article!!

gravatar
Mickey Hadick

I'm struggling getting my Kid template to play nice with fbml. Could you share a bit of your old Genshi, a skeleton is all I need, I guess I'm not so handy with Kid that I understand what it's complaining about. I think I really don't get what the xml parser is doing.

I'm trying to put together an example of TurboGears and Facebook that I can share.

Thanks.

gravatar
Stone

Nice work, thanks a lot!

to Mickey: I think you can easily come over it by modifying the kid template source (init.py & serialization.py), e.g. adding a new format like %fbml%.

gravatar
Mickey Hadick

To Stone and Will:

I did overcome my problems with Kid. Thanks, Will, for the examples. I'll be wrapping it up into something useful as a tutorial, and will put the code on GoogleCode to share.

The trick was using output='xml' in the expose decorator, and getting the name space correct in the kid file. I don't understand xml namespaces, but somehow I got the correct combinations.

gravatar
Jims
how can you help me a young programmer develop into a good one like you. python is the first programming language am learning and am keen on working on it for as long as possible. so were i do start from? you help would be much appreciated. were can i get you tutorials?
gravatar
viresh
hi Will McGugan,
i wqs going through document in that i found http://microbes.willmcgugan.com address but as i click on browser says web page not found.
gravatar
Long
Oops! Google Chrome could not find microbes.willmcgugan.com
gravatar
Will McGugan
The virtual microbes project was abandoned a while ago. Just wasn't worth keeping it running. Great learning experience though…