While working on a recent Django project I had the need to create thumbnails from images residing on a remote server to store within one of my Django app models.  The wrinkle was that I wanted to programmatically make the thumbnail "on the fly" before storage since I didn't want to waste space storing the original, much larger, image.  So if you are in a similar situation, where do you start?

Considering the vast array of libraries for Python, I hunted down the most referenced one, Python Imaging Library(PIL), and installed it. For purposes of this tutorial, I'll presuppose that you've already installed PIL on your platform and have some experience manipulating images using it.  I'm working on OS X (Snow Leopard) and had no issues getting PIL working but if you do, follow the directions on this blog post. (I can't help if you're on Windows) If you can run the command 'from PIL import Image' from the python prompt then you've installed it properly, Django shouldn't complain, and the code below should work.


The Thumbnail Model

Once you've installed PIL your hardest struggles are over. For our simplified example we'll create a custom image model with 'url' and 'thumb' attributes. The 'url' attribute will store the url of the image in the event you need to reference the original picture and 'thumb' will be an ImageField that stores the location of the thumbnail we create. We'll define a function called 'create_thumb' that will perform the image manipulation.   Here's what the Model looks like, including the required imports:

import Image
import os
import urllib
from django.core.files import File
.....
....
..

class Thumbnail(models.Model):
 url  =models.CharField(max_length=255, unique=True)
         
 # Set the upload_to parameter to the directory where you'll store the 
 # thumbs
 thumb = models.ImageField(upload_to='thumbs', null=true)
 
 """ Pulls image, converts it to thumbnail, then 
   saves in thumbs directory of Django install """
 def create_thumb(self):
  
  if self.url and not self.thumb:
   
   image = urllib.urlretrieve(self.url)
   
   # Create the thumbnail of dimension size
   size=128,128
   t_img = Image.open(image[0])
   t_img.thumbnail(size) 
 
   # Get the directory name where the temp image was stored
   # by urlretrieve
   dir_name = os.path.dirname(image[0])

   # Get the image name from the url
   img_name = os.path.basename(self.url)

   # Save the thumbnail in the same temp directory 
   # where urlretrieve got the full-sized image, 
   # using the same file extention in os.path.basename()
   t_img.save(os.path.join(dir_name, "thumb" + img_name))
 
   # Save the thumbnail in the media directory, prepend thumb  
   self.thumb.save(os.path.basename("thumb" + self.url),File(open(os.path.join(dir_name, "thumb" + img_name)))
 


What is that Code Doing? Can it be Improved?

Although the code is commented pretty well, I'll give a bit more explanation. In Django, the ImageField doesn't actually store the image in your database. Instead, it is stored in a directory located on the path your 'MEDIA_ROOT' is configured to in the settings.py file. So make sure that this appropriately configured, then create the 'thumbs' subdirectory within that directory. That's what the 'upload_to' parameter is used for in the ImageField type.

The 'create_thumb' method should be called when you create an instance of the Thumbnail model. Here's an example of one way you could use it:

a = Thumbnail(url='http://url.to.the.image.you.want.a.thumbnail.of')
a.create_thumb()
a.save()


The 'create_thumb' method takes the url, creates the thumbnail, and saves it in the 'upload_to' directory. Since this is sample code I didn't put any provision for catching exceptions such as improper url provided or image processing errors - that would be one area I'd suggest you improve upon. Also, the thumbnails are saved under the same name of the image provided by the url with "thumb" prepended. You might wonder what happens if two urls have the same image name? Well, I'll tell you: The new thumb will overwrite the old so you will want to add code that creates a unique name for the image.

Besides the few caveats mentioned above, the code works as advertised and will make your life that much easier...at least when it comes to creating thumbnails. As usual, I only ask that if this post helped you, please leave a comment.

3 comments:

Anonymous said...

Hello!

First, I have to say that this is very useful piece of code.
I'm quite new to Python and Django programming so I'm having trouble with the last line:

self.thumb.save(os.path.basename("thumb" + self.url),File(open(os.path.join(dir_name, "thumb" + img_name)))

Django returns an error: global name 'File' is not defined

I tried to fixed it, but somehow I didn't find a solution.
Pls help!

best regards!

Grega

John Banks said...

@Grega, the problem you're having is related to a statement missing from my example code, my apologies. I've fixed the example to include the proper import (just add it to the top of your file):

from django.core.files import File

Hope that helps

Anonymous said...

Thanks for this, works great !

Post a Comment

top