PickledManyToManyField в Django
Однажды в студёную зимнюю пору понадобилось срочно подняться нам в гору сохранять ManyToMany в истории изменений. Само приложение для истории изменений обитает здесь, но там пока что старая версия, которая даже ForeignKey не умеет сохранять (когда доделаю всё - выложу). Два слова о том, как работает эта самая история работает.
Хранение истории
Для каждой модели, которая будет архивироваться создаётся своя собственная модель-история. Из нужной модели через TargetModel._meta.fields вытягиваются все поля и пересоздаются для новой модели. Ненужные поля отсекаются (например, AutoField), ненужные настройки - тоже (таких целая куча). Также в модель-историю добавляются несколько своих полей: для сохранения IP-адреса, номера ревизии, времени изменения, ссылки на оригинальную модель.
Потом через dispatcher к сигналу post_save присоединяется функция, которая определяет изменилась ли модель, и если да - сохраняется новая ревизия, в которую копируются все поля.
Хранение ManyToMany
Если хранить m2m таким же способом, как все остальные поля, то в таблице в которой будут храниться эти отношения достаточно несложно достичь огромного количества записей. Если в среднем около пятидесяти связей и двухсот ревизий на объект, то нужно всего 10‘000 объектов для ста миллионов отношений в m2m-таблице. Значит, хранить нужно каким-то альтернативным образом. Выход - сохранять отношения в той же таблице.
Задача - написать своё поле, которое внешне будет выглядеть как обычное ManyToManyField, но хранить данные «по месту прописки». Единственное - у него не может быть такой же обратной связи, по понятным причинам. Проведённые исследования показали, что проще всего унаследоваться от базового Field, а не от ManyToManyField, так как переписывать нужно практически все значимые методы, и дополнительные классы (дескриптор, менеджер, rel-класс). А также, информацию про отношение (rel-класс) нельзя хранить в field.rel, как это делается во всех остальных полях с отношениями, потому что в таком случае Django пытается создать обратное отношение (от целевой модели к стартовой), а в случае с Pickled организовать такую связь на уровне таблицы невозможно.
Так как сигналы для ManyToMany пока ещё не включены в Django, то это поле ещё немного бесполезно. А пока кода в django-history нет, на него можно посмотреть на dumpz.org, а покритиковать здесь.
Comments
Comment form for «PickledManyToManyField в Django»