toothie
toothie

Reputation: 1029

Overiding save for just one field in Django

This is my models.py:

class College(models.Model):
  name = models.CharField(unique=True, max_length=50, 
    help_text='Name of the college.'
  )
  slug = models.SlugField(unique=True)
  description = models.TextField(blank = True)
  image = models.ImageField(upload_to='site-media/media/college_images/', 
    default = 'site-media/media/college_images/default.jpeg' 
  )
  user = models.ForeignKey(User)


  def get_absolute_url(self):
    return "/%s/" % self.slug

  def create_thumbnail(self):
    if not self.image:
        return

    THUMBNAIL_SIZE = (250,193)

    image = Image.open(StringIO(self.image.read()))
    thumb = ImageOps.fit(image, THUMBNAIL_SIZE, Image.ANTIALIAS)
    temp_handle = StringIO()
    thumb.convert('RGB').save(temp_handle, 'jpeg')
    temp_handle.seek(0)

    suf = SimpleUploadedFile(os.path.split(self.image.name)[-1],
            temp_handle.read(), content_type='image/jpeg')
    self.image.save('%s_college_.%s'%(os.path.splitext(suf.name)[0],'jpeg'), suf, save=False)

  def save(self, *args, **kwargs):
    self.slug = slugify(self.name)
    self.create_thumbnail()

    super(College, self).save(*args, **kwargs)

I have presented the user with a form to edit just the description. When the description 'POST' is made the 'save()' method above is called. The problem with this is that the thumbnail is created over and over again with a bigger name every time. And, also the previous thumbnail is not deleted from the hard disk. Is it possible, that this 'thumbnail' method doesn't get called over and over again with each edit of the 'description'.

Upvotes: 2

Views: 675

Answers (3)

bruno desthuilliers
bruno desthuilliers

Reputation: 77902

The simple solution is to NOT try to create the thumbnail at this stage, but only when it's needed, ie (pseudocode example):

class Whatever(models.Model):
    image = models.ImageField(...)

    @property
    def thumbnail(self):
       thumb = do_i_have_a_thumbnail_yet(self.image)
       if not thumb:
           thumb = ok_then_make_me_a_thumbnail_and_store_it_please(self.image)
       return thumb

Implementing do_i_have_a_thumbnail_yet() and ok_then_make_me_a_thumbnail_and_store_it_please() is left as an exercise to the reader, but there are librairies or django apps providing such services.

Another - and even better imho - solution is to delegate thumbnails handling to a templatetag or template filter. Why ? because

  1. thumbnails are mostly presentation stuff and do not belong to the model
  2. most of the time you'll need different thumbnails sizes from a same image
  3. the front-end developer may want to be free to change the thumbnail's size without having to ask you to change your backend code and write a migration script to recompute all existing thumbnails.

Upvotes: 0

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

There are two reasonable paths I see to handle this. Neither are ideal, so I'll be interested to see if anyone has a better option to offer.

One is to save the filename of the most recent image for which you created a thumbnail as a model field. Then in your save method you can check the filename of your image field against it and create a new thumbmail if it has changed. This has the disadvantage of requiring a new model field, but is more universal in its application.

The other is to override the save method of the form class. You can then check the old image filename by looking at self.instance.image and comparing that against self.cleaned_data['image']. This has the disadvantage of only affecting views that use that form class, but doesn't require changing your data model. You can pass a custom form to the admin, if you're using it, or override the admin class's save_model method.

Upvotes: 1

Aks
Aks

Reputation: 1232

You can check whether you are sending image file in you request post or not. For this You need to call your save in view with one argument request like : college.save(request)

def save(self, request=False, *args, **kwargs):
    self.slug = slugify(self.name)

    if request and request.FILES.get('image',False):
        self.create_thumbnail()

    super(College, self).save(*args, **kwargs)

OR you can differentiate your save and edit using

if self.pk is not None 

But it can create problem if you edit your image.

So its your choice How you want to go with it.

Upvotes: 3

Related Questions