The following code snippet used to work with Django 1.0.2, but with the latest svn version the slug is not being set to the filename produced by get_path because, as I understand it, get_path will not be called until the instance is saved to the database.
def get_path(instance, filename): salt = hashlib.sha1(str(random.random())).hexdigest() name = hashlib.sha1(salt+filename).hexdigest()[:16] return '%s.jpg' % (name)
I could call instance.save() twice in my view to force it to use the correct name, but this feels a bit messy to me. What's the best way to get the correct value for the slug field prior to saving it to the database, given that the filename is 'randomly' generated?
> The following code snippet used to work with Django 1.0.2, but with > the latest svn version the slug is not being set to the filename > produced by get_path because, as I understand it, get_path will not be > called until the instance is saved to the database.
> def get_path(instance, filename): > salt = hashlib.sha1(str(random.random())).hexdigest() > name = hashlib.sha1(salt+filename).hexdigest()[:16] > return '%s.jpg' % (name)
> I could call instance.save() twice in my view to force it to use the > correct name, but this feels a bit messy to me. What's the best way to > get the correct value for the slug field prior to saving it to the > database, given that the filename is 'randomly' generated?
From reading the above mentioned ticket and it's referenced discussion threads, I can't see any way of generating a slug field from the filename before the instance is saved. At the moment I am saving the instance to generate the hashed filename, creating the slug based on the dynamically generated filename, and then saving to the database again.
Doesn't seem ideal to me, especially as it worked without the extra save in 1.0.2, and I'm using this dynamic filename/slug method on several projects.
> > The following code snippet used to work with Django 1.0.2, but with
> > the latest svn version the slug is not being set to the filename
> > produced by get_path because, as I understand it, get_path will not be
> > called until the instance is saved to the database.
> > I could call instance.save() twice in my view to force it to use the
> > correct name, but this feels a bit messy to me. What's the best way to
> > get the correct value for the slug field prior to saving it to the
> > database, given that the filename is 'randomly' generated?
> From reading the above mentioned ticket and it's referenced discussion
> threads, I can't see any way of generating a slug field from the
> filename before the instance is saved. At the moment I am saving the
> instance to generate the hashed filename, creating the slug based on
> the dynamically generated filename, and then saving to the database
> again.
> Doesn't seem ideal to me, especially as it worked without the extra
> save in 1.0.2, and I'm using this dynamic filename/slug method on
> several projects.
Judging by the discussions on the above mentioned ticket, it may be a
while before this behaviour is reverted back (if it is reverted back
at all.)
It assumes that the expression will never result in a slug whose
length is more than 16. But that's not necessarily true because when
Django tries to save your photo file and finds another file with the
same name, it will append an underscore to the filename until it
generates a unique filename. In that case, your slug will extend
beyond its 16 character limit. Since your filenames are generated
randomly, it's not very likely that you will ever hit this edge case.
But just be aware.
As for your problem of computing a slug based on the filename, perhaps
the following will help:
def get_path(instance, filename):
salt = hashlib.sha1(str(random.random())).hexdigest()
name = hashlib.sha1(salt+filename).hexdigest()[:16]
# Save generated filename in an attribute of this model instance:
instance._my_filename = name
return '%s.jpg' % (name)
Then, in the model save do:
if not self.slug:
self.slug = self._my_filename
If this does work, you might still have to add some more defensive
code around there to check that the instance "hasattr" _my_filename
and if it doesn't, may be just generated a random 16 character slug.
> Judging by the discussions on the above mentioned ticket, it may be a > while before this behaviour is reverted back (if it is reverted back > at all.)
I'm not seeking to have the new behaviour reversed - if the powers that be say the new way is more robust, I'm happy to go along with that. But I think it is important to realise that it is a backwards incompatible change, and there are real use cases for the old behaviour.
> It assumes that the expression will never result in a slug whose > length is more than 16. But that's not necessarily true because when > Django tries to save your photo file and finds another file with the > same name, it will append an underscore to the filename until it > generates a unique filename. In that case, your slug will extend > beyond its 16 character limit. Since your filenames are generated > randomly, it's not very likely that you will ever hit this edge case. > But just be aware.
Duplicate filenames are checked for elsewhere in my code (I only presented a truncated version for clarity), so there shouldn't be a case where underscores are used, and the slug will always be unique (unless there are so many uploads, I run out of 16 hex digit combinations!).
> As for your problem of computing a slug based on the filename, perhaps > the following will help:
> def get_path(instance, filename): > salt = hashlib.sha1(str(random.random())).hexdigest() > name = hashlib.sha1(salt+filename).hexdigest()[:16] > # Save generated filename in an attribute of this model instance: > instance._my_filename = name > return '%s.jpg' % (name)
> Then, in the model save do:
> if not self.slug: > self.slug = self._my_filename
> If this does work, you might still have to add some more defensive > code around there to check that the instance "hasattr" _my_filename > and if it doesn't, may be just generated a random 16 character slug.
I don't think this will work, as get_path() will not be called until the model is saved. So the _my_filename attribute will not be available to the code in the save method before the super(Photo, self).save() line.
Would it be possible to define the hashed filename in the save method, and then pass it to the upload_to argument of the ImageField?
> 2009/7/2 Andrew Turner <acturne...@gmail.com>: >> Would it be possible to define the hashed filename in the save method, >> and then pass it to the upload_to argument of the ImageField?
> In answer to my own question, this seems to work:-
> def save(self): > name = hashlib.sha1(str(random.random())).hexdigest()[:16] > self._my_filename = '%s.jpg' % (name) > self.slug = name > super(Post, self).save()
Talking to myself again, I've changed the save method slightly:-
def save(self): if not self.slug: name = hashlib.sha1(str(random.random())).hexdigest()[:16] self._my_filename = '%s.jpg' % (name) self.slug = name super(Post, self).save()
and added a blank=True to the slug field so that it only sets it on the first save.
If there is a better way of doing all this, feel free to let me know. If not, I hope this is of use to somebody.
I have another question. You are naming images as hash of random number. There is a small chance to produce same name i think. It is not good way to give name ha?
2009/7/5 Mirat Can Bayrak <miratcanbay...@gmail.com>:
> I have another question. You are naming images as hash of random number. There is a small chance to produce same name i think. It is not good way to give name ha?
The slug field has a unique=True argument, so if the same file name does happen to be produced, it can't be saved to the database, and the user will have to try again.