Beardy Geek – Web Development Blog

A blog about web development – giving tips and tutorials about all aspects of web dev.

Beardy Geek – Web Development Blog

Creating User Profiles with Image Thumbnails in Django

I am currently working on a django application that will include a profile page for each user. Like many web apps, it will have the ability to upload a profile picture, and have this picture shrunk to a suitable thumbnail size.

To complicate matters, I want the photo uploaded to a directory which will be named after the user.

I’m fairly new to Django, so it took some time to piece this all together, but someone out there may have had the same problems and I hope this post helps.


You must have the Python Imaging Library installed. You can get this from the PIL homepage.

User Profiles

The first thing we need to do is create a model for our user profile.

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    nickname = models.CharField(max_length=50, blank=True, null=True)
    photo = models.ImageField(upload_to='profile_pics', blank=True, null=True)
    thumbnail = models.ImageField(upload_to='profile_thumb', blank=True, null=True,

I’ve made the upload_to attributes static for the time being, but we will be changing this later.

User Profile Setup

So that Django knows which model to use for you user profile, you have to add a setting to your settings file.

AUTH_PROFILE_MODULE = 'appname.UserProfile'

Make sure that this uses your app name, not your project name. I’ve used UserProfile for the model, but you can call it whatever you like.

Dynamic Upload Directory

To make the upload_to attribute dynamic based on the current user, we have to write a small function that returns the appropriately formed path. My thanks to Josh Ourisman and his post for this part.

Create a file called and save it in your application directory.

def get_profile_path(instance, filename)
    dir = "%s/profile_pics/%s" % (instance.user, filename)
    return dir

This will make the profile picture save in the directory ‘username/profile_pics/filename’. You can move these around if you want a different directory structure. The MEDIA_ROOT value from your settings file will be prepended to this path automatically, so make sure you set that.

Amend the Model

Now back to our model, where we call our new function from the upload_to attribute.

from projname.appname.files import get_profile_path

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    nickname = models.CharField(max_length=50, blank=True, null=True)
    photo = models.ImageField(upload_to='get_profile_path', blank=True, null=True)
    thumbnail = models.ImageField(upload_to='profile_thumb', blank=True, null=True,

We will leave the thumbnail upload_to field, as we will be dealing with that one manually.

Creating the View

I wanted to just create one view that would handle creating a new profile, and displaying and updating an existing profile. First I’ll create a form to display in the view.

from django import forms
from django.forms import ModelForm
from projname.appname.models import UserProfile

class UserProfileForm(ModelForm):
    class Meta:
        model = UserProfile
        exclude = ('user')

Now to the view

from django.shortcuts import render_to_response
from django.template import RequestContext
from import UserProfileForm
from import UserProfile

from django.contrib.auth.decorators import login_required

def profile(request):
        myprofile = request.user.get_profile()
        up = UserProfile(user=request.user)
        myprofile = request.user.get_profile()

    if request.method == 'POST':
        f = UserProfileForm(request.POST, request.FILES, instance=myprofile)
        if f.is_valid():
        f = UserProfileForm(instance=myprofile)

    return render_to_response('appname/profile.html', {'f':f, 'profile':myprofile},
        context_instance = RequestContext(request))

This view will try and retrieve the user profile based on the current logged in user. If the user profile doesn’t exist, then an exception is thrown. If this happens, we create a profile with just the username and call get_profile() again. We then deal either with a POST event, or we bind the retrieved profile instance to the form ready for updating by the user.

If you don’t want to create thumbnails of your images, you can leave it here. The images will have been loaded

Thumbnail Creation

To create the thumbnail, we now need to override the save def in our model.

def save(self, force_insert=False, force_update=False):
        #get mtime stats from file
        thumb_update = False

        if self.thumbnail:
            statinfo1 = os.stat(
            statinfo2 = os.stat(self.thumbnail.path)
            if statinfo1 > statinfo2:
                thumb_update = True

        if and not self.thumbnail or thumb_update:
            from PIL import Image

            THUMB_SIZE = (150, 150)

            #self.thumbnail =

            image =

            if image.mode not in ('L', 'RGB'):
                image = image.convert('RGB')

            image.thumbnail(THUMB_SIZE, Image.ANTIALIAS)
            (head, tail) = os.path.split(
            (a, b) = os.path.split(

            if not os.path.isdir(head + '/thumbs'):
                os.mkdir(head + '/thumbs')

   + '\\thumbs\\' + tail)

            self.thumbnail = a + '/thumbs/' + b

        super(UserProfile, self).save()

First we set a thumb_update var to false. Then, if the thumbnail exists, we check to see if the timestamp on the original image is newer than the existing thumbnail. If we didn’t check for this, then when we changed the image, we wouldn’t know whether or not to create the thumbnail.

The rest of the code deals with using the Imaging Library to resize the image. It also creates a ‘thumbs’ directory if one doesn’t already exist, saves the image, and updates the ‘thumbnail’ field in the model.


I hope this has been helpful. Like I said, I haven’t been using Django or Python for more than a few weeks, so if anyone has any suggestions for improving this code, then please comment.