1) You can get any Field instance from a class by using the
model._meta.get_field() method. Instead of your list comprehension,
just use the following line:
picfield = p._meta.get_field('picture')
2) If you read the comments in delete_file(), you'll see the
following: "If the file exists and no other object of this type
references it, delete it from the filesystem." Your problem is that
the Person "p" still references the file, so delete_file() won't
delete it. You'd need to put something like this before the
delete_file() call in order to get it to work properly:
p.picture = ''
p.save()
3) Saving an object with a new filename alters the corresponding field
in the database record. What this means is that you can somewhat
ignore the previous item, and simply put delete_file() at the *end* of
your script, instead of before the file saving code. That way, by the
time it runs, the Person "p" will already have a different filename,
and delete_file() won't find it, and will happily delete the file for
you.
4) save_FOO_file() automatically saves the model instance with the new
filename. Right now, you're calling p.save() after
p.save_picture_file(), which results in two hits to the database for
absolutely no reason. save_picture_file() calls save() internally, so
don't bother doing it again.
5) With those changes, your code should look something like this:
class PersonForm(forms.Form):
# ...
picture = forms.ImageField(required=False)
# ...
def save(self):
# ...
p = Person.objects.get(pk=pk)
file, content = (self.cleaned_data['picture'].filename,
self.cleaned_data['picture'].content)
p.save_picture_file(file, content)
p._meta.get_field('picture').delete_file(p)
6) Deleting files will get much easier soon, and it will even be a bit
simpler to do what you're looking to do, without having to build all
this into your form logic. So most of the things I've just covered
will change, but for the better.
-Gul