ResizeImageField

  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

  1. ImageWithThumbsField by zenx 5 years, 3 months ago
  2. UPDATED: Django Image Thumbnail Filter by danfairs 6 years, 5 months ago
  3. Dynamic thumbnail generator by semente 6 years, 1 month ago
  4. Image model with thumbnail by clawlor 3 years, 9 months ago
  5. Add a "remove file" field for Image- or FileFields by rodrigoc 5 years, 9 months ago

Comments

Meza (on November 19, 2010):

Looks like a nice idea, but can't seem to get this saving images in the right paths.

IOError at /admin/news/article/1469/

[Errno 2] No such file or directory: u'/var/sites/dev/dev/media/testimage.jpg'

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?

#

(Forgotten your password?)