Posts Tagged ‘pylons’

Facebook apps in Python and Pylons part 2

Saturday, November 22nd, 2008

This article is a followup to my previous post, Facebook apps in Python and Pylons part 1. I’m going to talk a little more about what is interesting about Facebook apps and how they work in practice. At the end, I provide a little code sample and a convenience decorator to save you some hassle.

Why write a Facebook app?

facebook logo

Even if you are pretty familiar with using Facebook, you would be easily forgiven if you didn’t fully understand what the capabilities of a Facebook application are, and how the flow works. Facebook applications essentially offer you:

  • The ability to put your own content on a user’s profile.
  • The ability to update a user’s news feed.

While there are a bunch more things you can do with your Facebook application - as described on the official “Anatomy of a Facebook application” page on developers.facebook.com, those two things are likely the most interesting to you.

How do I add content to the user’s profile and to their news feed?

This is the next question! Obviously, the basic answer is “by writing a Facebook app, stupid!”. Of course, you’re looking for a little bit more than just that. The first step is for the user to add your application. I’m about to drop a whole load of Facebook API jargon - specialised terms are highlighted in bold - the user can do this by visiting your canvas URL. You generate the canvas page from your callback URL, and include some FBML to give it an add to profile button. Once the person adds your application, Facebook will redirect them to your post add URL. From the post add hook, you can use the Facebook API to call setFBML to add content to their profile page, and publishUserAction to add stuff to their feed.

Its pretty trivial - but there is an additional caveat. Before you can do anything useful in a Facebook app, you must have a valid Facebook session. Basically, you want most of your entry points to only be loaded if a user has a logged in session, and if they don’t, you want them to be redirected to the login page. This ends up being a fair bit of boiler-plate code. I have written this method decorator to normalise the boiler-plate code into a single place, such that your Pylons controller methods will be handed a valid PyFacebook API object (from the PyFacebook library - see part 1). Here is an extremely basic code skeleton for a Facebook app in Pylons using my decocorator and PyFacebook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def require_login(f):
    ''' This decorator first checks to see
       if the user is authenticated.
       If not, it redirects them
       in the appropriate fashion to the
       log in page.  If they are authenticated,
       it sets up the PyFacebook Facebook
       object and passes it down to our wrapped method. '''
    def redirect(fb, url):
        if fb.in_canvas:
            log.info("doing fbml redirect 302")
            return '<fb:redirect url="%s" />' %(url, )
        else:
            log.info("sending a 302")
            response.status_int = 302
            response.headers['location'] = url
            return 'Moved temporarily'
    api_key = config['pyfacebook.apikey']
    secret_key = config['pyfacebook.secret']
    appid = config['pyfacebook.appid']
    auth_token = request.params.get('auth_token', None)
    fb = Facebook(api_key, secret_key, app_name='myapp',
             callback_path='/myapp/callback',
             auth_token=auth_token)
    if not fb.check_session(request) or not auth_token:
        log.info("got an unauthenticated session request")
        return lambda a: redirect(fb, fb.get_login_url())
    return lambda a: f(a, fb=fb)
 
class FacebookController(BaseController):
 
    def index(self):
        return 'Hello World'
 
    @require_login
    def post_add(self, fb=None):
        fb.auth.getSession()
        log.info("got a valid session from user %s", fb.uid)
        fb.profile.setFBML('<fb:wide></fb:wide>')
 
    @require_login
    def callback(self, fb=None):
        c.uid = fb.uid
        return render('/canvas.fbml')

Hopefully that is enough to get you started. I’ll be writing more about this subject so stay tuned. If you have any specific questions, feel free to post a comment!

Facebook apps in Python and Pylons part 1

Thursday, November 20th, 2008

Recently I have been working on a pretty simple Facebook application. I’ve found that the tough thing about writing a Facebook app is not the app per se, but figuring out what a Facebook app actually is, and how it is supposed to work! Anyway, I’m hoping to shed some light on the subject as I figure it out. This first post is mostly some background and description of the various Python implementations of the Facebook API.

Unfortunately, Facebook’s documentation is pretty bad, with a poorly maintained wiki full of out-of-date and plain misleading information. Also, it is all heavily biased toward PHP. On the one hand, this makes a certain amount of sense, since PHP is a very popular Web scripting language, and Facebook itself is implemented in PHP. However, Facebook apps are fundamentally a HTTP-level construct - that is, Facebook makes HTTP requests of various sorts to your application. HTTP is obviously language independent - any language capable of speaking HTTP can be use to implement a Facebook app. I feel that Facebook should focus more on the system at the HTTP level rather than having a load of PHP code and other examples. Anyway, I digress - what you really care about is writing Facebook apps in Python!

There really two distinct things going on in a Facebook app. One is answering HTTP requests from their servers in a specific way. As I wrote above, any language capable of speaking HTTP can handle this. The second is sending requests back to Facebook, in the form that they expect, with data correctly marshaled and so on. While you could implement this interface yourself, there are existing libraries which do all this marshaling and de-marshaling for you. The two I found for Python were minifb and PyFacebook.

I chose to go with PyFacebook, mostly because it seems actively maintained and has a little more documentation than minifb. The biggest gripe I have with PyFacebook is that it is very Django-centric. The second biggest gripe I have is that it is not very well documented. There is a tutorial on the Facebook wiki which is sadly lacking polish, but its still better than nothing.

The PyFacebook source and documentation talk about some Pylons middleware stuff. I tried to use this but found it to be more hassle than its worth - you can just use their provided Facebook class yourself and avoid some dicking about with middleware.py and so on. Actually all you need if you go this route is their __init__.py file. I dropped this in a subdir named ‘facebook’ in my Pylons lib directory. Then you can just do this to use it in your controller:

1
from intothebin.lib.facebook import Facebook

Then from your callback URL handler, you can do stuff like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def callback(self):
    def redirect(fb, url):
        if fb.in_canvas:
            return '<fb:redirect url="%s" />' %(url, )
        else:
            response.status_int = 302
            response.headers['location'] = url
            return 'Moved temporarily'
    api_key = config['pyfacebook.apikey']
    secret_key = config['pyfacebook.secret']
    appid = config['pyfacebook.appid']
    auth_token = request.params.get('auth_token', None)
    fb = Facebook(api_key, secret_key, app_name='myapp',
            callback_path='/myapp/callback',
            auth_token=auth_token)
    if not fb.check_session(request) or not auth_token:
        log.info("got an unauthenticated session request")
        return redirect(fb, fb.get_login_url())
    fb.auth.getSession()

I’ll talk more about what all this stuff means, and what you actually need to do, in the next post. Stay tuned for more!

Pylons tip #3: Easy caching with Beaker

Tuesday, November 18th, 2008

Pylons ships with Beaker, which provides some very useful caching functionality. While there are a bunch of ways to use it, I like the handy decorator, @beaker_cache. To use it, simply wrap up your response-generating function call like so:

1
2
3
4
5
6
7
8
9
10
from pylons.decorators.cache import beaker_cache
class MyController(BaseController):
# ...
    @beaker_cache(expire=600, type='memory')
    def rss(self):
        ''' generate RSS feed '''
        feed = Atom1Feed(...)
        # do some stuff, like add items to the feed
        response.content_type = 'application/atom+xml'
        return feed.writeString('utf-8')

This will automatically handle caching the output of your rss() method in memory, with a lifetime of 600 seconds (10 minutes).

Of course, there are a number of other parameters supported by @beaker_cache. Read the docs here.

Pylons tip #2: Using SQLite with Pylons

Sunday, November 16th, 2008

SQLite is an extremely useful little database. It has a nifty bunch of features and is super simple to set up. Using SQLite reduces the cost of developing and maintaining a powerful SQL database even more than traditional free RDBMS’ like PostgreSQL and MySQL. Your database is simply an on-disk file - no need to configure user accounts, connection strings or any of that stuff.

While this has its limitations, I have found that SQLite is more than sufficient for many web applications. These days I like to write my web applications in Pylons. Python has a full-featured SQLite module which conforms to DB-API 2.0.

To use SQLite in a Pylons application, all you need to do is open the SQLite database file. I use a little convenience function to do this:

1
2
3
4
import sqlite3
def open_db():
    conn = sqlite3.connect(config['sqlite_db_file'])
    return conn

As you can see, the open_db() function references a configuration variable, sqlite_db_file. Instead of having to hard-code the location of the file in your source code, you can simply put it in your Pylons configuration file. to do this, simply add the settings to the app:man section of your INI-file - typically development.ini or test.ini:

1
2
3
4
5
6
7
[app:main]
use = egg:MyApp
full_stack = true
cache_dir = %(here)s/data
beaker.session.key = mykey
beaker.session.secret = somesecret
sqlite_db_file = /path/to/your/sqlite.db

And there you go!

Pylons tip #1 - RESTful Web API decorators

Tuesday, October 21st, 2008

I’ve been playing around with implementing REST-ful HTTP APIs under Pylons recently. Something I find myself doing frequently is writing code to restrict the allowed methods for a given URL. For example, a read-only service should generally only allow GET actions while a write service should generally require POST. Its easy to end up with code like this sprinkled in each controller action:

1
2
3
4
5
6
    # writeservice only accepts POST method requests
    def writeservice(self):
        if request.method != 'POST':
            abort(405,  headers=[('Allow', ['POST'])]
        # ...
        pass

One of the handy modules provided by Pylons is REST-ful decorators. This module provides a simple method decorator called pylons.decorators.rest.restrict. This decorator does essentially the same as the above conditional, but normalises it to a shorter block:

1
2
3
4
5
6
7
from pylons.decorators import rest
 
    # writeservice only accepts POST method requests
    @rest.restrict('POST')
    def writeservice(self):
        # ...
        pass

While in the above example, there is not much space savings, when dealing with various API entry points, the @rest.restrict decorator can make things much clearer, less error-prone and even save a modest amount of lines of code. Handy!