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"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | #-----
# Create a file resize_image.py:
#-----
# Adapted from: http://www.djangosnippets.org/snippets/636/
# ResizeImageField stands for ScalableAndRemovableImageField
from django import forms
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext as _
import os
import Image # from PIL
ORIGINAL_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, width
class DeleteCheckboxWidget(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)
def render(self, name, value, attrs=None):
value = value or self.value
if value:
attrs['style'] = 'width:auto;'
s = u'<label for="%s">%s %s</label>' % (
attrs['id'],
super(DeleteCheckboxWidget, self).render(name, False, attrs),
_('Delete')
)
if self.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))
return s
else:
return u''
class RemovableFileFormWidget(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)
def decompress(self, value):
return [None, value]
class RemovableFileFormField(forms.MultiValueField):
widget = RemovableFileFormWidget
field = forms.FileField
is_image = False
def __init__(self, *args, **kwargs):
fields = [self.field(*args, **kwargs), forms.BooleanField(required=False)]
# Compatibility with form_for_instance
if kwargs.get('initial'):
initial = kwargs['initial']
else:
initial = None
self.widget = self.widget(is_image=self.is_image, initial=initial)
super(RemovableFileFormField, self).__init__(fields, label=kwargs.pop('label'), required=False)
def compress(self, data_list):
return data_list
class ResizeImageFormField(RemovableFileFormField):
field = forms.ImageField
is_image = True
class ResizeImageField(models.ImageField):
def delete_file(self, instance, *args, **kwargs):
'''Overwrite delete method. Delete scaled instances as well.'''
if getattr(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.
if os.path.exists(file_name) and \
not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}).exclude(pk=instance._get_pk_val()):
if os.path.exists(file_name):
os.remove(file_name)
scaled_name = file_name.replace(ORIGINAL_NAME, SCALED_NAME, 1)
if os.path.exists(scaled_name):
os.remove(scaled_name)
thumb_name = file_name.replace(ORIGINAL_NAME, THUMB_NAME, 1)
if os.path.exists(thumb_name):
os.remove(thumb_name)
def get_internal_type(self):
'''Copied from Django snippet example and probably incorrect.'''
return 'FileField'
def check_or_create_dir(self, full_path):
'''Create dir if it does not yet exist.'''
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
os.makedirs(directory)
elif not os.path.isdir(directory):
raise IOError("%s exists and is not a directory." % directory)
def save_form_data(self, instance, data):
'''Save/replace or delete file. If saving, store scaled images as well.'''
if data and data[0]: # Replace file
self.delete_file(instance)
super(ResizeImageField, self).save_form_data(instance, data[0])
image = getattr(instance, '%s' % self.name)
file_path = image.path
img = Image.open(file_path)
self.resize(img, file_path, SCALED_NAME, SCALED_SIZE)
self.resize(img, file_path, THUMB_NAME, THUMB_SIZE)
if data and data[1]: # Delete file
self.delete_file(instance)
setattr(instance, self.name, None)
def resize(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)
def formfield(self, **kwargs):
'''Django default.'''
defaults = {'form_class': ResizeImageFormField}
defaults.update(kwargs)
return super(ResizeImageField, self).formfield(**defaults)
#-----
# In settings.py:
#-----
PHOTO_DIR = 'photos/original' # No trailing slash!
#-----
# Example models.py:
#-----
from django.db import models
from settings import PHOTO_DIR
from resize_image import ResizeImageField
class Address(models.Model):
photo = ResizeImageField(upload_to=PHOTO_DIR, blank=True)
#------
# templatetags/resize_filters.py:
#------
from django import template
register = template.Library()
from address.resize_image import ORIGINAL_NAME, SCALED_NAME, THUMB_NAME
@register.filter
def scaled(value):
return value.replace(ORIGINAL_NAME, SCALED_NAME, 1)
@register.filter
def thumb(value):
return value.replace(ORIGINAL_NAME, THUMB_NAME, 1)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 8 months ago
Comments
Looks like a nice idea, but can't seem to get this saving images in the right paths.
It should be uploading to /var/sites/dev/dev/media/uploads/images/ Changing settings of PHOTO_DIR made no difference at all. Python 2.6 / Django 1.2.2 Any ideas?
#
Please login first before commenting.