Python: Django ORM
Теория: Многие ко Многим
На предыдущем уроке были рассмотрены связи "один к одному" и "один ко многим". Тогда же был упомянут и вид связи "многие ко многим".
Для этого вида связи тоже есть специальный тип поля: ManyToManyField. Этот вид связи подразумевает, что у объектов обеих моделей будет RelatedManager, отражающий множество связанных сущностей. Более того, это будет отдельный вид менеджера — ManyRelatedManager.
Например, у нас есть следущие модели:
У любого поста может быть несколько тегов, а может не быть ни одного. И одним тегом можно пометить более чем один пост. Поэтому пост и тег соотносятся как "многие ко многим". Заметьте, что опция on_delete не указана: кажется неверным удалять посты, если вдруг будет удалён тег, и уж точно не следует удалять теги при удалении помеченного ими поста.
Работают с такого рода связью следующим образом:
Заметьте, что вставка производится в таблицу "blog_post_tags" — это вспомогательная таблица, которую создает Django ORM, чтобы связать таблицы "blog_post" и "blog_tag". В проекте для неё вы не найдёте соответствующей модели. Как видите, ORM может скрывать даже части схемы базы и берёт на себя всю работу.
Также обратите внимание на то, как происходит связывание тега и поста: ManyRelatedManager в атрибуте .tags экземпляра модели Post имеет специальный метод .add(). Он принимает произвольное количество тегов с которыми нужно связать данный пост. В свою очередь, со стороны модели Tag также есть ManyRelatedManager, позволяющий работать с постами, связанными с конкретным тегом.
Разорвать связь между объектами можно с помощью метода .remove() у любого из двух ManyRelatedManager, передав в аргументах метода перечень тегов для поста и наоборот. Но помните, что сами теги при этом удалены не будут. Объекты, связанные как "многие ко многим" удалять следует с помощью их собственных менеджеров, а не с помощью ManyRelatedManager.
Связь через выделенную модель
По умолчанию на каждый ManyToManyField Django ORM создаст по вспомогательной таблице, в которой будет два столбца типа FOREIGN KEY, которые и будут ссылаться на сущности в связываемых таблицах. Такое неявное использование таблиц удобно в большинстве случаев.
Однако встречаются ситуации, когда факт связи между двумя сущностями хочется сопроводить какой-либо дополнительной информацией. Например, хочется знать, в какой момент времени некий тег был прикреплён к некоторому посту в блоге или какой пользователь этот тег посту присвоил. Здесь пригодилась бы отдельная модель, но не хочется терять все те удобства, которые даёт использование ManyRelatedManager.
Специально для подобных случаев ManyToManyField позволяет с помощью аргумента through указать конкретную модель, которая будет выступать связью. И разумеется, если модель будет указана, то ORM лишнюю таблицу создавать не будет.
Этот подвид связи достаточно интересен, но применяется наиболее редко. Более подробно почитать про особенности использования вспомогательной модели вы сможете в специальном разделе документации.


