Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/docs/howto/custom-file-storage.txt
blob: 9683dcd20a5b689a997886d79a32509dbc477f07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
Writing a custom storage system
===============================

.. currentmodule:: django.core.files.storage

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
                ...

#. Your storage class must implement the :meth:`_open()` and :meth:`_save()`
   methods, along with any other methods appropriate to your storage class. See
   below for more on these methods.

   In addition, if your class provides local file storage, it must override
   the ``path()`` method.

#. Your storage class must be :ref:`deconstructible <custom-deconstruct-method>`
   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
   :ref:`serializable <migration-serializing>`, 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:

* :meth:`Storage.delete`
* :meth:`Storage.exists`
* :meth:`Storage.listdir`
* :meth:`Storage.size`
* :meth:`Storage.url`

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:

.. method:: _open(name, mode='rb')

**Required**.

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.

.. method:: _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).

.. method:: get_valid_name(name)

Returns a filename suitable for use with the underlying storage system. The
``name`` argument passed to this method is either the original filename sent to
the server or, if ``upload_to`` is a callable, the filename returned by that
method after any path information is removed. Override this to customize how
non-standard characters are converted to safe filenames.

.. versionchanged:: 1.9

    In older versions, this method was not called when ``upload_to`` was a
    callable.

The code provided on ``Storage`` retains only alpha-numeric characters, periods
and underscores from the original filename, removing everything else.

.. method:: 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 :exc:`SuspiciousFileOperation
<django.core.exceptions.SuspiciousOperation>` 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.