Integrating Flickr With Django
Seeing as how flickr photostreams are the hot thing in blogs these days, I figured I'd share one way of adding your public flickr photostream to your Django project. All that's needed is a model with four or five strings and a few functions to do the synchronization and URL building. The synchronization method only adds photos in your flickr stream that don't exist in your database. When it hits the first dupe in your chronological photostream, it stops. You can modify the synchronization method to allow two-way synchronization or more robust synchronization that doesn't stop when it hits the first flickr photo that already exists in your database.
Before we start, you'll need the following flickr information:
- API key
- Your user id. This looks something like "12345678@N00".
I organized my project like so, but you can set up yous any way you want, as long as you modify my code samples to match.
project/ __init__.py apps/ __init__.py photos/ __init__.py models.py ... lib/ __init__.py templates/ manage.py settings.py urls.py
You'll need Michele Campeotto's excellent FlickrClient libraries, available for download here. Unzip it and place "FlickrClient.py" and "xmltramp.py" in your "lib" folder (or wherever you want to store them. Storing them inside your app is fine, too.):
project/ ... lib/ __init__.py FlickrClient.py xmltramp.py ...
Knowing only a few bits of flickr's data for your photos allows you a wide array of options when creating URLs. You can specify the photo size, create links to the photo's flickr page, etc. You can read more about the URLs on flickr's URL documentation page. This makes our model a snap. We only have to store four small strings of flickr data in our model, or five if you want to include the title, which I do here.
You can set up your model in most any way, as long as you have the four flickr-specific models. Here's how my "photos/models.py" code looks:
from django.db import models from project.lib.FlickrClient import FlickrClient class Photo(models.Model): title = models.CharField(blank=True, maxlength=100) flickr_id = models.IntegerField() flickr_owner = models.CharField(maxlength=20) flickr_server = models.IntegerField() flickr_secret = models.CharField(maxlength=50) class Admin: list_display = ('title',) def __str__(self): return self.title def get_absolute_url(self): return "/photos/%s/" % (self.id)
Easy enough so far, right? Now, we'll need the actual synchronization code. Add the following function to your "photos/models.py" file, adding your flickr key and user ID where needed:
def sync_flickr_photos(*args, **kwargs): API_KEY = 'INSERT API KEY' USER_ID = 'INSERT USER ID' cur_page = 1 # Start on the first page of the stream paginate_by = 20 # Get 20 photos at a time dupe = False # Set our dupe flag for the following loop client = FlickrClient(API_KEY) # Get our flickr client running while (not dupe): photos = client.flickr_people_getPublicPhotos(user_id=USER_ID, page=cur_page, per_page=paginate_by) for photo in photos: try: row = Photo.objects.get(flickr_id=photo("id"), flickr_secret=str(photo("secret"))) # Raise exception if photo doesn't exist in our DB yet # If the row exists already, set the dupe flag dupe = True except ObjectDoesNotExist: p = Photo( title = str(photo("title")), flickr_id = int(photo("id")), flickr_owner = str(photo("owner")), flickr_server = int(photo("server")), flickr_secret = str(photo("secret")), ) p.save() if (dupe or photos("page") == photos("pages")): # If we hit a dupe or if we did the last page... break else: cur_page += 1
(Note: I know there's probably a million better ways to do this (esp. in regards to raising exceptions on "success"). You'll also want to include proper error catching. I'm no Python expert yet, so I took the easy route. Feel free to improve the code, but keep it simple.
I'll leave the rest of this up to you:
- You'll need to run the sync_flickr_photos function at a reasonable interval. Don't flood flickr with API calls every time someone views your page. For instance, I use signals and the dispatcher to raise a signal whenever someone visits my photos page. If it's been more than 15 minutes since the last time I synchronized, I run the function.
- To actually do anything useful with your flickr information, you'll need to create methods that build the URLs. Here's how I do it:
def get_pic_url(self, size='small'): # small_square=75x75 # thumb=100 on longest side # small=240 on longest side # medium=500 on longest side # large=1024 on longest side # original=duh base_url = "http://static.flickr.com" size_char='s' # default to small_square if size == 'small_square': size_char='_s' elif size == 'thumb': size_char='_t' elif size == 'small': size_char='_m' elif size == 'medium': size_char='' elif size == 'large': size_char='_b' elif size == 'original': size_char='_o' return "%s/%s/%s_%s%s.jpg" % (base_url, self.flickr_server, self.flickr_id, self.flickr_secret, size_char)
- And of course, you'll have to do all the views and templates yourself. But that's the easy part!