Django советы по архитектуре проекта - PullRequest
1 голос
/ 02 марта 2020

У меня есть проект django, и у меня есть модель Post, похожая на эту:

class BasicPost(models.Model):
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    published = models.BooleanField(default=False)
    created_date = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=False)
    body = models.TextField(max_length=999)
    media = models.ImageField(blank=True)

    def get_absolute_url(self):
        return reverse('basic_post', args=[str(self.pk)])

    def __str__(self):
        return self.title

Также я использую модель пользователя basi c, которая поставляется с базой c django app.

Я хочу сохранить сообщения ведьмы, которые прочитал каждый пользователь, чтобы я мог отправлять ему сообщения, которые он не читал. Мой вопрос заключается в том, каков наилучший способ сделать это, если я использую поле «Многие ко многим», следует ли мне поместить его в модель «Пользователь» и сохранить все прочитанные им сообщения, или я должен сделать это в другом направлении, поставить «многие ко многим» поле в модели Post и сохранить для каждого сообщения, которое читал пользователь? в модели Post будет более 1 миллиона сообщений и около 50 000 пользователей, и я хочу использовать лучшие фильтры для возврата непрочитанных сообщений пользователю

Если мне нужно использовать первый вариант, как мне это сделать? расширить пользовательскую модель? спасибо!

Ответы [ 3 ]

1 голос
/ 02 марта 2020

Могли бы вы иметь отдельную модель ReadPost вместо потенциально большого м2, которую вы могли бы сохранить, когда пользователь читает сообщение? Таким образом, вы можете просто запросить модели ReadPost, чтобы получить данные, вместо того, чтобы хранить их все в сообщении в блоге.

Может быть что-то вроде этого:

from django.utils import timezone


class UserReadPost(models.Model):
    user = models.ForeignKey("auth.User", on_delete=models.CASCADE, related_name="read_posts")
    seen_at = models.DateTimeField(default=timezone.now)
    post = models.ForeignKey(BasicPost, on_delete=models.CASCADE, related_name="read_by_users")

Вы можете добавить unique_together ограничение, чтобы убедиться, что только один UserReadPost объект создан для каждого пользователя и публикации (чтобы вы не учитывали дважды), и используйте get_or_create() при создании новых записей.

Затем можно найти сообщения, которые прочитал пользователь: posts = UserReadPost.objects.filter(user=current_user).values_list("post", flat=True)

Это также можно относительно легко расширить. Например, если ваши BasicPost объекты могут быть отредактированы, вы можете добавить к сообщению поле updated_at. Затем вы можете сравнить seen_at поля UserReadPost с полем updated_at поля BasicPost, чтобы проверить, видели ли они обновленную версию.

Недостатком является то, что вы будете создавать много строк в БД для этой таблицы.

1 голос
/ 02 марта 2020

По вашему первому вопросу (какой путь к go): я считаю, что ManyToMany по умолчанию создает индексы в БД для обоих внешних ключей. Поэтому, куда бы вы ни поместили отношение, в User или в BasicPost, вы будете иметь прямые и обратные отношения, работающие через индекс. Django создаст для вас сводную таблицу с тремя столбцами, такими как: (id, user_id, basic_post_id). Каждый доступ к этой таблице будет индексироваться через user_id или basic_post_id и проверять наличие уникальной пары (user_id, basic_post_id), если таковая имеется. Таким образом, именно в вашем приложении вы решите, будете ли вы фильтровать набор из 1 миллиона или 50 000 сообщений.

По второму вопросу (как перегрузить пользователя) обычно рекомендуется подкласс пользователя из самое начало. Если это слишком поздно, и ваш проект слишком продвинут для этого, вы можете сделать это в своем models.py:

class BasicPost(models.Model):
    # your code
    readers = models.ManyToManyField(to='User', related_name="posts_already_read")

# "manually" add method to User class
def _unread_posts(user):
    return BasicPost.objects.exclude(readers__in=user)

User.unread_posts = _unread_posts

Хотя этот код не запускался! Надеюсь, это поможет.

0 голосов
/ 02 марта 2020

Если вы размещаете свои сообщения в хронологическом порядке (например, created_at), вы можете выбрать расширить модель пользователя с полем latest_read_post_id.

В этом случае:

class BasicPost(models.Model):
    # your code

    def is_read_by(self, user):
        return self.id < user.latest_read_post_id
...