Writing a custom storage system¶
If you need to provide custom file storage – a common example is storing files on some remote system – you can do so by defining a custom storage class. You’ll need to follow these steps:
Your custom storage system must be a subclass of django.core.files.storage.Storage:
from django.core.files.storage import Storage class MyStorage(Storage): ...
Django must be able to instantiate your storage system without any arguments. This means that any settings should be taken from django.conf.settings:
from django.conf import settings from django.core.files.storage import Storage class MyStorage(Storage): def __init__(self, option=None): if not option: option = settings.CUSTOM_STORAGE_OPTIONS ...
In addition, if your class provides local file storage, it must override the path() method.
Your storage class must be deconstructible so it can be serialized when it’s used on a field in a migration. As long as your field has arguments that are themselves serializable, you can use the django.utils.deconstruct.deconstructible class decorator for this (that’s what Django uses on FileSystemStorage).
By default, the following methods raise NotImplementedError and will typically have to be overridden:
Note however that not all these methods are required and may be deliberately omitted. As it happens, it is possible to leave each method unimplemented and still have a working Storage.
By way of example, if listing the contents of certain storage backends turns out to be expensive, you might decide not to implement Storage.listdir.
Another example would be a backend that only handles writing to files. In this case, you would not need to implement any of the above methods.
Ultimately, which of these methods are implemented is up to you. Leaving some methods unimplemented will result in a partial (possibly broken) interface.
You’ll also usually want to use hooks specifically designed for custom storage objects. These are:
- _open(name, mode='rb')¶
Called by Storage.open(), this is the actual mechanism the storage class uses to open the file. This must return a File object, though in most cases, you’ll want to return some subclass here that implements logic specific to the backend storage system.
- _save(name, content)¶
Called by Storage.save(). The name will already have gone through get_valid_name() and get_available_name(), and the content will be a File object itself.
Should return the actual name of name of the file saved (usually the name passed in, but if the storage needs to change the file name return the new name instead).
Returns a filename suitable for use with the underlying storage system. The name argument passed to this method is the original filename sent to the server, after having any path information removed. Override this to customize how non-standard characters are converted to safe filenames.
The code provided on Storage retains only alpha-numeric characters, periods and underscores from the original filename, removing everything else.
- get_available_name(name, max_length=None)¶
Returns a filename that is available in the storage mechanism, possibly taking the provided filename into account. The name argument passed to this method will have already cleaned to a filename valid for the storage system, according to the get_valid_name() method described above.
The length of the filename will not exceed max_length, if provided. If a free unique filename cannot be found, a SuspiciousFileOperation exception is raised.
If a file with name already exists, an underscore plus a random 7 character alphanumeric string is appended to the filename before the extension.
Previously, an underscore followed by a number (e.g. "_1", "_2", etc.) was appended to the filename until an available name in the destination directory was found. A malicious user could exploit this deterministic algorithm to create a denial-of-service attack. This change was also made in Django 1.6.6, 1.5.9, and 1.4.14.
The max_length argument was added.