I was trying to save some files using Django FileSystemStorage. My model is as shown below
key_store = FileSystemStorage(
location='account/files/'+datetime.date.today().isoformat())
class Account(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
user = models.ForeignKey(User, related_name='auth_user_account_relation')
subscription_id = models.CharField(max_length=100, null=True, blank=True)
info_file = models.FileField(storage=key_store)
But when I save an object of this model only the file name is stored in the db.
So when I try to access the path it returns path appended with todays date and not as the uploaded date.
ie. If I upload a file on say 09-21-2015 and I try to access the path on the next day it will return
account/files/09-22-2015/<file_name>
which will be an invalid path.
So what tweak should be made to store absolute path in db. Or what am I doing wrong here?
I'm pretty sure, your version does not do what you intend it to:
key_store = FileSystemStorage(
location='account/files/'+datetime.date.today().isoformat()
)
is evaluated when the module is loaded (usually only when you start your application). After that the date stays fixed as long as the module is not re-loaded. Hence it points to a directory with the name of the date the application was started, which is probably not what you want.
Also, FileSystemStorage
serializes with full path name, meaning that this also triggers a migration every other day (because the storage path changed).
You can solve this problem by using a combination of FileSystemStorage
(to store files outside of the media directory) and upload_to
with a callable:
key_store = FileSystemStorage(location='account/files/')
def key_store_upload_to(instance, path):
# prepend date to path
return os.path.join(
datetime.date.today().isoformat(),
path
)
class Account(models.Model):
# [...]
info_file = models.FileField(storage=key_store, upload_to=key_store_upload_to)
This uses a new FileSystemStorage
for the base directory of uploaded files and upload_to
to determine the name of an uploaded file relative to the storage's root.
Edit 1: Thanks for pointing out that upload_to
takes a date format.
Since upload_to
also takes strftime()
format specifiers, a pure-date related path choice can also be implemented without the callable:
info_file = models.FileField(storage=key_store, upload_to='account/files/%Y-%m-%d')
As for explanation:
A storage is essentially an abstraction of a hierarchical file system in django. FileField
s always store their files in a storage. By default the media storage is used, but that can be changed.
To determine the path a file is uploaded to, FileField
roughly does the following
upload_to
option specified for the file field.
If upload_to
is a string, use this string as a base directory. upload_to
is run through strftime()
to process any date specifiers. Concatenate upload_to
and the request path, which results in a target path relative to the storage.upload_to
is a callable, call it with (instance, request_path)
and use the return value as the target path (relative to the storage).As one can see, the storage is meant to be more or less static. Changing the storage potentially invalidates all existing file paths in the database, because they are relative to the storage.