Opened 12 years ago

Last modified 8 years ago

#18283 assigned Bug

FileField should not reuse FieldFiles

Reported by: Sayane Owned by: Andrzej Pragacz
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

A simple example:

old_image = mymodel.image
old_image.open()
try:
    mymodel.image = do_something_with_image(old_image)
finally:
    old_image.close()

mymodel.save() # transaction manager is in auto commit mode
old_image.delete()

Expected behavior:

New file is commited to disk and database, old file is removed.

Actual behavior:

New file is commited to disk and database, then this new file is removed from disk.

That's because FileField (or to be more precise: FileDescriptor class) is re-using FieldFile objects instead of creating new FieldFile for every new instance of a file. This leads to unexpected results and data loss (like in example).

Change History (4)

comment:1 by Jannis Leidel, 12 years ago

Triage Stage: UnreviewedAccepted

comment:2 by Andrzej Pragacz, 8 years ago

Owner: changed from nobody to Andrzej Pragacz
Status: newassigned

comment:3 by Andrzej Pragacz, 8 years ago

Owner: changed from Andrzej Pragacz to Andrzej Pragacz

comment:4 by Andrzej Pragacz, 8 years ago

I have a real trouble with reproducing this bug. The description of it is very enigmatic - the submitter didn't mention what do_something_with_image() actually does (for instance, what type of object it returns).
This bug seems to happen only when one takes the FieldFile object from specified model field and modifies it in place. This is illustrated by test_unchanged_fieldfile in the diff below:

https://github.com/django/django/compare/master...szopu:ticket_18283?expand=1

The only way I see this problem can be fixed, is defensive shallow copying of the FieldFile object in __get__ and __set__ (I think that doing it for __set__ only is not enough). But this solution has a downside, for instance, this would generate following semantics:

model.myfile.name = 'somefile.txt'
print(model.myfile.name)  # returns the previous filename instead of 'somefile.txt'

So my question is: Is it the right way to go?

Note: See TracTickets for help on using tickets.
Back to Top