PythonPyFacebookTutorial

From Facebook Developers Wiki

Jump to: navigation, search

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 facebook global 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_LOADERS contains '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_DIRS to 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_KEY and FACEBOOK_SECRET_KEY to 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 your urlpatterns like:
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:

@facebook.require_login()
def callback(request):
    return render_to_response('canvas.fbml', {'key': request.facebook.session_key})
  • Create a canvas.fbml template file that includes the following:
Your session key is: {{ key }}

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.

Navigation