Using Pylons for a Facebook Application
Cue Inspiration
I had an idea the other day for a simple Facebook application. With Pylons, I figured it would take no more than an hour or two to get the mechanics of the application working at which point a designer could come in to handle the rest. What followed was quite a struggle.
I’ve written Facebook applications using the old PHP SDK. While this method is rather well documented, moving to Python should have been easy. PyFacebook, a project hosted at GitHub, is woefully out of date. Even after applying three of the suggested patches, and modifying one of the imports in the library, I was left with numerous issues regarding the access_token, odd redirects and a few other minor issues. Add to this the fact that Facebook really suggests that applications use the Graph API and IFrames rather than the FBML canvas and we’re setting ourselves up for a problem down the road. Facebook has maintained that they will always support FBML and the REST API, but, new features won’t be accessible to the older API.
With that in mind, I looked at Python SDK, the officially recommended library for Python and Facebook. While Pylons is supported fairly well, going through the documentation on Facebook’s site resulted in looking through the PHP-SDK, the supplied oauth access in the Python-SDK and a bit of trial and error along the way. OAuth with the Graph API is a bit more complex to understand, but, if your application is using AJAX or JSON, avoiding the FBML proxy is much quicker. Flash was used quite a bit with FBML applications so that applications could communicate directly with a game server which made things much quicker. HTML apps using FBML often exhibited pageload performance problems. With the IFrame method, your application still runs within Facebook’s canvas, but, the surfer is communicating directly with your application.
What happened?
First, I tried to replicate what I had done in PHP using PyFacebook and Pylons. While there are hooks for WSGI (which includes Paster/Pylons implemented servers), there were a number of issues. I briefly tried Django with PyFacebook and met different issues. Once you stray from PHP, you’re in uncharted territory. A statistic I read somewhere claimed that only a few hundred apps were developed in something other than PHP with Java/JSP being the most common alternate. Django, the Google App Engine and web.py appear to be the favorites among Python frameworks. While I know there are a handful of applications running Pylons, and at least one running TurboGears, I don’t believe there are many using the Graph API.
At this point, the Graph API and OAuth seemed to be the sane choice. An IFrame canvas, using the Javascript SDK to complement Python SDK appeared to be the answer.
The first stumbling block when following the OAuth guide on Facebook is the frame in a frame shaded authentication box. Clicking on the grey box opens the inner frame to the full page where you can authorize the application, but, that is a rather ugly situation. The following Javascript fixes that which isn’t great solution, but does work.
<script language="javascript"> top.location.href='https://graph.facebook.com/oauth/authorize?client_id=${config['facebook.appid']}&redirect_uri=${config['facebook.callbackurl']}&display=page&scope=publish_stream'; </script>
Error validating verification code
After working with a few other issues, another issue with the auth_token resulted in the following error (after loading the url that was being fetched):
{ "error": { "type": "OAuthException", "message": "Error validating verification code." } }
Adding &type=client_cred to your access_token url fixes that situation.
Here’s the guide
We’re going to put our project in the facebook directory and use Pylons 1.0:
git clone http://github.com/facebook/python-sdk.git wget http://www.pylonshq.com/download/1.0/go-pylons.py python go-pylons.py facebook cd facebook source bin/activate paster create -t pylons fbapp cd fbapp vi development.ini rm fbapp/public/index.html cp ../python-sdk/src/facebook.py fbapp/fbapp/lib
We need to make a few changes to our development.ini in the [app:main] section:
facebook.callbackurl = http://apps.facebook.com/ourfbapp/ facebook.apikey = 6b5aca8bd71c1234590e697f79xxxxxx facebook.secret = df5d928b87c0df312c8be101e5xxxxxx facebook.appid = 124322020xxxxxx
modify config/routing.py:
map.connect('/{action}', controller='root') map.connect('/', controller='root', action='index')
templates/oauth_redirect.mako:
<script language="javascript"> top.location.href='https://graph.facebook.com/oauth/authorize?client_id=${config['facebook.appid']}&redirect_uri=${config['facebook.callbackurl']}&display=page&scope=publish_stream'; </script> <noscript> <a href="https://graph.facebook.com/oauth/authorize?client_id=${config['facebook.appid']}&redirect_uri=${config['facebook.callbackurl']}&display=page&scope=publish_stream" target="_top">Click here to authorize this application</a> </noscript>
templates/index.mako:
${tmpl_context.user}
controllers/root.py:
# using python 2.5 import simplejson import cgi import urllib from pylons import request, response, session, tmpl_context, config from pylons.controllers.util import abort, redirect from fbapp.lib.base import BaseController, render import fbapp.lib.facebook as facebook class RootController(BaseController): def __before__(self): tmpl_context.user = None if request.params.has_key('session'): access_token = simplejson.loads(request.params['session'])['access_token'] graph = facebook.GraphAPI(access_token) tmpl_context.user = graph.get_object("me") def index(self): if not tmpl_context.user: return render('/oauth_redirect.mako') return render('/index.mako')
In Facebook, you want to make the following changes:
Canvas Callback URL: http://yourdomain.com/
Connect URL: http://yourdomain.com/
Canvas URL: http://apps.facebook.com/yourappname/
FBML/Iframe: iframe
Application Type: website
Under the Migrations Tab, Verify that New Data Permissions and New SDKs are both set to enabled. When you write your application, you can refer to the extended permissions which are set in the &scope= section of oauth_redirect.mako.
What’s next?
Once you’ve retrieved the access_token and the user_id, you probably want to save that into your local database so that you don’t need to fetch the data from the Graph API on every pageload. While the Graph method is indeed faster than FBML, Facebook has lifted some of the restrictions regarding the data you can keep which allows for faster pageloads. With the IFrame method, pages using AJAX/JSON are indeed much quicker.
While I originally estimated this project to take ‘a few hours’, working through all of the possible scenarios with the Python Facebook SDK ended up taking quite a bit more time than expected. The Graph API is very well thought out and is much faster than the REST API and appears to be almost as fast as FQL.
Good Luck!
May 27th, 2010 at 5:45 pm
[…] This post was mentioned on Twitter by Social Lattice, Chris Davies. Chris Davies said: blog: Using Pylons for a Facebook Application http://cli.gs/n8UtJ #facebook #graph_api #pylons […]
November 7th, 2010 at 3:39 am
Are you running your apps on Google App Engine? I have a problem decode Facebook signed_request on it…
November 7th, 2010 at 3:38 pm
If I recall, the signed request documentation had a bug that used the APP ID rather than the APP Key in the call. I’ve actually resorted to using the Javascript SDK to do everything, and just reading the graph token in Pylons. I’ve not worked much with the App Engine for Facebook applications, but, most of the Python SDK was written for the app engine and contains a working ‘app’ in the download.
December 27th, 2010 at 10:11 pm
Is the app in this state supposed to do anything? When I try and fire up “paster serve –reload development.ini” and navigate to http://127.0.0.1:5000/, I get an error that says “ImportError: No module named fbapp.lib.base”, which is called in the root.py controller. Any idea why this is? I thought I followed all the steps to the letter
December 28th, 2010 at 1:47 am
When you did your paster create, did you choose something other than fbapp?
December 28th, 2010 at 5:25 pm
Ahh good point, I named it the actual application name…I’ll change the corresponding import and see if that works. Thanks!