blog

Django logo

Tutorial: Posting to Facebook from a Django App

by

This tutorial is the first part of a series of tutorials that build a complete Django application, codenamed procrastination automation. The tutorial on Python Social Authentication can be considered a preface to this series – if you would like an introduction into using social authentication with Python or Django, check it out.

Some time ago, I saw a diagram that showed how content originates from 4chan (or was it 9gag?), then gets reused by Reddit, then gets reposted on Digg, and ends up on Facebook. Don\’t google it, it is using a very ugly image for the metaphor. But the idea is that thousands of people are viewing things on one subset of social resources and reposting on another subset, where this content becomes the source of entertainment and news for more viewers.

Literally millions of man-hours are spent daily to transfer images of cat-based memes from Reddit to Facebook. This is a perfect opportunity for automation.

Let\’s build an app that will allow a person who would ordinarily browse Reddit a few hours a day and repost pretty much every link from a favourite subreddit to Facebook, set a personal re-poster that will automatically forward those updates from Reddit to his Facebook wall for him, impressing his friends with his immense social presence, and saving his precious time.

Step 1. A Bare-Bones Django App + Facebook Authentication

We\’ll begin with an app that\’s very similar to the one we built in the Python Social Auth tutorial. For reference, here\’s a rehash of the skeleton-building process: – Create a virtualenv, set up Django, python-social-auth, and facebook-sdk. Other convenient things for Django development, such as South and django-debug-toolbar, are recommended.

  • Start a Django project and create an app within it. Let\’s name it fbposter, because I have no creative imagination.
  • Enable python-social-auth in your Django project settings.
  • Use a fake custom domain (aliased to 127.0.0.1 in your /etc/hosts or equivalent), so that OAuth2 can work with the dev server.
  • Get a client ID for Facebook application. Note on security: for production use, it is not recommended to put client ID in the source of the application – certainly not in an open-source one (hat tip to a commenter named Burke who pointed this out in the previous tutorial). Use environment variables and untracked files instead.
  • Get the basic page templates and static stuff in place. We\’ll continue using Bootstrap-based templates in this tutorial.

The only difference between the previous tutorial and this one so far is the Facebook scope in settings: we added status_update to the list of things we are asking permission to do. This will be reflected in the authentication token that gets saved during Facebook authentication/authorization step. (See official documentation here.)

SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {'locale': 'en_US'}

What have we achieved so far? We have a Django app that can authenticate users via Facebook, and most importantly for the next steps, stores the authentication tokens in the database, enabling us to post to Facebook in the next step.

Step 2. Add Models for Storing Facebook Statuses

When our application is complete, it will have two automatic background processes: a job that accumulates updates for future publishing, and another job that periodically publishes them. Let\’s add a model for the statuses. For fields, will follow the cue from Facebook Graph API description of status publishing:

class FacebookStatus(models.Model):
    class Meta:
        verbose_name_plural = 'Facebook Statuses'
        ordering = ['publish_timestamp']
    STATUS = (
        ('draft', 'Draft'),
        ('approved', 'Approved'),
    )
    status = models.CharField(max_length=255,
        choices=STATUS, default=STATUS[0][0])
    publish_timestamp = models.DateTimeField(null=True, blank=True)
    author = models.ForeignKey(User)
    message = models.TextField(max_length=255)
    link = models.URLField(null=True, blank=True)
    def __unicode__(self):
        return self.message

The status and publish_timestamp fields will help us keep track of statuses that are ready for posting, and have been already posted. The author FK links the status to the user, who, in turn, has an authentication record created during Facebook login. For now, we do not need any custom views – we can use our home page to log on to Facebook, the Python Social Auth callback will populate the authentication tokens for us, and we can start creating statuses in the back-end. Simply run ./manage.py syndcb or generate and run the migration if you are using South, and you are ready for the next step.

Step 3. Post a Facebook status

Let\’s run a little test. We\’ve completed all the steps before, and now we have a system that

  1. Allows the user to authenticate with Facebook
  2. Saves Facebook access tokens in the database
  3. Allows us to create statuses in the database.

Make sure you\’ve logged in to Facebook at least once through the application, and created at least one status with some text. Now, let\’s run ./manage.py shell, and type:

import datetime
from django.contrib.auth.models import User
from fbposter.models import FacebookStatus
import facebook
user = User.objects.get(email='<your email address>')
statuses = FacebookStatus.objects.filter(author=user, status='approved', publish_timestamp=None)[:1] # We only need one status for this test
status = statuses[0]
auth = user.social_auth.first()
graph = facebook.GraphAPI(auth.extra_data['access_token'])
graph.put_object('me', 'feed', message=status.message)
status.publish_timestamp = datetime.datetime.now()
status.save()

If the social_auth record was set up correctly, and the status was there, then our status should now appear on Facebook. If it did, let\’s wrap this code into a custom Django command, which we will be able to run from a cron setup.

Step 4. Setting up Custom Django Management Command for Posting to Facebook

To add a custom command that can run through ./manage.py custom_command, or more specifically, ./manage.py facebook_post, add a package named fbposter.management.commands – that is, create a folder structure like this:

fbposter/management/
├── commands
│   ├── facebook_post.py
│   ├── __init__.py
├── __init__.py

The source of facebook_post should look like this:

from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from fbposter.models import FacebookStatus
import facebook
import datetime
class Command(BaseCommand):
    args = ''
    help = 'Posts a message to Facebook'
    def handle(self, *args, **options):
        if len(args) < 1:
            self.stdout.write('Usage: ./manage.py facebook_post ')
            return
        user = User.objects.get(email=args[0])
        messages = FacebookStatus.objects.filter(status='approved', publish_timestamp=None, author=user)[:1]
        if not messages:
            self.stdout.write('There are no pending messages for %s' % user)
            return
        message = messages[0]
        auth = user.social_auth.first()
        if not auth:
            self.stdout.write('User %s is not authenticated with Facebook' % user)
            return
        graph = facebook.GraphAPI(auth.extra_data['access_token'])
        graph.put_object(me, feed, message=message.message)
        message.publish_timestamp = datetime.datetime.now()
        message.save()
        self.stdout.write('Message posted successfully on behalf of %s: %s' % (user, message))

Now just add a cron entry that will trigger this command periodically, and you have your own private Facebook messaging scheduler! You can read your morning news, populate the FacebookStatus table with new entries, and focus on the more important tasks, while your Facebook scheduler will keep posting news to Facebook on your behalf. Isn\’t it cool?

In the next part of this sequence of tutorials, we will add some automation to populate the FacebookStatus from data sources you like and trust.

+ more