Django FileField (or ImageField) open() method returns None for valid file?
Question:
let me put it like this:
model.py:
class Task(models.Model):
...
seq_file = models.FileField(upload_to='files/', blank=True, null=True)
...
ajax.py (I’m using dajaxice but it doesn’t matter):
...
def startTask(request, name):
task = Task.objects.get(task_name=name)
data = task.seq_file.open()
filename = os.path.join(settings.MEDIA_ROOT ,task.seq_file.name)
if not os.path.isfile(filename):
raise Exception, "file " + filename + " not found."
sequences = parser.parse(data.read())
...
this returns:
File "/home/mnowotka/Dokumenty/MgrFuncAdnot/app/django-gui/src/gui/ajax.py", line 43, in startTask
sequences = parser.parse(data.read())
AttributeError: 'NoneType' object has no attribute 'read'
but:
...
def startTask(request, name):
task = Task.objects.get(task_name=name)
filename = os.path.join(settings.MEDIA_ROOT ,task.seq_file.name)
if not os.path.isfile(filename):
raise Exception, "file " + filename + " not found."
data = open(filename)
sequences = parser.parse(data.read())
...
works perfectly!
Why?
(I’m using django 1.3)
Answers:
A FileField
will give you a file-like object and there is no need to call open() on it. In your example, just call task.seq_file.file
.
Why is that? There are many storage backends for FileField
, and many of them are not backed by a file in disk (think of S3 storage, for example). I guess that is why the documentation says it returns a file-like object, not a file. For some kinds of storage the “open” method makes no sense.
because open
method of models.FileField
doesn’t return anything
you can just use:
task.seq_file.read()
and you don’t need calculate path of file for checking if file exist. you can use task.seq_file.path
:
if not os.path.isfile(task.seq_file.path):
....
When in doubt, check the code. Here’s an excerpt from django.db.models.fields.files
:
def open(self, mode='rb'):
self._require_file()
self.file.open(mode)
# open() doesn't alter the file's contents, but it does reset the pointer
open.alters_data = True
So, in the case of a FileField
, open
reopens the file using the specified mode. Then, once you call open
, you can continue to use methods like read
using the newly applied mode.
Surprisingly but django.db.models.fields.files
does not utilize file.storage.exists()
method so I had to implement my own small function to have cross-storage compatible check for actual physical file existence:
# Check whether actual file of FileField exists (is not deleted / moved out).
def file_exists(obj):
return obj.storage.exists(obj.name)
let me put it like this:
model.py:
class Task(models.Model):
...
seq_file = models.FileField(upload_to='files/', blank=True, null=True)
...
ajax.py (I’m using dajaxice but it doesn’t matter):
...
def startTask(request, name):
task = Task.objects.get(task_name=name)
data = task.seq_file.open()
filename = os.path.join(settings.MEDIA_ROOT ,task.seq_file.name)
if not os.path.isfile(filename):
raise Exception, "file " + filename + " not found."
sequences = parser.parse(data.read())
...
this returns:
File "/home/mnowotka/Dokumenty/MgrFuncAdnot/app/django-gui/src/gui/ajax.py", line 43, in startTask
sequences = parser.parse(data.read())
AttributeError: 'NoneType' object has no attribute 'read'
but:
...
def startTask(request, name):
task = Task.objects.get(task_name=name)
filename = os.path.join(settings.MEDIA_ROOT ,task.seq_file.name)
if not os.path.isfile(filename):
raise Exception, "file " + filename + " not found."
data = open(filename)
sequences = parser.parse(data.read())
...
works perfectly!
Why?
(I’m using django 1.3)
A FileField
will give you a file-like object and there is no need to call open() on it. In your example, just call task.seq_file.file
.
Why is that? There are many storage backends for FileField
, and many of them are not backed by a file in disk (think of S3 storage, for example). I guess that is why the documentation says it returns a file-like object, not a file. For some kinds of storage the “open” method makes no sense.
because open
method of models.FileField
doesn’t return anything
you can just use:
task.seq_file.read()
and you don’t need calculate path of file for checking if file exist. you can use task.seq_file.path
:
if not os.path.isfile(task.seq_file.path):
....
When in doubt, check the code. Here’s an excerpt from django.db.models.fields.files
:
def open(self, mode='rb'):
self._require_file()
self.file.open(mode)
# open() doesn't alter the file's contents, but it does reset the pointer
open.alters_data = True
So, in the case of a FileField
, open
reopens the file using the specified mode. Then, once you call open
, you can continue to use methods like read
using the newly applied mode.
Surprisingly but django.db.models.fields.files
does not utilize file.storage.exists()
method so I had to implement my own small function to have cross-storage compatible check for actual physical file existence:
# Check whether actual file of FileField exists (is not deleted / moved out).
def file_exists(obj):
return obj.storage.exists(obj.name)