Управление файлами¶
Этот документ описывает API Django для работы с файлами, которые были загружены пользователем. API более низкого уровня довольно стандартны и вы можете использовать их для других целей. Если вам надо обрабатывать «статические файлы» (JS, CSS и так далее), обратитесь к Как управлять статическими файлами (изображениями, скриптами JS, 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 является файловым объектом, а значит имеет ряд атрибутов, описанных ниже.
Примечание
Файл сохраняется в базе данных как часть модели, поэтому фактическое название файла не может быть использовано до того момента, пока не будет сохранена модель, включающая его.
Например, вы можете изменить имя файла, установив для 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, такие как высота, ширина и размер, доступны в экземпляре, базовые данные изображения нельзя использовать без повторного открытия изображения. Например:
>>> 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 является 'django.core.files.storage.FileSystemStorage'. Если вы явно не указали систему хранения в ключе default параметра STORAGES, будет использоваться именно она.
Для получения более подробной информации о встроенной системе хранения файлов смотрите нижеследующие абзацы или обратитесь к главе о создании пользовательской системы хранения файлов Как написать собственный класс хранилища, чтобы понять, как с ней работать.
Объект Storage¶
Хотя в большинстве случаев вам захочется использовать объект «Файл» (который делегирует соответствующее хранилище для этого файла), вы можете использовать файловые системы хранения напрямую. Вы можете создать экземпляр какого-либо пользовательского класса хранения файлов или — что зачастую более полезно — использовать глобальную систему хранения по умолчанию:
>>> 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.
Встроенный класс FileSystemStorage¶
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)
Custom storage systems работает аналогичным образом: вы можете обратиться к нему как к аргументу 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)