Yeah, this seems to make sense to me; can you file a ticket so we
don't lose track of this?
Thanks for catching this, but I'm not sure this is the best approach
yet. It certainly touches the least amount of code, but I think this
is a symptom of something a bit bigger. I think it'd be better if
Storage._save() returns a File object that can be used by
ImageFieldFile.save() to update those related fields. That way, if
you're using something S3, you don't have to make a separate call to
the service just to get a file you already had in memory a second ago.
That would introduce another requirement on Storage subclasses, but I
think it's worthwhile in the long run, since there are bound to be
more issues like this. I'll work on getting a patch up later tonight.
I should also note up-front that this would also introduce a
backwards-incompatibility. Right now, Storage.save() returns just the
name of the file, but in order to support this ticket without making
unnecessary trips to the storage backend, it would need to return the
full File object. The name is still accessible as an attribute of the
File, of course, but it's definitely an incompatibility. I still think
it's worth it, but I wanted to be clear in case somebody has reason to
object to it.
To me, Storage objects are all about storing and retrieving files.
That's it. Some things, like determining the file's name, are
certainly related to that task, so they're included, but others, such
as altering the file's content, aren't. The filename normalization
mentioned previously is a great task for get_valid_filename() to
perform. In addition, the Flickr example above is now possible after
, by having FlickrStorage._save() return the actual name
(including photo ID) that Flickr uses to store the image. So that's
one thing out of the way.
On the other hand, this thread is more about a Storage object altering
the file's content (resizing an image) and expecting the width and
height on the model to be updated. Getting this to happen is a bit
troublesome, because it requires Storage to return a full File object
from both save() and _save(), which is neither pretty nor easy to use.
Instead, I'd like to go back to the idea of purpose.
The purpose of Storage is to store and retrieve files. The purpose of
File, on the other hand, is to manage the details of specific file
types. FieldFile is a customization of this, designed to work with
model fields. In this context, I think the appropriate place to do the
image resizing is in the FieldFile's save() method, *prior* to passing
it to the storage system in the first place. Something like this:
def save(self, name, content, save=True):
# Use PIL or whatever to resize the file
super(NormalizedImage, self).save(name, resized_content, save)
attr_class = NormalizedImageFile
Then just use NormalizedImageField on your models instead of
ImageField, and it'll automatically handle the resizing for you and
update the related fields properly. If you want, you can even override
NormalizedImageField.__init__() and accept target dimensions to make
it even more reusable.
That seems like a better approach to me overall, and is possible
without any changes to trunk. Thoughts? If nobody has any objections
to that, I'll go ahead and close #8208, but I'll wait and see if I'm
smoking something at the moment.
In those cases, Storage._save() won't know what the final file looks
like anyway. The whole point of passing the File back up through the
chain is so that you could get the changed file without making an
extra call to the remote storage. In your Facebook example, that's
impossible, since you would have to call out to Facebook to get it
anyway. In that case, passing it back up wouldn't save you anything at
> I'm sorry, but what's not easy nor pretty about File objects? I think
> the interface is very slick!
As one of the main guys behind it, I obviously think so too! :) But
after spending a couple hours trying to get the whole
pass-a-File-in-get-a-different-file-out thing working, trust me when I
say it's not as easy as it sounds.