<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=338168267432629&amp;ev=PageView&amp;noscript=1">
Programming

Bixly How-To: Integrate Django with Flickr

Flickr is one of the most popular photo management websites in the world. It can host videos as well. Flickr also provides a rich API which programmers can use to access the services Flickr provides to all its users. Several API kits for different languages are available for the developers. …


Flickr is one of the most popular photo management websites in the world. It can host videos as well. Flickr also provides a rich API which programmers can use to access the services Flickr provides to all its users.

Several API kits for different languages are available for the developers. These include Flickcurl for C, flickrj for Java, Flickr.NET for .NET, phpFlickr for PHP, flickraw for Ruby, and so on. Since Django is a Python Web framework, we need a Flickr API kit for Python. The easiest to use Python interface to Flickr is Beej’s Python Flickr API. There are other API kits available for Python but Beej’s Python Flickr API proves to have the best features and best documentation.

Step 1: We start off with the Flickr integration by applying for an API Key and Secret. We apply for a  non-commercial key since we are just creating an application for this tutorial.

Step 2: We then add some basic information regarding our application.

Step 3: We obtain the API Key and Secret for our application(3.png). We store them as FLICKR_API_KEY and FLICKR_API_SECRET in our settings.py respectively.

Step 4: We then go to the Edit Authentication Flow page. We set the callback url to http://localhost:8000/flickr-callback. This is the url where users go after they authenticate to Flickr.

 

Step 5: We create the appriopriate page to catch the authentication callback. Note that the callback will include the parameter frob which can be used to obtain the user access token.

http://localhost:8000/flickr-callback?frob=XXXXXXXXXXXXXX-XXXXXXXXXXXXXX-XXXXX

We obtain the frob value from the URL parameters, obtain the user’s access token from the frob value, and store the access token in the session. We could also store the access token in the database. The code for views.py is shown below.

from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from settings import FLICKR_API_KEY, FLICKR_API_SECRET
import flickrapi

def index(request):
	if 'flickr_token' in request.session:
		token = True
	else:
		flickr = flickrapi.FlickrAPI(
			FLICKR_API_KEY, 
			FLICKR_API_SECRET)
		flickr.get_token_part_one(
			perms='write')	
		token = False

	context = {
		'token': token
		}
	return render_to_response(
			'index.html',
			context,
			context_instance=RequestContext(request))

def flickr_callback(request):
	if not 'flickr_token' in request.session:
		f = flickrapi.FlickrAPI(
			FLICKR_API_KEY, 
			FLICKR_API_SECRET, 
			store_token=False)
		frob = None
		token = None	
		try:
			frob = request.GET['frob']
			token = f.get_token(frob)
			request.session['flickr_token'] = token		
		except Exception:
			pass

	return redirect('index')

The code above first checks if a flickr token is stored in the session. If not, it requires the user to authenticate and finally redirect to the index page. Otherwise, it will continue normally with token having the value of True.

Note that when you have the user’s access token, you are provided several rights to his or her account depending on the permissions allowed by the user.

Step 6: Next, we download the official Python interface to the Flickr API. We download the flickrapi-X.X.X.zip file. We extract the flickrapi folder contained inside it to our project root directory.

Step 7: Now, we are ready to use the Flickr API. To test our installation, we start the interactive Python interpreter with python manage.py shell.

import flickrapi
api_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXX'
flickr = flickrapi.FlickrAPI(api_key)
sets = flickr.photosets_getList(user_id='73509078@N00')
import xml.etree.ElementTree as xml
xml.dump(sets)

We use xml.dump to display the contents of the xml element. The response XML of the Flickr API is returned as an ElementTree element. We demonstrate some of the parsing abilities of the ElementTree with the following examples.

sets.attrib['stat'] 
#ok

sets0 = sets.find('photosets').findall('photoset')[0] 
#we get the first photoset element stored in sets0

xml.dump(sets0)
"""
<photoset 
	farm="6" 
	id="72157626330076643" 
	photos="16" 
	primary="5600258215" 
	secret="e6c7026d26" 
	server="5149" 
	videos="0">
	<title>Reiki-weekend</title>
	<description>&lt;a 
	href="http://www.lighthouseworks.nl/" 	
	rel="nofollow"&gt;www.lighthouseworks.nl/&lt;/a&gt;
	</description>
</photoset>
"""
sets0.attrib['id']
#'72157626330076643'

sets0.attrib['farm']
#'6'

sets0.attrib['photos']
#'16'

It’s also possible to return the responses of the Flickr API as json. We do this using f = flickrapi.FlickrAPI(api_key, format=’json’). the variable sets will contain a json string after calling sets = flickr.photosets_getList(user_id=’73509078@N00’)

Step 8: You may have noticed that we didn’t use the API secret in the last example. It’s because the API secret is required for calls which require the application to be authenticated. In other words, the app user needs to tell Flickr that the application is given access to his or her account.

In the Python interpreter, we test Flickr’s two-phase authentication.

import filckrapi
api_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXX'
api_secret = 'XXXXXXXXXXXXXXXXXXX'

flickr = flickrapi.FlickrAPI(api_key, api_secret)
(token, frob) = flickr.get_token_part_one(perms='write')
#triggers a new popup or new window

flickr.get_token_part_one() first checks the on-disk token cache if the application has already been authenticated. If it isn’t, an new Flick page is opened where the access priviledges are granted by the user.

flickr.get_token_part_two((token, frob)) stores the token in the cache and remembers it for future API calls

Step 9: The problem with our implementation of Flickr authentication in views.py is that it requires a new window to be opened just to proceed with the authentication process. Another problem is that the Flick authentication process leaves repetitive code when required in several pages. The solution to these problems is to create a custom Django view decorator which handles most of the authentication process. Notice that the code is cleaner and it only requires the view to have @require_flickr_auth decorator to ensure that the user is connected to Flickr.

from settings import FLICKR_API_KEY, FLICKR_API_SECRET
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
import flickrapi

def require_flickr_auth(view):
	def protected_view(request, *args, **kwargs):
		if 'token' in request.session:
			token = request.session['token']			
		else:
			token = None			

		f = flickrapi.FlickrAPI(FLICKR_API_KEY,
			FLICKR_API_SECRET, 
			token=token,
			store_token=False)

		if token:
			#check validity of the token
			try:
				f.auth_checkToken()
			except flickrapi.FlickrError:
				#set invalid token to None
				token = None
				del request.session['token']

		if not token:
			# Redirect to Flickr for authentication 
			# if no valid token
			url = f.web_login_url(perms='write')
			return redirect(url)

		return view(request, *args, **kwargs)

	return protected_view

def flickr_callback(request):
	f = flickrapi.FlickrAPI(FLICKR_API_KEY,
		FLICKR_API_SECRET, 
		store_token=False)

	frob = request.GET['frob']
	token = f.get_token(frob)
	request.session['token'] = token

	return redirect('index')

def flickr(request):
	token = request.session['token']			

	f = flickrapi.FlickrAPI(FLICKR_API_KEY,
		FLICKR_API_SECRET, token=token,
		store_token=False)

	return f

@require_flickr_auth
def index(request):		
	f = flickr(request)
	context = {}
	return render_to_response('index.html',
		context,
		context_instance=RequestContext(request))

Step 10: We now try to display all of the user’s photos in each of his or her photosets in succession. We then create wrapper functions getPhotos() and getPhotosets(). The function getPhotos returns all the photos of a specified photoset via photoset ID. The function getPhotosets returns all the photosets of the user. The wrapper functions are shown below. Notice that we used attrib, find, and findall just like in our test earlier. These wrapper functions parse through the contents of the returned ElementTree element and returns only the needed data as a list.

def getPhotosets(flickr):
	photoset_list = flickr.photosets_getList()
		.find('photosets')
		.findall('photoset')
	photoset_list_array = []
	for photoset in photoset_list:
		photoset_id = photoset.attrib['id']
		photoset_title = photoset.find('title').text
		photoset_list_array.append({
			'id':photoset_id,
			'name':photoset_title})
	return photoset_list_array

def getPhotos(flickr,id):
	photoset_photos = flickr
		.photosets_getPhotos(photoset_id=id)
		.find('photoset').findall('photo')
	photoset_photos_list = []
	for photo in photoset_photos:
		photo_id = photo.attrib['id']
		secret = photo.attrib['secret']
		farm = photo.attrib['farm']
		server = photo.attrib['server']
		title = photo.attrib['title']
		photo_url = 
		'http://farm%s.static.flickr.com/%s/%s_%s.jpg' 
			% (farm,server,photo_id,secret)
		photoset_photos_list.append(photo_url)
	return photoset_photos_list

getPhotosets() returns all the names and IDs of all the photosets of the user in a list of the form [{‘id’:’photoset_id1’,’name’:’photoset_title1’},{‘id’:’photoset_id2’,’name’:’photoset_title2’},{‘id’:’photoset_id3’,’name’:’photoset_title3’}]

getPhotos() on the other hand returns all urls of each of the photos of a specifed photoset. It returns a list of the [“url1”,”url2”,”url3”]

We modify views.py some more to include getPhotosets() and getPhotos(). The code for views.py is shown below.

from settings import FLICKR_API_KEY, FLICKR_API_SECRET
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
import flickrapi

def require_flickr_auth(view):
	def protected_view(request, *args, **kwargs):
		if 'token' in request.session:
			token = request.session['token']			
		else:
			token = None			

		f = flickrapi.FlickrAPI(FLICKR_API_KEY,
			FLICKR_API_SECRET, token=token,
			store_token=False)

		if token:
			#check validity of the token
			try:
				f.auth_checkToken()
			except flickrapi.FlickrError:
				#set invalid token to None
				token = None
				del request.session['token']

		if not token:
			# Redirect to Flickr for authentication 
			# if no valid token
			url = f.web_login_url(perms='write')
			return redirect(url)

		return view(request, *args, **kwargs)

	return protected_view

def flickr_callback(request):
	f = flickrapi.FlickrAPI(FLICKR_API_KEY,
		   FLICKR_API_SECRET, store_token=False)

	frob = request.GET['frob']
	token = f.get_token(frob)
	request.session['token'] = token

	return redirect('index')

def flickr(request):
	token = request.session['token']			

	f = flickrapi.FlickrAPI(FLICKR_API_KEY,
		FLICKR_API_SECRET, token=token,
		store_token=False)

	return f

def getPhotosets(flickr):
	photoset_list = flickr.photosets_getList()
		.find('photosets')
		.findall('photoset')
	photoset_list_array = []
	for photoset in photoset_list:
		photoset_id = photoset.attrib['id']
		photoset_title = photoset.find('title').text
		photoset_list_array.append({
			'id':photoset_id,
			'name':photoset_title})
	return photoset_list_array

def getPhotos(flickr,id):
	photoset_photos = flickr.photosets_getPhotos(photoset_id=id)
		.find('photoset').findall('photo')
	photoset_photos_list = []
	for photo in photoset_photos:
		photo_id = photo.attrib['id']
		secret = photo.attrib['secret']
		farm = photo.attrib['farm']
		server = photo.attrib['server']
		title = photo.attrib['title']
		photo_url = 
		'http://farm%s.static.flickr.com/%s/%s_%s.jpg' 
			% (farm,server,photo_id,secret)
		photoset_photos_list.append(photo_url)
	return photoset_photos_list

@require_flickr_auth
def index(request):
	f = flickr(request)
	photosets = getPhotosets(f)
	output = []
	for photoset in photosets:
		photos = getPhotos(f,photoset['id'])
		output.append({
			'album_name':photoset['name'],
			'photo_list': photos})

	context = {'output':output}
	return render_to_response(
			'index.html',
			context,
			context_instance=RequestContext(request))

Finally, the template page should look like this:

 

The sample Flickr integration app we created shows all of the user’s photos in succession. There are options in the Flickr API which can limit the number of results returned by the request. There is a lot more to Flickr API and Beej’s Python Flickr API than what I have described here. Be sure to check out the documentation for all the rest of the features.

 

References:

 

Similar posts

Get notified about the latest in Tech

Be the first to know about new tech from the experts at Bixly!