I’ve been using Scott’s CustomImageField for quite some time, but recently it stopped working when I moved to Newforms Admin. I wasn’t really sure why this was, but decided to build a quick workaround for now so I can continue to use the newforms-admin.
Scott’s Field used the post_init and pre_save signals, which is probably the right way… Since this wasn’t working anymore, I decided to use the post_save signal. This way the image or file would be saved to a temporary directory initially and then moved to the appropriate directory afterwards.
Here is how the field would be defined in the model:
class Image(models.Model):
file = CustomImageField(use_key=True, upload_to='tmp')
use_key and upload_to are optional. use_key defaults to False. If it is True then the id of the instance will be used as a prefix for the new file as there is the potential for overwriting now that we are moving the file. upload_to will simply define the temporary directory to upload the files to initially.
Below is the code that defines the CustomImageField. I have this in a file called “custom_fields.py.”
from django.db.models import ImageField, FileField, signals
from django.dispatch import dispatcher
from django.conf import settings
import shutil, os, glob, re
from distutils.dir_util import mkpath
class CustomImageField(ImageField):
"""Allows model instance to specify upload_to dynamically.
Model class should have a method like:
def get_upload_to(self, attname):
return 'path/to/%d' % self.id
Based closely on: http://scottbarnham.com/blog/2007/07/31/uploading-images-to-a-dynamic-path-with-django/
"""
def __init__(self, *args, **kwargs):
if not 'upload_to' in kwargs:
kwargs['upload_to'] = 'tmp'
self.use_key = kwargs.get('use_key', False)
if 'use_key' in kwargs:
del(kwargs['use_key'])
super(CustomImageField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
"""Hook up events so we can access the instance."""
super(CustomImageField, self).contribute_to_class(cls, name)
dispatcher.connect(self._move_image, signal=signals.post_save, sender=cls)
def _move_image(self, instance=None):
"""
Function to move the temporarily uploaded image to a more suitable directory
using the model's get_upload_to() method.
"""
if hasattr(instance, 'get_upload_to'):
src = getattr(instance, self.attname)
if src:
m = re.match(r"%s/(.*)" % self.upload_to, src)
if m:
if self.use_key:
dst = "%s/%d_%s" % (instance.get_upload_to(self.attname), instance.id, m.groups()[0])
else:
dst = "%s/%s" % (instance.get_upload_to(self.attname), m.groups()[0])
basedir = "%s%s/" % (settings.MEDIA_ROOT, os.path.dirname(dst))
mkpath(basedir)
shutil.move("%s%s" % (settings.MEDIA_ROOT, src),"%s%s" % (settings.MEDIA_ROOT, dst))
setattr(instance, self.attname, dst)
instance.save()
def db_type(self):
"""Required by Django for ORM."""
return 'varchar(100)'