I'm new to Django (and Python) and I have been trying to work out a few things myself, before jumping into using other people's apps. I'm having trouble understanding where things 'fit' in the Django (or Python's) way of doing things. What I'm trying to work out is how to resize an image, once it's been uploaded. I have my model setup nicely and plugged into the admin, and the image uploads fine to the directory:
from django.db import models # This is to list all the countries # For starters though, this will be just United Kingdom (GB) class Country(models.Model): name = models.CharField(max_length=120, help_text="Full name of country") code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)") flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True) class Meta: verbose_name_plural = "Countries" def __unicode__(self): return self.name
The thing I'm now having trouble with is taking that file and making a new file into a thumbnail. Like I say, I'd like to know how to do it without using others' apps (for now). I have got this code from DjangoSnippets:
from PIL import Image import os.path import StringIO def thumbnail(filename, size=(50, 50), output_filename=None): image = Image.open(filename) if image.mode not in ('L', 'RGB'): image = image.convert('RGB') image = image.resize(size, Image.ANTIALIAS) # get the thumbnail data in memory. if not output_filename: output_filename = get_default_thumbnail_filename(filename) image.save(output_filename, image.format) return output_filename def thumbnail_string(buf, size=(50, 50)): f = StringIO.StringIO(buf) image = Image.open(f) if image.mode not in ('L', 'RGB'): image = image.convert('RGB') image = image.resize(size, Image.ANTIALIAS) o = StringIO.StringIO() image.save(o, "JPEG") return o.getvalue() def get_default_thumbnail_filename(filename): path, ext = os.path.splitext(filename) return path + '.thumb.jpg'
...but this has ultimately confused me... As I don't know how this 'fits in' to my Django app? And really, is it the best solution for simply making a thumbnail of an image that has been successfully uploaded? Can anyone possibly show me a good, solid, decent way that a beginner like me can learn to do this properly? As in, knowing where to put that sort of code (models.py? forms.py? ...) and how it would work in context? ... I just need a bit of help understanding and working this problem out.
This is what I use in my models to save a new thumbnail if the uploaded image has changed. It's based of another DjangoSnippet but it I can't remember who wrote the orginal - if you know please add a comment so that I can credit them.
from PIL import Image from django.db import models from django.contrib.auth.models import User import os import settings class Photo_Ex(models.Model): user = models.ForeignKey(User, blank=True, null=True) photo = models.ImageField(upload_to='photos') thumbnail = models.ImageField(upload_to='profile_thumb', blank=True, null=True, editable=False) def save(self, *args, **kwargs): size = (256,256) if not self.id and not self.photo: return try: old_obj = Photo_Ex.objects.get(pk=self.pk) old_path = old_obj.photo.path except: pass thumb_update = False if self.thumbnail: try: statinfo1 = os.stat(self.photo.path) statinfo2 = os.stat(self.thumbnail.path) if statinfo1 > statinfo2: thumb_update = True except: thumb_update = True pw = self.photo.width ph = self.photo.height nw = size nh = size if self.photo and not self.thumbnail or thumb_update: # only do this if the image needs resizing if (pw, ph) != (nw, nh): filename = str(self.photo.path) image = Image.open(filename) pr = float(pw) / float(ph) nr = float(nw) / float(nh) if image.mode not in ('L', 'RGB'): image = image.convert('RGB') if pr > nr: # photo aspect is wider than destination ratio tw = int(round(nh * pr)) image = image.resize((tw, nh), Image.ANTIALIAS) l = int(round(( tw - nw ) / 2.0)) image = image.crop((l, 0, l + nw, nh)) elif pr < nr: # photo aspect is taller than destination ratio th = int(round(nw / pr)) image = image.resize((nw, th), Image.ANTIALIAS) t = int(round(( th - nh ) / 2.0)) image = image.crop((0, t, nw, t + nh)) else: # photo aspect matches the destination ratio image = image.resize(size, Image.ANTIALIAS) image.save(self.get_thumbnail_path()) (a, b) = os.path.split(self.photo.name) self.thumbnail = a + '/thumbs/' + b super(Photo_Ex, self).save() try: os.remove(old_path) os.remove(self.get_old_thumbnail_path(old_path)) except: pass def get_thumbnail_path(self): (head, tail) = os.path.split(self.photo.path) if not os.path.isdir(head + '/thumbs'): os.mkdir(head + '/thumbs') return head + '/thumbs/' + tail def get_old_thumbnail_path(self, old_photo_path): (head, tail) = os.path.split(old_photo_path) return head + '/thumbs/' + tail