Многие ко Многим
В этой статье мы поговорим о связи многие-ко-многим. Перед чтением нужно обязательно познакомиться со связью один-ко-многим.
Начнём сразу с примера, у вас есть блог, в котором есть пользователи и посты:
class Post(models.Model):
author = models.ForeignKey("User", on_delete=models.CASCADE)
...
class User(models.Model):
login = models.CharField(max_length=200)
email = models.EmailField()
...
Теперь вы решили приделать к блогу комментарии. Комментарии очень похожи на посты. У них тоже есть автор, текст, дата создания и так далее. Они отличаются только тем, что всегда привязаны к посту:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
...
Теперь хочется добавить в блог лайки. Их можно хранить так же, как и комментарии: у них тоже есть автор и они привязаны к посту. Лайк каждого человека хранится как отдельная запись в базе: лайк какого-нибудь Серёжи под постом про космонавтику — это просто объект модели Like
:
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
У вас получилась модель Like
, в которой есть только два ForeignKey
. С их помощью модель Like
связывает пользователей и посты отношением многие-ко-многим: у каждого пользователя может быть много лайков под постами, а у каждого поста может быть много лайкнувших пользователей. Такие модели встречаются так часто, что в Django есть поле ManyToManyField
. Оно само создаёт таблицы, похожие на модель Like
, вам остаётся только указать что с чем связать. Код ниже делает то же самое, что и наверху, но с помощью ManyToManyField
:
class Post(models.Model):
author = models.ForeignKey("User", on_delete=models.CASCADE)
liked_by = models.ManyToManyField("User", related_name="liked_posts")
...
liked_by
— это новое поле поста, которое возвращает всех людей, которые его лайкнули. Что интересно, у User
тоже появилось новое поле liked_posts
, которое возвращает все посты, понравившиеся этому пользователю. Оно появится само, автоматически.
Под капотом ManyToManyField
тоже создаёт модель, один в один такую же, как описанная выше модель Like
. Просто благодаря ManyToManyField
она стала как будто бы полем моделей Post
и User
. Сравните использование ManyToManyField
и модели Like
, созданной вручную:
# Сколько лайков на посте?
likes_amount = post.liked_by.count()
likes_amount = Like.objects.filter(post=post).count()
# Что лайкал этот юзер?
likes = user.liked_posts
likes = Like.objects.filter(author=user)
# Поставить лайк посту
post.liked_by.add(user)
Like.objects.create(author=user, post=post)