编写自定义存储系统 — Django 文档

来自菜鸟教程
Django/docs/3.1.x/howto/custom-file-storage
跳转至:导航、​搜索

编写自定义存储系统

如果您需要提供自定义文件存储——一个常见的例子是在某个远程系统上存储文件——您可以通过定义一个自定义存储类来实现。 您需要执行以下步骤:

  1. 您的自定义存储系统必须是 django.core.files.storage.Storage 的子类:

    from django.core.files.storage import Storage
    
    class MyStorage(Storage):
        ...
  2. Django 必须能够在没有任何参数的情况下实例化您的存储系统。 这意味着应从 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
            ...
  3. 您的存储类必须实现 _open()_save() 方法,以及适合您的存储类的任何其他方法。 有关这些方法的更多信息,请参见下文。

    此外,如果您的类提供本地文件存储,则必须覆盖 path() 方法。

  4. 您的存储类必须是 可解构 ,以便在迁移中的字段上使用它时可以对其进行序列化。 只要您的字段具有本身是 serializable 的参数,您就可以为此使用 django.utils.deconstruct.deconstructible 类装饰器(这就是 Django 在 FileSystemStorage 上使用的)。

默认情况下,以下方法会引发 NotImplementedError 并且通常必须被覆盖:

但是请注意,并非所有这些方法都是必需的,可以故意省略。 碰巧的是,有可能让每个方法都未实现而仍然有一个可用的存储。

例如,如果列出某些存储后端的内容成本很高,您可能会决定不实现 Storage.listdir()

另一个示例是仅处理写入文件的后端。 在这种情况下,您不需要实现上述任何方法。

最终,实现这些方法中的哪一个取决于您。 保留一些未实现的方法将导致部分(可能已损坏)接口。

您通常还需要使用专为自定义存储对象设计的挂钩。 这些是:

_open(name, mode='rb')

必需

Storage.open() 调用,这是存储类用于打开文件的实际机制。 这必须返回一个 File 对象,尽管在大多数情况下,您需要在此处返回一些实现特定于后端存储系统的逻辑的子类。

_save(name, content)

Storage.save() 调用。 name 将已经通过 get_valid_name()get_available_name(),而 content 将是一个 File 对象本身。

应该返回保存的文件名的实际名称(通常是传入的name,但如果存储需要更改文件名,则返回新名称)。

get_valid_name(name)

返回适用于底层存储系统的文件名。 传递给此方法的 name 参数是发送到服务器的原始文件名,或者,如果 upload_to 是可调用的,则在删除任何路径信息后该方法返回的文件名。 覆盖它以自定义如何将非标准字符转换为安全文件名。

Storage 上提供的代码仅保留原始文件名中的字母数字字符、句点和下划线,并删除了其他所有内容。

get_alternative_name(file_root, file_ext)

3.0 版中的新功能。


返回基于 file_rootfile_ext 参数的替代文件名。 默认情况下,一个下划线加上一个随机的 7 个字符的字母数字字符串被附加到扩展名之前的文件名。

get_available_name(name, max_length=None)

返回存储机制中可用的文件名,可能会考虑提供的文件名。 根据上述 get_valid_name() 方法,传递给此方法的 name 参数将已清除为对存储系统有效的文件名。

如果提供,文件名的长度不会超过 max_length。 如果找不到可用的唯一文件名,则会引发 SuspiciousFileOperation 异常。

如果已经存在带有 name 的文件,则调用 get_alternative_name() 以获取替代名称。