Ответ
Для реализации механизма подписок используется классический паттерн "многие-ко-многим" (Many-to-Many) через промежуточную (связующую) таблицу.
Эта таблица хранит пары идентификаторов: кто подписался (subscriber) и на кого подписались (target).
Пример реализации на Django ORM
Модель Subscription выступает в роли связующей таблицы между пользователями (User).
from django.db import models
from django.contrib.auth.models import User
class Subscription(models.Model):
"""Модель для хранения подписок пользователей друг на друга."""
# Пользователь, который подписывается
subscriber = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='following' # user.following -> на кого он подписан
)
# Пользователь, на которого подписываются
target = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='followers' # user.followers -> кто на него подписан
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата подписки"
)
class Meta:
# Гарантирует, что пользователь не может подписаться на другого дважды
unique_together = ('subscriber', 'target')
ordering = ['-created_at']
def __str__(self):
return f"{self.subscriber} follows {self.target}"
Ключевые аспекты этой архитектуры:
- Промежуточная таблица:
Subscriptionсвязывает двух пользователей. - Внешние ключи:
subscriberиtargetссылаются на одну и ту же модельUser. related_name: Позволяет удобно получать связанные объекты. Например,user.followers.all()вернет всех подписчиков пользователя.- Ограничение уникальности:
unique_togetherпредотвращает дублирование записей о подписке. - Масштабируемость: Этот подход легко расширяется, например, добавлением поля для уведомлений.
Ответ 18+ 🔞
А, ну это ж классика, блядь! Подписки, лента, друзья-подписчики — вся эта социальная хуйня строится на одном и том же костыле, который называется «многие-ко-многим».
Представь себе, два пользователя. Один — подписчик, другой — тот, на кого подписались. И между ними надо как-то связать, кто на кого вешается, чтобы потом не было, как в том анекдоте: «А я тебя не подписывал, блядь!».
Вот для этого и делают промежуточную таблицу-связку. Она просто тупо хранит пары айдишников: кто подписался и на кого. Всё, ебушки-воробушки, больше от неё нихуя не нужно. Ну, кроме даты, чтобы знать, когда эта ебля началась.
Смотри, как это на Django ORM выглядит, прям как в жизни:
from django.db import models
from django.contrib.auth.models import User
class Subscription(models.Model):
"""Модель для хранения подписок пользователей друг на друга."""
# Пользователь, который подписывается
subscriber = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='following' # user.following -> на кого он подписан
)
# Пользователь, на которого подписываются
target = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='followers' # user.followers -> кто на него подписан
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата подписки"
)
class Meta:
# Гарантирует, что пользователь не может подписаться на другого дважды
unique_together = ('subscriber', 'target')
ordering = ['-created_at']
def __str__(self):
return f"{self.subscriber} follows {self.target}"
А теперь, сука, разжёвываю, что тут к чему:
- Промежуточная таблица: Это и есть наша
Subscription. Она как сводница, блядь, знакомитsubscriberсtarget. Больше она нихуя не делает. - Внешние ключи: Оба поля —
subscriberиtarget— тычут пальцем в одну и ту же модельUser. Потому что пользователь может быть и тем, и другим. Он сегодня подписчик, а завтра на него уже овердохуища народу подписалось. related_name: Вот это, блядь, магия! Благодаря этому можно писатьuser.followers.all()и получить всех, кто на него вешается. Илиuser.following.all()— чтобы увидеть, на кого он сам вешается. Удобно, ёпта!- Ограничение уникальности:
unique_together— это чтобы один мудак не мог дважды подписаться на другого мудака. Защита от долбоёбов, которые кнопку «Подписаться» как обезьяна тыкают. - Масштабируемость: А это самое охуенное. Захотел добавить, например, поле
notify = models.BooleanField()— чтобы подписчик выбирал, присылать ему уведомления или нет. И всё, блядь, добавляешь одно поле в модель — и функционал расширился. Красота, в рот меня чих-пых!
Вот и вся философия. Ничего сложного, просто табличка, которая спаривает пользователей. Как в зоопарке.