Spec-Zone.ru › Django 6.0

Управление файлами

В этом документе описаны API Django для доступа к файлам, например к файлам, загруженным пользователем. API более низкого уровня достаточно универсальны, чтобы использовать их и для других целей. Если вам нужно работать со «статическими файлами» (JS, CSS и т. д.), см. Как управлять статическими файлами (например, изображениями, JavaScript, CSS).

По умолчанию Django хранит файлы локально, используя настройки MEDIA_ROOT и MEDIA_URL. В приведённых ниже примерах предполагается, что вы используете эти настройки по умолчанию.

Однако Django позволяет создавать собственные системы хранения файлов, благодаря которым можно полностью настроить место и способ хранения файлов в Django. Во второй половине этого документа описана работа таких систем хранения.

Использование файлов в моделях

При использовании FileField или ImageField Django предоставляет набор API для работы с этим файлом.

Рассмотрим следующую модель, в которой для хранения фотографии используется ImageField:

from django.db import models


class Car(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    photo = models.ImageField(upload_to="cars")
    specs = models.FileField(upload_to="specs")

У любого экземпляра Car будет атрибут photo, с помощью которого можно получить сведения о прикреплённой фотографии:

>>> car = Car.objects.get(name="57 Chevy")
>>> car.photo
<ImageFieldFile: cars/chevy.jpg>
>>> car.photo.name
'cars/chevy.jpg'
>>> car.photo.path
'/media/cars/chevy.jpg'
>>> car.photo.url
'https://media.example.com/cars/chevy.jpg'

Этот объект — car.photo в примере — является объектом File, а значит, имеет все описанные ниже методы и атрибуты.

Примечание

Файл сохраняется вместе с моделью в базе данных, поэтому узнать фактическое имя файла на диске можно только после сохранения модели.

Например, имя файла можно изменить, задав атрибуту name путь относительно расположения файлового хранилища (это MEDIA_ROOT, если вы используете FileSystemStorage по умолчанию):

>>> import os
>>> from django.conf import settings
>>> initial_path = car.photo.path
>>> car.photo.name = "cars/chevy_ii.jpg"
>>> new_path = os.path.join(settings.MEDIA_ROOT, car.photo.name)
>>> # Move the file on the filesystem
>>> os.rename(initial_path, new_path)
>>> car.save()
>>> car.photo.path
'/media/cars/chevy_ii.jpg'
>>> car.photo.path == new_path
True

Чтобы сохранить существующий файл с диска в FileField:

>>> from pathlib import Path
>>> from django.core.files import File
>>> path = Path("/some/external/specs.pdf")
>>> car = Car.objects.get(name="57 Chevy")
>>> with path.open(mode="rb") as f:
...     car.specs = File(f, name=path.name)
...     car.save()
...

Примечание

Хотя у ImageField экземпляра доступны атрибуты, не содержащие данные изображения, например height, width и size, использовать сами данные изображения нельзя, не открыв изображение заново. Например:

>>> from PIL import Image
>>> car = Car.objects.get(name="57 Chevy")
>>> car.photo.width
191
>>> car.photo.height
287
>>> image = Image.open(car.photo)
# Raises ValueError: seek of closed file.
>>> car.photo.open()
<ImageFieldFile: cars/chevy.jpg>
>>> image = Image.open(car.photo)
>>> image
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=191x287 at 0x7F99A94E9048>

Объект File

Внутри Django использует экземпляр django.core.files.File каждый раз, когда ему нужно представить файл.

В большинстве случаев вы будете использовать объект File, предоставленный Django (то есть файл, прикреплённый к модели, как показано выше, или, возможно, загруженный файл).

Если вам нужно создать объект File самостоятельно, проще всего сделать это с помощью встроенного в Python объекта file:

>>> from django.core.files import File

# Create a Python file object using open()
>>> f = open("/path/to/hello.world", "w")
>>> myfile = File(f)

Теперь вы можете использовать любые документированные атрибуты и методы класса File.

Учтите, что файлы, созданные таким образом, не закрываются автоматически. Чтобы закрывать файлы автоматически, можно использовать следующий подход:

>>> from django.core.files import File

# Create a Python file object using open() and the with statement
>>> with open("/path/to/hello.world", "w") as f:
...     myfile = File(f)
...     myfile.write("Hello World")
...
>>> myfile.closed
True
>>> f.closed
True

Закрывать файлы особенно важно при обращении к полям файлов в цикле по большому количеству объектов. Если после обращения к файлам не закрывать их вручную, может возникнуть риск исчерпания дескрипторов файлов. Это может привести к следующей ошибке:

OSError: [Errno 24] Too many open files

Хранение файлов

За кулисами Django делегирует файловой системе хранения принятие решений о том, как и где хранить файлы. Именно этот объект обрабатывает такие операции, как работа с файловыми системами, открытие и чтение файлов и т. д.

По умолчанию Django использует файловое хранилище 'django.core.files.storage.FileSystemStorage'. Если вы явно не укажете систему хранения в ключе default настройки STORAGES, будет использоваться именно она.

Подробнее о встроенной файловой системе хранения по умолчанию см. ниже. О том, как создать собственную систему хранения, читайте в разделе Как написать собственный класс хранилища.

Объекты хранилищ

Хотя чаще всего вам понадобится объект File (который обращается к подходящему хранилищу для данного файла), файловые системы хранения можно использовать и напрямую. Вы можете создать экземпляр собственного класса хранения файлов или — что часто удобнее — воспользоваться глобальной системой хранения по умолчанию:

>>> from django.core.files.base import ContentFile
>>> from django.core.files.storage import default_storage

>>> path = default_storage.save("path/to/file", ContentFile(b"new content"))
>>> path
'path/to/file'

>>> default_storage.size(path)
11
>>> default_storage.open(path).read()
b'new content'

>>> default_storage.delete(path)
>>> default_storage.exists(path)
False

Описание API файловых хранилищ см. в разделе API файловых хранилищ.

Встроенный класс файлового хранилища

Django включает класс django.core.files.storage.FileSystemStorage, реализующий базовое хранение файлов в локальной файловой системе.

Например, следующий код будет сохранять загруженные файлы в /media/photos независимо от значения настройки MEDIA_ROOT:

from django.core.files.storage import FileSystemStorage
from django.db import models

fs = FileSystemStorage(location="/media/photos")


class Car(models.Model):
    ...
    photo = models.ImageField(storage=fs)

Собственные системы хранения работают аналогичным образом: их можно передать в качестве аргумента storage в FileField.

Использование вызываемого объекта

В качестве параметра storage для FileField или ImageField можно использовать вызываемый объект. Это позволяет менять используемое хранилище во время выполнения, например выбирая разные хранилища для разных сред.

Вызываемый объект будет вызван при загрузке классов моделей и должен вернуть экземпляр Storage.

Например:

from django.conf import settings
from django.db import models
from .storages import MyLocalStorage, MyRemoteStorage


def select_storage():
    return MyLocalStorage() if settings.DEBUG else MyRemoteStorage()


class MyModel(models.Model):
    my_file = models.FileField(storage=select_storage)

Чтобы задать хранилище, определённое в настройке STORAGES, можно использовать storages:

from django.core.files.storage import storages


def select_storage():
    return storages["mystorage"]


class MyModel(models.Model):
    upload = models.FileField(storage=select_storage)

Поскольку вызываемый объект вызывается при загрузке классов моделей, для переопределения настройки STORAGES в тестах следует использовать подкласс LazyObject:

from django.core.files.storage import storages
from django.utils.functional import LazyObject


class OtherStorage(LazyObject):
    def _setup(self):
        self._wrapped = storages["mystorage"]


my_storage = OtherStorage()


class MyModel(models.Model):
    upload = models.FileField(storage=my_storage)

Объект LazyObject откладывает получение хранилища до момента, когда оно действительно понадобится, позволяя применить override_settings():

@override_settings(
    STORAGES={
        "mystorage": {
            "BACKEND": "django.core.files.storage.InMemoryStorage",
        }
    }
)
def test_storage():
    model = MyModel()
    assert isinstance(model.upload.storage, InMemoryStorage)

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/files/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API