ResizeImageField

ResizeImageField

(extension of RemovableImageField)

by Wim Feijen, Go2People.

What does it do?

ResizeImageField is a replacement for django's ImageField. It has two major benefits:
1. Creation of thumbnails and scaled images.
1. Extends the image upload form and adds a preview and a checkbox to remove the existing image.

It's easy to use:
- Replace ImageField by ResizeImageField
- No further changes are necessary

Requirements:

Working installation of PIL, the Python Imaging Library

Usage

add resize_image to your app

add resize_filters.py to your templatetags

in settings.py, set a PHOTO_DIR, f.e. photos/original

in models.py, add:

from settings import PHOTO_DIR

from resize_image import ResizeImageField

photo = ResizeImageField(upload_to=PHOTO_DIR, blank=True)

Scaled images will be stored in 'photos/scaled',
thumbnails will be stored in 'photos/thumb'.

Access your images from your template. Add::

{% load resize_filters %}
{{ address.photo.url|thumb }}

or::

{{ address.photo.url|scaled }}

Defaults

Scaled images are max. 200x200 pixels by default

Thumbnails are 50x50 pixels.

Override the default behaviour in settings.py

Scaling is done by PIL's thumbnail function, transparency is conserved.

Credits

This code is an adaptation from python snippet 636 by tomZ: "Updated Filefield / ImageField with a delete checkbox"

#-----# Create a file resize_image.py:#-----# Adapted from: http://www.djangosnippets.org/snippets/636/# ResizeImageField stands for ScalableAndRemovableImageFieldfromdjangoimportformsfromdjango.confimportsettingsfromdjango.dbimportmodelsfromdjango.utils.translationimportugettextas_importosimportImage# from PILORIGINAL_NAME=os.path.basename(settings.PHOTO_DIR)SCALED_NAME=getattr(settings,'SCALED_NAME','scaled')THUMB_NAME=getattr(settings,'THUMB_NAME','thumb')SCALED_SIZE=getattr(settings,'SCALED_SIZE',(200,200))THUMB_SIZE=getattr(settings,'SCALED_SIZE',(50,50))# height, widthclassDeleteCheckboxWidget(forms.CheckboxInput):def__init__(self,*args,**kwargs):self.is_image=kwargs.pop('is_image')self.value=kwargs.pop('initial')super(DeleteCheckboxWidget,self).__init__(*args,**kwargs)defrender(self,name,value,attrs=None):value=valueorself.valueifvalue:attrs['style']='width:auto;'s=u'<label for="%s">%s%s</label>'%(attrs['id'],super(DeleteCheckboxWidget,self).render(name,False,attrs),_('Delete'))ifself.is_image:s+=u'<br><img src="%s%s" height="100">'%(settings.MEDIA_URL,unicode(value).replace(ORIGINAL_NAME,SCALED_NAME,1))else:s+=u'<br><a href="%s%s">%s</a>'%(settings.MEDIA_URL,value,os.path.basename(value))returnselse:returnu''classRemovableFileFormWidget(forms.MultiWidget):def__init__(self,is_image=False,initial=None,**kwargs):widgets=(forms.FileInput(),DeleteCheckboxWidget(is_image=is_image,initial=initial))super(RemovableFileFormWidget,self).__init__(widgets)defdecompress(self,value):return[None,value]classRemovableFileFormField(forms.MultiValueField):widget=RemovableFileFormWidgetfield=forms.FileFieldis_image=Falsedef__init__(self,*args,**kwargs):fields=[self.field(*args,**kwargs),forms.BooleanField(required=False)]# Compatibility with form_for_instanceifkwargs.get('initial'):initial=kwargs['initial']else:initial=Noneself.widget=self.widget(is_image=self.is_image,initial=initial)super(RemovableFileFormField,self).__init__(fields,label=kwargs.pop('label'),required=False)defcompress(self,data_list):returndata_listclassResizeImageFormField(RemovableFileFormField):field=forms.ImageFieldis_image=TrueclassResizeImageField(models.ImageField):defdelete_file(self,instance,*args,**kwargs):'''Overwrite delete method. Delete scaled instances as well.'''ifgetattr(instance,self.attname):image=getattr(instance,'%s'%self.name)file_name=image.path# If the file exists and no other object of this type references it,# delete it from the filesystem.ifos.path.exists(file_name)and \
notinstance.__class__._default_manager.filter(**{'%s__exact'%self.name:getattr(instance,self.attname)}).exclude(pk=instance._get_pk_val()):ifos.path.exists(file_name):os.remove(file_name)scaled_name=file_name.replace(ORIGINAL_NAME,SCALED_NAME,1)ifos.path.exists(scaled_name):os.remove(scaled_name)thumb_name=file_name.replace(ORIGINAL_NAME,THUMB_NAME,1)ifos.path.exists(thumb_name):os.remove(thumb_name)defget_internal_type(self):'''Copied from Django snippet example and probably incorrect.'''return'FileField'defcheck_or_create_dir(self,full_path):'''Create dir if it does not yet exist.'''directory=os.path.dirname(full_path)ifnotos.path.exists(directory):os.makedirs(directory)elifnotos.path.isdir(directory):raiseIOError("%s exists and is not a directory."%directory)defsave_form_data(self,instance,data):'''Save/replace or delete file. If saving, store scaled images as well.'''ifdataanddata[0]:# Replace fileself.delete_file(instance)super(ResizeImageField,self).save_form_data(instance,data[0])image=getattr(instance,'%s'%self.name)file_path=image.pathimg=Image.open(file_path)self.resize(img,file_path,SCALED_NAME,SCALED_SIZE)self.resize(img,file_path,THUMB_NAME,THUMB_SIZE)ifdataanddata[1]:# Delete fileself.delete_file(instance)setattr(instance,self.name,None)defresize(self,img,file_path,new_name,new_size):'''Resize image, using PIL, and save.'''new_path=file_path.replace(ORIGINAL_NAME,new_name,1)self.check_or_create_dir(new_path)img.thumbnail(new_size,Image.ANTIALIAS)try:transparency=img.info['transparency']img.save(new_path,transparency=transparency)except:img.save(new_path)defformfield(self,**kwargs):'''Django default.'''defaults={'form_class':ResizeImageFormField}defaults.update(kwargs)returnsuper(ResizeImageField,self).formfield(**defaults)#-----# In settings.py:#----- PHOTO_DIR='photos/original'# No trailing slash!#-----# Example models.py:#-----fromdjango.dbimportmodelsfromsettingsimportPHOTO_DIRfromresize_imageimportResizeImageFieldclassAddress(models.Model):photo=ResizeImageField(upload_to=PHOTO_DIR,blank=True)#------# templatetags/resize_filters.py:#------fromdjangoimporttemplateregister=template.Library()fromaddress.resize_imageimportORIGINAL_NAME,SCALED_NAME,THUMB_NAME@register.filterdefscaled(value):returnvalue.replace(ORIGINAL_NAME,SCALED_NAME,1)@register.filterdefthumb(value):returnvalue.replace(ORIGINAL_NAME,THUMB_NAME,1)