【问题标题】:Django project architecture adviceDjango 项目架构建议
【发布时间】:2020-03-02 15:00:47
【问题描述】:

我有一个 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

另外,我使用基本 django 应用附带的基本用户模型。

我想保存每个用户已阅读的女巫帖子,以便我可以将他未阅读的帖子发送给他。 我的问题是最好的方法是什么,如果我使用多对多字段,我应该把它放在用户模型上并保存他读过的所有帖子还是应该在另一个方向做,把多对多帖子模型中的字段并为每个帖子保存女巫用户阅读它? Post 模型中将有超过 100 万个帖子和大约 50,000 个用户,我想做最好的过滤器将未读帖子返回给用户

如果我应该使用第一个选项,我该如何扩展 User 模型? 谢谢!

【问题讨论】:

    标签: django django-models django-rest-framework django-views


    【解决方案1】:

    关于您的第一个问题(走哪条路):我相信 ManyToMany 默认情况下会在数据库中为两个外键创建索引。因此,无论您将关系放在 User 中还是 BasicPost 中,您都将通过索引获得直接和反向关系。 Django 将为您创建一个包含三列的数据透视表,例如:(id, user_id, basic_post_id)。对该表的每次访问都将通过 user_id 或 basic_post_id 进行索引,并检查是否存在唯一的一对(user_id、basic_post_id),如果有的话。因此,您将更多地在您的应用程序中决定是从 100 万个集合中还是从 50k 个帖子中过滤。

    关于第二个问题(如何重载 User),通常建议从一开始就对 User 进行子类化。如果为时已晚,并且您的项目太先进了,您可以在您的 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
    

    虽然没有运行此代码!希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      您能否有一个单独的ReadPost 模型,而不是一个可能很大的 m2m,当用户阅读帖子时您可以保存它?这样,您只需查询 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 字段。然后您可以将UserReadPost 字段的seen_atBasicPostupdated_at 字段进行比较,以检查他们是否看到了更新版本。

      缺点是您会在数据库中为此表创建很多行。

      【讨论】:

      • 使用多对多字段会更好吗?因为我知道多对多会自动创建一个像你写的表
      【解决方案3】:

      如果您将帖子按时间顺序排列(例如,created_at),您可以选择 extend user modellatest_read_post_id 字段。

      这个案例:

      class BasicPost(models.Model):
          # your code
      
          def is_read_by(self, user):
              return self.id < user.latest_read_post_id
      

      【讨论】:

        猜你喜欢
        • 2021-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-04
        • 2010-11-08
        相关资源
        最近更新 更多