По долгу службы ковырялся тут внутри у Джанго вокруг работы с файлами. Всякие хранилища, файловые поля, дескрипторы, врапперы... Признаться, штука вышла далеко не простая ("а я еще во-о-от такой помню..."). Я начал набрасывать себе схему того, как объекты друг с другом взаимодействуют, а потом решил ее причесать и выложить, чтобы остальным проще разбираться было.
Итак, первая версия схемы файлового инструментария Джанго:
К схеме прилагаются отмазка и пояснения. Отмазка простая: сейчас полчетвертого ночи, поэтому я реально мог там что-то напутать. Буду благодарен за исправления!
Пояснения следуют...
Схема намерено не перечисляет всех существующих наследников базовых классов и их подробных интерфейсов, чтобы не создавать каши. Перечислены только основные действующие лица:
StorageFileSystemStorage). Но вообще эта штука предназначена для того, чтобы пользователи писали свои интересные наследники: удаленные хранилища, распределенные хранилища, шифрованные хранилища и т.п.Filefileом с добавлением пары удобных методов (итератор chunks(), свойства .path, .url). Именно этот объект возвращает метод open() из хранилища.FileUploadHandlerОбработчик потока байтов, идущих по HTTP при upload'е файлов. Джанго умеет разбирать поток по ходу дела на файлы и передавать их на обработку таким вот хендлерам. По умолчанию в Джанго реализован хендлер, который умеет принимать маленькие файлы в память, а большие — сваливать на диск. Но этот интерфейс тоже предназначен для расширения юзерами. Возможная функциональность: потоковая распаковка архивов, проксирование файлов на удаленную машину без сохранения локально, проверка на вирусы, а также подсчет принятых байтов для реализации ajax'овых progress bar'ов.
Дефолтный FileUploadHandler в зависимости от размера файла рождает одного из двух наследников UploadedFile (см. далее): InMemoryUploadedFile или TemporaryUploadedFile (во временной директории на диске).
UploadedFileFile. По сути — реимплементация его, умеющая безопасно обрезать заданное пользователем имя и помнящая кодировку загрузки. Вряд ли когда-то кому-то понадобится от него наследоваться или что-то с ним делать напрямую.FileFieldCharField заменяет себя в классе модели на простой строковый атрибут). Атрибут этот — дескриптор FileDescriptor, который при запросе его у модели через obj.file_field лезет в хранилище и достает оттуда файл в виде класса FieldFile.FieldFile (у вас не рябит еще в глазах от "F", "i", "e" и "l"?)File, знающий, что он представлен в модели как имя файла. По этому имени он умеет доставать и сохранять контент из/в хранилище, на которое хранит у себя ссылку. Именно от этого класса предполагается наследоваться для создания своих интересных файловых полей. Самый очевидный пример такого интересного поля — всевозможные варианты ThumbnailImageField (привет, Саша!). Кстати, если будете делать что-то в таком духе, пожалуйста, сохраняйте данные не напрямую в файловую систему, а пользуйтесь интерфейсом хранилища, чтобы тамбнейлы сохранялись туда же, где и сами картинки.Вот, как-то так. HTH!
Комментарии: 12
Спасибо за статью, Иван. Хороший повод был самому разобраться, что есть что среди этих 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... :)