По долгу службы ковырялся тут внутри у Джанго вокруг работы с файлами. Всякие хранилища, файловые поля, дескрипторы, врапперы... Признаться, штука вышла далеко не простая ("а я еще во-о-от такой помню..."). Я начал набрасывать себе схему того, как объекты друг с другом взаимодействуют, а потом решил ее причесать и выложить, чтобы остальным проще разбираться было.
Итак, первая версия схемы файлового инструментария Джанго:
К схеме прилагаются отмазка и пояснения. Отмазка простая: сейчас полчетвертого ночи, поэтому я реально мог там что-то напутать. Буду благодарен за исправления!
Пояснения следуют...
Схема намерено не перечисляет всех существующих наследников базовых классов и их подробных интерфейсов, чтобы не создавать каши. Перечислены только основные действующие лица:
Storage
- Базовый класс для понятия "хранилище файлов". В Джанго есть самая очевидная реализация — хранилище файлов в локальной файловой системе (
FileSystemStorage
). Но вообще эта штука предназначена для того, чтобы пользователи писали свои интересные наследники: удаленные хранилища, распределенные хранилища, шифрованные хранилища и т.п. File
- Базовый класс, представляющий все джанговские файлы. По сути — легкая обертка над стандартным питоньим
file
ом с добавлением пары удобных методов (итераторchunks()
, свойства.path
,.url
). Именно этот объект возвращает методopen()
из хранилища. FileUploadHandler
-
Обработчик потока байтов, идущих по HTTP при upload'е файлов. Джанго умеет разбирать поток по ходу дела на файлы и передавать их на обработку таким вот хендлерам. По умолчанию в Джанго реализован хендлер, который умеет принимать маленькие файлы в память, а большие — сваливать на диск. Но этот интерфейс тоже предназначен для расширения юзерами. Возможная функциональность: потоковая распаковка архивов, проксирование файлов на удаленную машину без сохранения локально, проверка на вирусы, а также подсчет принятых байтов для реализации ajax'овых progress bar'ов.
Дефолтный
FileUploadHandler
в зависимости от размера файла рождает одного из двух наследниковUploadedFile
(см. далее):InMemoryUploadedFile
илиTemporaryUploadedFile
(во временной директории на диске). UploadedFile
- Наследник
File
. По сути — реимплементация его, умеющая безопасно обрезать заданное пользователем имя и помнящая кодировку загрузки. Вряд ли когда-то кому-то понадобится от него наследоваться или что-то с ним делать напрямую. FileField
- Поле модели, которое представляет собой файл, сохраненный в модели. На самом деле, как и все остальные поля моделей, он виден в объектах моделей не сам по себе, а заменяет себя в классе на атрибут с данным (так, например,
CharField
заменяет себя в классе модели на простой строковый атрибут). Атрибут этот — дескрипторFileDescriptor
, который при запросе его у модели черезobj.file_field
лезет в хранилище и достает оттуда файл в виде классаFieldFile
. FieldFile
(у вас не рябит еще в глазах от "F", "i", "e" и "l"?)- Наследник
File
, знающий, что он представлен в модели как имя файла. По этому имени он умеет доставать и сохранять контент из/в хранилище, на которое хранит у себя ссылку. Именно от этого класса предполагается наследоваться для создания своих интересных файловых полей. Самый очевидный пример такого интересного поля — всевозможные варианты ThumbnailImageField (привет, Саша!). Кстати, если будете делать что-то в таком духе, пожалуйста, сохраняйте данные не напрямую в файловую систему, а пользуйтесь интерфейсом хранилища, чтобы тамбнейлы сохранялись туда же, где и сами картинки.
Вот, как-то так. HTH!
Комментарии: 13
Спасибо за статью, Иван. Хороший повод был самому разобраться, что есть что среди этих File'ов (сам как-то раз запутался, так и не распутался).
Мне показалось, что конкретно File ничего про хранилища не знает (и не должен).
Кирилл, спасибо, я действительно попутал этот момент. Про свой storage знает только FieldFile. Поправил текст соответственно.
А в чем такую красивую схемку нарисовали?
Это Inkscape. Мой второй в жизни опыт общения с ним. Не скажу, что все получалось гладко, надо бы его еще поизучать поглубже. Умеет он действительно много.
Привет-привет.
Схема прикольная, мне понравилась. Вот что значит правильное представление информации... А в виде кода всё гораздо страшнее в этих файловых дебрях:-)
Ещё бы ссылочки на документацию сделать или на статьи, если есть.
Статей пока не встречал (это новая штука). А ссылку на документацию вкрутил в самое начало (http://docs.djangoproject.com/en/dev/topics/files/#topics-files)
Не хватает такой же общей информации куда и как вкручивать пользовательские классы унаследованные от описанных в посте.
Кстати, штука получилась очень универсальной. Я когда с бэкэндом разобрался, что-то похожее, но сильно попроще на asp.net сделал. Получилось просто и красиво и работает на ура.
Беда с этими именами - то FileField, то FieldFile, мало того, что легко i-e-l местами перепутать при наборе и не заметить, так ещё и друг от друга визуально слабо отличаются. А может, логичнее и удобнее было бы FieldFile назвать FileFieldFile?
Только не надо предлагать ещё FieldFileFile, FileFieldFieldFile, FieldFileFileFile... :)