ImprovedImageField

 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
import os
import shutil
import Image
from django.db.models import ImageField, signals
from django.dispatch import dispatcher
from django.conf import settings
def fit(file_path, max_width, max_height):
if not (max_width or max_height):
# Can't resize
return
img = Image.open(file_path)
w, h = img.size
w = int(max_width or w)
h = int(max_height or h)
img.thumbnail((w, h), Image.ANTIALIAS)
img.save(file_path)
def rename(old_path, new_name):
"""
old_name is relative to MEDIA_ROOT
new_name is just base name, without extension
"""
if not os.path.isfile(old_path):
return old_path
path = os.path.dirname(old_path)
ext = os.path.splitext(old_path)[1]
# django wants to have '/' in path
new_path = '/'.join([path, new_name + ext])
def fp(path):
return os.path.join(settings.MEDIA_ROOT, path)
if new_path != old_path:
try:
shutil.move(fp(old_path), fp(new_path))
except IOError:
return old_path
return new_path
class ImprovedImageField(ImageField):
"""Allows model instance to specify following parameters dynamically:
- upload_to, specify following method for model:
def get_FIELD_upload_to(self):
return 'avatars/%d' % self.user.username
- filename (relative to upload_to, without extension):
def get_FIELD_filename(self):
return self.pk
Additionally field supports automatic resizing, if at least one of
max_width and max_height supplied.
Current flaws: upload_to must be specified as parameter (even if
custom method exist, although parameter can carry useless value).
Based on:
http://code.djangoproject.com/wiki/CustomUploadAndFilters
http://scottbarnham.com/blog/2007/07/31/uploading-images-to-a-dynamic-path-with-django/
Copyright (c) 2008 Alexander Solovyov under new BSD License.
"""
def __init__(self, max_width=None, max_height=None, **kwargs):
self.max_width, self.max_height = max_width, max_height
super(ImprovedImageField, self).__init__(**kwargs)
def contribute_to_class(self, cls, name):
"""Hook up events so we can access the instance."""
super(ImprovedImageField, self).contribute_to_class(cls, name)
dispatcher.connect(self._post_init, signals.post_init, sender=cls)
dispatcher.connect(self._resize, signals.post_save, sender=cls)
dispatcher.connect(self._rename, signals.pre_save, sender=cls)
def _post_init(self, instance=None):
"""Get dynamic upload_to value from the model instance."""
if hasattr(instance, 'get_%s_upload_to' % self.attname):
self.upload_to = getattr(instance, 'get_%s_upload_to' % self.attname)()
def _resize(self, instance):
real_path = os.path.join(settings.MEDIA_ROOT, getattr(instance, self.attname))
if os.path.isfile(real_path):
fit(real_path, self.max_width, self.max_height)
def _rename(self, instance):
if hasattr(instance, 'get_%s_filename' % self.attname):
filename = getattr(instance, 'get_%s_filename' % self.attname)()
new_path = rename(getattr(instance, self.attname), filename)
setattr(instance, self.attname, new_path)
def db_type(self):
"""Required by Django for ORM."""
return 'varchar(100)'