По долгу службы ковырялся тут внутри у Джанго вокруг работы с файлами. Всякие хранилища, файловые поля, дескрипторы, врапперы... Признаться, штука вышла далеко не простая ("а я еще во-о-от такой помню..."). Я начал набрасывать себе схему того, как объекты друг с другом взаимодействуют, а потом решил ее причесать и выложить, чтобы остальным проще разбираться было.

Итак, первая версия схемы файлового инструментария Джанго:

К схеме прилагаются отмазка и пояснения. Отмазка простая: сейчас полчетвертого ночи, поэтому я реально мог там что-то напутать. Буду благодарен за исправления!

Пояснения следуют...

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

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

  1. Cyril Nikolaev

    Спасибо за статью, Иван. Хороший повод был самому разобраться, что есть что среди этих File'ов (сам как-то раз запутался, так и не распутался).

    Соответственно и сам File умеет себя сохранять и удалять из хранилища.

    Мне показалось, что конкретно File ничего про хранилища не знает (и не должен).

  2. Иван Сагалаев

    Кирилл, спасибо, я действительно попутал этот момент. Про свой storage знает только FieldFile. Поправил текст соответственно.

  3. getopts.blogspot.com/

    А в чем такую красивую схемку нарисовали?

  4. Иван Сагалаев

    Это Inkscape. Мой второй в жизни опыт общения с ним. Не скажу, что все получалось гладко, надо бы его еще поизучать поглубже. Умеет он действительно много.

  5. Александр Кошелев

    Самый очевидный пример такого интересного поля — всевозможные варианты ThumbnailImageField (привет, Саша!).

    Привет-привет.

    Схема прикольная, мне понравилась. Вот что значит правильное представление информации... А в виде кода всё гораздо страшнее в этих файловых дебрях:-)

  6. AmdY

    Ещё бы ссылочки на документацию сделать или на статьи, если есть.

  7. Иван Сагалаев

    Статей пока не встречал (это новая штука). А ссылку на документацию вкрутил в самое начало (http://docs.djangoproject.com/en/dev/topics/files/#topics-files)

  8. getopts.blogspot.com/

    Не хватает такой же общей информации куда и как вкручивать пользовательские классы унаследованные от описанных в посте.

  9. ziro

    Кстати, штука получилась очень универсальной. Я когда с бэкэндом разобрался, что-то похожее, но сильно попроще на asp.net сделал. Получилось просто и красиво и работает на ура.

  10. Nick

    Беда с этими именами - то FileField, то FieldFile, мало того, что легко i-e-l местами перепутать при наборе и не заметить, так ещё и друг от друга визуально слабо отличаются. А может, логичнее и удобнее было бы FieldFile назвать FileFieldFile?

    Только не надо предлагать ещё FieldFileFile, FileFieldFieldFile, FieldFileFileFile... :)

  11. [...] 08.02.2009 18:14 ImageFieldFile далекий предок стандартного питоновского file.ImageField - вообще из «другой оперы», предок джанговского Field.Соотношение «Is-a» (наследование) не имеет смысла, правильно использовать «Has-a».Еще это почитайте: http://softwaremaniacs.org/blog/2008/11/17/django-file-backend-chart/ [...]

Добавить комментарий