###################################################################### Django Facebook by Thierry Schellenbach (mellowmorning.com) ######################################################################
Contributions are strongly appreciated. Seriously, give github a try, fork and get started :)
Now with support for django custom user models
Django Facebook enables your users to easily register using the Facebook API. It converts the Facebook user data and creates regular User and Profile objects. This makes it easy to integrate with your existing Django application.
I've built it for my startup Fashiolista.com and it's currently used in production with thousands of signups per day. For a demo of the signup flow have a look at Fashiolista's landing page (fashiolista.com)
After registration Django Facebook gives you access to user's graph. Allowing for applications such as:
- Open graph/ Timeline functionality
- Seamless personalization
- Inviting friends
- Finding friends
- Posting to a users profile
Updates and tutorials can be found on my blog mellowmorning
Further demos and tutorials will soon be available on django-facebook.com
- Access the Facebook API, from:
- Your website (Using javascript OAuth)
- Facebook canvas pages (For building facebook applications)
- Mobile (Or any other flow giving you a valid access token)
- Django User Registration (Convert Facebook user data into a user model)
- Store likes, friends and user data locally.
- Facebook FQL access
- OAuth 2.0 compliant
- Automated reauthentication (For expired tokens)
- Includes Open Facebook (stable and tested Python client to the graph API)
Clone the source code or use pip install django_facebook
.
Create a Facebook App
You need a facebook app to use the open graph API and make the login process work. If you don't have a facebook app, now is the time to create one. You can create a facebook app at this url.
Facebook authentication only works if the domain you are working on matches your app domain. Be sure to configure the right app domain in your facebook application settings.
An example:
Your site is www.fashiolista.com, your app domain is set to fashiolista.com and you do your development at local.fashiolista.com
. If you try to authenticate with Facebook from a different domain you will get an authentication error.
Settings
Define the following settings in your settings.py file:
FACEBOOK_APP_ID
FACEBOOK_APP_SECRET
Url config, context processor, auth backend
add django facebook to your installed apps:
'django_facebook',
Add this line to your context processors (TEMPLATE_CONTEXT_PROCESSORS
setting):
'django_facebook.context_processors.facebook',
# and add request if you didn't do so already
'django.core.context_processors.request',
The full setting on a new django 1.5 app looks like this:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.tz',
'django.core.context_processors.request',
'django.contrib.messages.context_processors.messages',
'django_facebook.context_processors.facebook',
)
Add this to your AUTHENTICATION_BACKENDS
setting:
'django_facebook.auth_backends.FacebookBackend',
The full setting on a new django 1.5 app looks like this:
AUTHENTICATION_BACKENDS = (
'django_facebook.auth_backends.FacebookBackend',
'django.contrib.auth.backends.ModelBackend',
)
Now, add this line to your url config:
(r'^facebook/', include('django_facebook.urls')),
(r'^accounts/', include('django_facebook.auth_urls')), #Don't add this line if you use django registration or userena for registration and auth.
Update your models
The following step depends on your version of Django. Django versions before 1.5 need to use a custom profile model. Whereas Django 1.5 and up can use a custom user model.
Django 1.5
If you don't already have a custom user model, simply uses the provided model by setting your AUTH_USER_MODEL to FacebookCustomUser:
AUTH_USER_MODEL = 'django_facebook.FacebookCustomUser'
Alternatively use the abstract model provided in django_facebook.models.FacebookProfileModel
Django < 1.5
If you don't already have a custom Profile model, simply uses the provided model by setting your AUTH_PROFILE_MODULE to FacebookProfile:
AUTH_PROFILE_MODULE = 'django_facebook.FacebookProfile'
Be sure to run manage.py syncdb after setting this up.
Otherwise Django Facebook provides an abstract model which you can inherit like this:
from django_facebook.models import FacebookProfileModel
class MyCustomProfile(FacebookProfileModel):
user = models.OneToOneField('auth.User')
....
from django.contrib.auth.models import User
from django.db.models.signals import post_save
#Make sure we create a MyCustomProfile when creating a User
def create_facebook_profile(sender, instance, created, **kwargs):
if created:
MyCustomProfile.objects.create(user=instance)
post_save.connect(create_facebook_profile, sender=User)
Don't forget to update your database using syncdb or south after this step.
Note: You need a profile model attached to every user model. For new accounts this will get created automatically, but you will need to migrate older accounts.
Congratulations Right now you should have a working registration/connect/login in flow available at /facebook/example/! Test if everything is working and ensure you didn't miss a step somewhere. If you encounter any difficulties please open an issue.
Customize and integrate into your site
Now it's time to customize things a little. For an example you can look at connect.html in the templates directory.
First load the css:
<link href="{{ STATIC_URL }}css/facebook.css" type="text/css" rel="stylesheet" media="all" />
Secondly load the javascript:
{% include 'django_facebook/_facebook_js.html' %}
If you encounter issues here you probably don't have django static files setup correctly. Alternatively you might be missing the context processor.
Subsequently implement a form which calls Facebook via javascript. Note that you can control which page to go to after connect using the next input field.
<form action="{% url facebook_connect %}?facebook_login=1" method="post">
<a href="javascript:void(0);" style="font-size: 20px;" onclick="F.connect(this.parentNode);">Register, login or connect with facebook</a>
<input type="hidden" value="{{ request.path }}" name="next" />
<input type="hidden" value="{{ request.path }}" name="register_next" />
<input type="hidden" value="{{ request.path }}" name="error_next" />
{% csrf_token %} </form>
For most applications you can simply use the next, register next and error next parameters to control the post registration flow. The "next" parameter provides the default next page for login, connect, error or register actions. "register_next" and "error_next" allow you to customize the next page for those specific scenarios. This is usefull when you for instance want to show an introduction page to new users.
Flows
- Login (login_next, next, default)
- Connect (connect_next, next, default)
- Register (register_next, next, default)
- Error (error_next, next, default)
The default redirect is specified by the FACEBOOK_LOGIN_DEFAULT_REDIRECT setting.
If the default customizability isn't adequate for your needs you can also subclass the default registration backend.
:
- class CustomBackend(FacebookRegistrationBackend):
- def post_connect(action):
# go as crazy as you want, just be sure to return a response response = HttpRedirect('/something/') if action is CONNECT_ACTIONS.LOGIN: response = HttpRedirect('/') return response
In order to use build a facebook canvas application, you should add this to your MIDDLEWARE_CLASSES
setting:
'django_facebook.middleware.FacebookCanvasMiddleWare'
- This middleware will check for the signed_request parameter in the url and take the appropriate action:
- redirect to app authorization dialog if user has not authorized the app, some permission is missing or any other error.
- login the current facebook user in django's system and store the access token.
Django Facebook expects that you are using static files in order to load the required javascript. If you are not using staticfiles you should load facebook.js provided in the static directory manually.
Another common issue are the url matching settings from Facebook. Facebook requires you to fill in a domain for your application. In order for things to work with local development you need to use the same domain. So if you production site is www.mellowmorning.com you should run your development server on something like local.mellowmorning.com in order for facebook to allow authentication.
If you encounter any difficulties please open an issue.
Registration Backends
By default Django Facebook ships with its own registration system. It provides a basic manual registration flow and the option to connect with Facebook.
If you are looking for something more complicated it's possible to integrate with Userena or Django Registration. To add support for these systems we use the FACEBOOK_REGISTRATION_BACKEND setting.
Django Registration support Create a registration backend which subclasses both Django Facebook and Django Registration's registration backend. An example is included in facebook_example/registration_backends.py
- ::
# in registration_backends.py class DjangoRegistrationDefaultBackend(DefaultBackend, NooptRegistrationBackend): ''' The redirect behaviour will still be controlled by the post_error post_connect functions the form and other settings will be taken from the default backend ''' pass
# in your settings file FACEBOOK_REGISTRATION_BACKEND = 'registration.backends.default.DefaultBackend'
Django Userena support
Django Userena is easier to work with than Django Registration. It is however hard to setup unittesting with Userena, so the integration between Django Facebook and Userena might not work. Please report any bugs you run into.
FACEBOOK_REGISTRATION_BACKEND = 'django_facebook.registration_backends.UserenaBackend'
Also have a look at the userena settings file in the facebook example project. It provides a clear example of how to configure Userena and Django Facebook to work together.
Other registration systems
Supporting any other registration system is quite easy. Adjust the above settings to point to your own code. Note that the form's save method needs to return the new user object.
Django-facebook ships with a few signals that you can use to easily accommodate Facebook related activities with your project.
facebook_user_registered
signal is sent whenever a new user is registered by Django-facebook, for example:
from django_facebook.utils import get_user_model
from django_facebook import signals
def fb_user_registered_handler(sender, user, facebook_data, \*\*kwargs):
# Do something involving user here
signals.facebook_user_registered.connect(user_registered, sender=get_user_model())
facebook_pre_update
signal is sent just before Django-facebook updates the profile model with Facebook data. If you want to manipulate Facebook or profile information before it gets saved, this is where you should do it. For example:
from django_facebook import signals
from django_facebook.utils import get_user_model
def pre_facebook_update(sender, user, profile, facebook_data, \*\*kwargs):
profile.facebook_information_updated = datetime.datetime.now()
# Manipulate facebook_data here
signals.facebook_pre_update.connect(pre_facebook_update, sender=get_user_model())
facebook_post_update
signal is sent after Django-facebook finishes updating the profile model with Facebook data. You can perform other Facebook connect or registration related processing here.
from django_facebook import signals
from django_facebook.utils import get_user_model
def post_facebook_update(sender, user, profile, facebook_data, \*\*kwargs):
# Do other stuff
signals.facebook_post_update.connect(post_facebook_update, sender=get_user_model())
facebook_post_store_friends
signal is sent after Django-facebook finishes storing the user's friends.
from django_facebook import signals
from django_facebook.utils import get_user_model
def post_friends(sender, user, friends, current_friends, inserted_friends, \*\*kwargs):
# Do other stuff
facebook_post_store_friends.connect(post_friends, sender=get_user_model())
facebook_post_store_likes
signal is sent after Django-facebook finishes storing the user's likes. This is usefull if you want to customize what topics etc to follow.
from django_facebook import signals
from django_facebook.utils import get_user_model
def post_likes(sender, user, likes, current_likes, inserted_likes, \*\*kwargs):
# Do other stuff
facebook_post_store_likes.connect(post_likes, sender=get_user_model())
Facebook APIs can take quite some time to respond. It's very common that you will wait between 1-3 seconds for a single API call. If you need multiple calls, pages can quickly become very sluggish.
The recommended solution is to use Celery. Celery is a task queueing system which allows you to run the API requests outside of the request, response cycle.
Step 1 - Install Celery
Step 2 - Enable Tasks
# use celery for storing friends and likes
FACEBOOK_CELERY_STORE = True
# use celery for extending tokens
FACEBOOK_CELERY_TOKEN_EXTEND = True
When writing your own Facebook functionality you will see a big speedup by using @facebook_required_lazy instead of @facebook_required
Tests are run from within the example project. You can run them yourself as follows:
install from git
facebook_example/manage.py test django_facebook
Vagrant
A vagrant development setup is included in the GIT repo. Assuming you have vagrant installed, simply type the following in your shell:
# First get a fresh Django-Facebook checkout
git clone git@github.com:tschellenbach/Django-facebook.git django-facebook
# Go to the directory:
cd django-facebook
# Currently Vagrant is only support on branch `five`, so until this has been merged you need to switch branches:
git checkout five
# Time to start Vagrant (grab a cup of coffee after this command, it'll take a while) :)
vagrant up
# Finally done?
vagrant ssh
python manage.py runserver 0:8080
To have a working Django Facebook example up and running at 192.168.50.42:8000/facebook/example/. For the facebook login to work simply map that ip to vagrant.mellowmorning.com (Since Facebook checks the domain)
You can run the test suite by typing:
python manage.py test django_facebook
Do you also see the beauty in clean code? Are you experienced with high scalability web apps? Currently we're looking for additional talent over at our Amsterdam office. Feel free to drop me a line at my personal email for more information: thierryschellenbach[at]gmail.com