PythonPyFacebookTutorial
From Facebook Developers Wiki
Contents
|
[edit] PyFacebook Tutorial
PyFacebook is a Python interface to the Facebook API. It can be used in both desktop and web (internal and external) applications.
If you need any clarification or help as you're going through the tutorial, feel free to ping me (yoshiznit123) on the #facebook IRC channel.
[edit] Installation
First, you need to grab a copy of PyFacebook. On Linux, make sure you have the Subversion client installed, and type
svn checkout http://pyfacebook.googlecode.com/svn/trunk/ pyfacebook
Since no release has been made yet, instead of doing the usual python setup.py install you may want to create a symlink to keep up-to-date with the latest code. To do this, type (as root)
ln -s `pwd`/pyfacebook/facebook SITE-PACKAGES-DIR/facebook
replacing SITE-PACKAGES-DIR with the appropriate location for your system.
(The location of the site-packages directory depends on the operating system, and the location in which Python was installed. On Linux, it might look
something like /usr/lib/python2.5/site-packages, and on Windows it might be C:\Python25\Lib\site-packages. To find out your
system’s site-packages location, execute the following:
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
Note that this should be run from a shell prompt, not a Python interactive prompt.)
On Windows, since symlinking doesn't work, use a Subversion client such as TortoiseSVN, and check out http://pyfacebook.googlecode.com/svn/trunk/facebook directly into SITE-PACKAGES-DIR/facebook (see note above about the site-package directory).
For desktop apps, if you don't want to download everything, you can get by with a single file - grab this file, rename it to facebook.py, and place it somewhere where it can be imported.
[edit] Django Helpers
If you're going to be using Django, PyFacebook can also help you set up your projects (explained below). If you want, you can copy or symlink the file pyfacebook/bin/djangofb.py to somewhere on your system path, such as /usr/local/bin (Unix) or C:\Python25\Scripts (Windows). This step simply lets you type djangofb.py from within any directory, rather than having to qualify the command with the full path to the file.
Assuming you've set everything up correctly, it's time to get to the fun part: coding!
[edit] Desktop Applications
For desktop applications, take a look at the main function in facebook/__init__.py (or facebook.py if you renamed it). This should be enough to get you started.
Facebook API calls are directly mapped to Python calls. For example:
>>> import facebook
>>> fb = facebook.Facebook('YOUR_API_KEY', 'YOUR_SECRET_KEY')
>>> fb.auth.createToken()
u'AUTH_TOKEN'
>>> fb.login()
>>> fb.auth.getSession()
{u'secret': u'DESKTOP_SECRET', u'session_key': u'SESSION_KEY', u'uid': u'13608493', u'expires': 1181156943}
>>> fb.users.getInfo([fb.uid], ['name', 'birthday'])
[{u'birthday': u'August 18, 1988', u'name': u'Samuel Cormier-Iijima', u'uid': 13608493}]
Contextual help with help() is available for each of the functions in the API.
To use infinite sessions, you need to follow the above steps once. When logging in, check "Stay logged in to this application." Also, store the values for 'secret' and 'session_key' that you get from auth.getSession(). From then on you can do the following, for example from a cron job:
fb = facebook.Facebook('YOUR_API_KEY', 'YOUR_SECRET_KEY')
fb.session_key = 'session_key value from getSession() call'
fb.secret = 'secret value from getSession() call'
# make any API calls you want here
[edit] Web Applications
If you want to write web applications with Python, you'll probably want to use a web framework. Frameworks make your life much simpler by providing a nice interface for writing server apps.
PyFacebook is currently best-tested with Django, and if you are just starting out with Python web development, the author highly recommends this combination :-). If you'd rather use another framework, there are also Pylons and other WSGI helpers in PyFacebook as well.
[edit] Django
We are assuming that you already have Django installed and ready to go. You may also need a database, like MySQL, PostgreSQL, or SQLite. If you aren't sure about any of this, visit www.djangoproject.com. Their documentation is excellent :-).
[edit] Basic Setup
First, we're going to make a Django project called "fbsample" by running
django-admin.py startproject fbsample
This creates a directory structure that looks like this:
fbsample/
__init__.py
manage.py
settings.py
urls.py
Django projects are a collection of applications, which are modular pieces of functionality that can usually be plugged into different projects.
PyFacebook has a helper to get you started with an application quickly. This is the recommended way of setting up new projects, as it shows you good structure and coding style.
If you've installed djangofb.py into your system path, change
to the fbsample/ directory, and type
djangofb.py startapp fbapp
If you didn't install it to the system, you will have to use the full path to djangofb.py.
(If running this command gives you an error, make sure your current directory is fbsample/.)
This creates an internal Facebook application called "fbapp" inside your project directory. djangofb.py
will create some models, views, and templates for you:
fbsample/
fbapp/
templates/
canvas.fbml
__init__.py
models.py
urls.py
views.py
__init__.py
manage.py
settings.py
urls.py
After its done setting these up, it will tell you a list of a couple things that you have to do before being able to run your project. These may include:
[edit] Set your app keys
In order to be able to make API calls, you have to tell tell Django what your application keys are. Open up fbsample/settings.py, and
inside it paste the following at the end:
FACEBOOK_API_KEY = 'your_api_key' FACEBOOK_SECRET_KEY = 'your_secret_key'
Of course, replace these values with the right ones for your application.
[edit] Add the middleware
Look for the MIDDLEWARE_CLASSES setting in fbsample/settings.py,
and add the PyFacebook middleware to the end of the list. It should look something like:
MIDDLEWARE_CLASSES = (
...,
...,
'facebook.djangofb.FacebookMiddleware',
)
This will attach a facebook object to every incoming request.
[edit] Add the template loader
Most of the time, this should already be done for you by Django. This lets templates be loaded from
the application directories, so that looking for a template called 'canvas.fbml' will also look
in fbsample/fbapp/templates/, which is where the default templates are placed.
[edit] Add the app to INSTALLED_APPS
For Django to know about the app you created, you have to tell it where they are. djangofb.py
should give you the right line to add to settings.py. In this example, it would look like:
INSTALLED_APPS = (
...,
...,
'fbsample.fbapp',
)
[edit] Add the URL mappings
You need to hook up a URL to the application so that Django knows which incoming requests
should be directed to which views. Again, djangofb.py automagically guesses
the correct line you have to add, so your urls.py file would contain:
urlpatterns = (
...,
(r'^fbsample/', include('fbsample.fbapp.urls')),
...,
)
This will tell the sample application to live under /fbsample/.
On the Facebook applications page, make sure that you set your callback to the appropriate URL, i.e. the one you
just created in urls.py. In this example, it would be 'http://www.example.com/fbsample/'.
DON'T FORGET THE TRAILING SLASH :-)
[edit] Running the Development Server
If you're going to be using a database, make sure you set your database settings in settings.py. Read this tutorial if you aren't sure of how to do this.
That's all the configuration we're going to need. Now run
python manage.py syncdb
to install all of the required tables in your database, and then
python manage.py runserver 0.0.0.0:80
to start the development server. (Notice that we need to set the outside IP so that Facebook can call us back. Therefore, you need a publicly visible IP address, which might involve setting up your router with port forwarding.)
You should now be able to browse to http://apps.facebook.com/YOURAPP/ and see a basic application!
[edit] URL Mappings
Django uses a URL mapping system to route URLs to their respective Django "views". Views are just Python functions that take a django.http.HttpRequest object and return an HttpResponse.
For this sample application, we want a canvas view. In the settings page on Facebook for your application, make sure that you set an appropriate callback URL. If your callback page is at http://1.2.3.4/fbsample/callback/, we need to tell Django how to handle the /callback/ suffix. To do this, open up fbsample/fbapp/urls.py, and set the urlpatterns variable to this:
urlpatterns = patterns('',
(r'^callback/$', 'fbsample.fbapp.views.callback'),
)
This means that when somebody requests /fbsample/callback/, the function fbsample.fb.views.callback will be called.
[edit] Views
Now we need to set up a view so that Facebook will know where to look.
First, create the actual view that will be called. This would be where all your logic goes. Open up fbsample/fbapp/views.py, and type or copy the following:
from django.shortcuts import render_to_response
# for Python2.5, you may have to do:
# import .facebook
# for older versions ( < r70) of PyFacebook, do this:
# import facebook
# with this import, you shouldn't have to change any code
import facebook.djangofb as facebook
@facebook.require_login()
def callback(request):
name = request.facebook.users.getInfo([request.facebook.uid], ['first_name'])[0]['first_name']
return render_to_response('canvas.fbml', {'name': name})
Notice how little code you had to write!
The @-syntax is the new Python syntax for decorators. facebook.require_login() makes sure that the user has a valid session, and otherwise redirects them to a login page. This could also be @facebook.require_login(next="some/page") if you want the user to be redirected to a certain page after logging in. The FacebookMiddleware that you added at the beginning of the tutorial attaches a facebook object to every request, which you can use to make calls to the Facebook API. In this case, we call users.getInfo with the current user as the first argument, and we want to get their first name. The request actually returns a Python list or dictionary, depending on the JSON or XML returned by Facebook. In this case, the response would look something like:
[{'first_name': 'Samuel', 'uid': 13608493}]
If you want to find out what kind of response you'll get, you can use the API Test Console. Make the response format JSON - it's pretty similar to Python.
So now that we have their first name, we push them back a template with render_to_response (passing in the name we just got).
All that's left is to make that template page called "canvas.fbml" (this helps separate code from content, and is one of the reasons why PHP sucks ;-)) in a new directory called templates. So now the directory structure looks like this:
fbsample/
fbapp/
__init__.py
models.py
views.py
templates/
canvas.fbml
__init__.py
manage.py
settings.py
urls.py
Inside canvas.fbml, you can put any valid FBML you want, marked up with the awesome Django template language. For example, you can put:
<fb:header>
Welcome, {{ name }}!
</fb:header>
which would replace {{ name }} with their name.
Finally, you need to set the path to your templates in fbsample/settings.py. Open this file and search for TEMPLATE_DIRS. You should see something like the following:
TEMPLATE_DIRS = (
'/path/to/fbsample/templates',
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
Of course, you will need to replace /path/to/fbsample/templates with the full path to your template directory (i.e., the directory containing the canvas.fbml file you created above).
If you want to show a page that doesn't require a user to login, but want to provide personalized content if they are, you can use the following code for your view:
def view(request):
if not request.facebook.check_session(request):
return render_to_response('view.fbml', {})
else:
return render_to_response('view.fbml', {'user': request.facebook.uid})
And then in your template, you can choose what to display like this:
{% if user %}
Hello, <fb:name uid="{{ user }}" firstnameonly="true" useyou="false" />.
{% else %}
You are not logged in. <a href="?login">Log In Here</a>
{% endif %}
[edit] WSGI, Paste, and Pylons
pyfacebook.wsgi has some generic WSGI middleware, some Paste stuff, and some Pylons stuff. Once you put FacebookWSGIMiddleware into your middleware stack, you'll have access to environ["pyfacebook.facebook"], which is a facebook.Facebook object. If you're using Paste (which includes Pylons users), you can also access this directly using the "magic global" pyfacebook.wsgi.facebook.
[edit] Setting up development.ini
In the [app:main] section, you'll need:
# Setup PyFacebook. pyfacebook.callbackpath = myapp pyfacebook.apikey = 6b5aca8bd71c1234590e697f79ec0cc3 pyfacebook.secret = 2033b7d247a1123457663ba343817bea pyfacebook.appid = 2412345947
[edit] Setting up routing.py
You can get pretty fancy about how you setup your routing. Here's how I did it:
# CUSTOM ROUTES HERE
from pylons import config
prefix = config['app_conf']['pyfacebook.callbackpath']
assert (not prefix.startswith('/') and
not prefix.endswith('/'))
# Usually, the Facebook URL contains the callback path.
map.connect('%s/:controller/:action/:id' % prefix)
# It's okay if they leave out the controller. The first time I
# wrote this app, everything was routed through just one controller.
# I'm avoiding changing the URLs because I have this app written in
# multiple languages. Hence, I'll just provide a default
# controller.
map.connect('%s/:action/:id' % prefix, controller='main')
# Leaving off the prefix is okay too, but this will only happen if
# they try to hit the app directly. They'll just end up getting
# redirected.
map.connect(':controller/:action/:id')
# This is the default URL.
map.connect(prefix, controller='main')
map.connect('', controller='main')
map.connect('*url', controller='template', action='view')
Note that the order of these is pretty important. When Pylons generates a URL, you'll want it to generate a URL that Facebook can understand, which is stricter than what the app can understand in general due to the flexible setup of the routes.
[edit] Setting up middleware.py
In Pylons, you set up your middleware.py like this:
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) from facebook.wsgi import create_pylons_facebook_middleware app = create_pylons_facebook_middleware(app, app_conf)
[edit] Using facebook in your Controllers
Here are some snippits from a controller that uses PyFacebook:
...
from facebook import FacebookError
from facebook.wsgi import facebook
...
class MainController(BaseController):
def __before__(self):
action = request.environ['pylons.routes_dict']['action']
c.facebook = facebook
if facebook.check_session() and facebook.added:
pass
elif action == 'post_remove':
pass
elif facebook.in_canvas and action in ('help', 'privacy', 'tos'):
pass
else:
url = facebook.get_add_url()
log.debug("Force user to add app: facebook.redirect_to(%r)" % url)
facebook.redirect_to(url)
...
def edit(self):
...
if request.params.get('fb_sig_request_method') == 'POST':
...
facebook.redirect_to(h.url_for())
return render('edit')
...
def friends(self):
...
friends = facebook.friends.getAppUsers()
...
return render('friends')
...
def _update_profile(self):
"""Update the user's profile."""
markup = render('profile')
facebook.profile.setFBML(markup=markup, uid=facebook.uid)
log.debug("Updated profile for uid:%s" % facebook.uid)
def _publish_action(self):
"""Publish a news item."""
...
title = render('news')
try:
if not facebook.feed.publishActionOfUser(title=title):
# Yet another way this can fail.
raise FacebookError("Permission error")
log.debug("Publish action of user for uid:%s" % facebook.uid)
except FacebookError, e:
# There's no need to tell the user.
log.debug("Could not publish news item:%r" % e)
Notice in particular:
- The
facebookglobal is your key to everything.
- The
__before__function is used to set everything up.
- To do a redirect, you must use
facebook.redirect.
[edit] Learning More
Read the source for facebook.wsgi. It's short and heavily documented :)
[edit] Using the Sample Application
The sample application for Django lives in /examples/pyfacebook_sample/ in SVN. It can be seen in action at http://apps.facebook.com/pyfacebook/. It may be down for a while, as I can't use that server anymore and have to switch to another one.
To use the application, copy the entire folder to an existing Django project. Then you need to edit the following settings:
- You will need PyFacebook somewhere on your Python path.
- In settings.py, make sure that
TEMPLATE_LOADERScontains 'django.template.loaders.app_directories.load_template_source'. This is so that templates can be loaded from the sample template directory.
- In settings.py, define your template path in
TEMPLATE_DIRSto contain the path to the sample templates provided in the example. If your project name was myproject, path was mypath, and the instance was pyfacebook_sample, you would need to define 'mypath/myproject/pyfacebook_sample/templates'.
- In settings.py, define
FACEBOOK_API_KEYandFACEBOOK_SECRET_KEYto your own values.
- In settings.py, add the following line to the variable
MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = (
...,
...,
'facebook.djangofb.FacebookMiddleware',
)
replacing YOUR_PROJECT_NAME with the actual value. This will attach a facebook object to every incoming request.
- In
urls.py, have something in yoururlpatternslike:
urlpatterns = (
...
(r'^facebook/', include('YOUR_PROJECT_NAME.facebook.urls')),
)
This will tell the sample application to live under /facebook/.
- On the Facebook applications page, make sure that you set your callback to the appropriate URL. In this example, it would be '
http://YOUR_IP/facebook/canvas/', and DON'T FORGET THE TRAILING SLASH :-)
- Change any occurrences of "pyfacebook" in the templates and views to your own application name.
That should be about it...
[edit] Adding content to a profile page
The following code will place a message in your application's box on the user's profile page:
@facebook.require_login()
def update_profile(request):
fbml = "<fb:wide><fb:subtitle>This is the subtitle</fb:subtitle>"
fbml += "<fb:if-is-own-profile>This is your profile.<fb:else>This is not your profile.</fb:else></fb:if-is-own-profile></fb:wide>"
request.facebook.profile.setFBML(fbml, [request.facebook.uid])
return HttpResponseRedirect(request.facebook.get_url('profile'))
The above code assumes that you are using the Django framework. Edit the urls.py file and add a URL mapping such as (r'^update_profile/$', 'fbsample.facebook.views.update_profile'). Loading http://apps.facebook.com/yourapp/update_profile/ should forward you to your profile which will now have the new FBML.
[edit] Adding content to a profile page without user interaction/login
To do this you must first generate an infinite session key, which can be done as follows:
- Generate an authorization token by going here: http://www.facebook.com/code_gen.php?v=1.0&api_key=YOUR_API_KEY (you must replace YOUR_API_KEY with your application's API key)
- Create a canvas page that will echo your session key. Here is an example using the Django framework:
@facebook.require_login()
def callback(request):
return render_to_response('canvas.fbml', {'key': request.facebook.session_key})
- Create a
canvas.fbmltemplate file that includes the following:
Your session key is: {{ key }}
- Pass the authorization token generated above to your canvas page by going to the following URL: http://apps.facebook.com/YOUR_APP_NAME/?auth_token=YOUR_AUTH_TOKEN (replace YOUR_APP_NAME and YOUR_AUTH_TOKEN with the appropriate values; make sure requesting http://apps.facebook.com/YOUR_APP_NAME/ calls your
callback()function above.) - The page should display the text "Your session key is: " followed by a 33-character string. This is your infinite session key and should never expire.
You can now use this key along with the following code to set the FBML on a user's profile page:
import facebook
api_key = 'YOUR_API_KEY'
secret_key = 'YOUR_SECRET_KEY'
session_key = 'YOUR_SESSION_KEY'
fb = facebook.Facebook(api_key, secret_key)
fb.session_key = session_key
fb.profile.setFBML('<fb:wide>Woot!</fb:wide>', 'USER_ID')
where YOUR_API_KEY and YOUR_SECRET_KEY are your API and secret keys, USER_ID is the Facebook ID of the profile you would like to update, and YOUR_SESSION_KEY is the session key that you generated in the previous step.
[edit] Using Sessions (ie the django.contrib.sessions framework)
- BRAIN/CODEDUMP*
To use sessions from inside the Facebook canvas, you can hack around the sessions framework to use the user's session_key as the session_key instead of using cookies.
