Django 多线程评论系统

1 投票
1 回答
959 浏览
提问于 2025-04-18 14:05

(抱歉我的英语不好)

我正在学习Python和Django。现在,我的挑战是开发一个可以进行多层评论的通用评论系统。这里有两个模型,分别是帖子(Post)和评论(Comment)。

- 帖子可以被评论。

- 评论也可以被评论。(可以无限层级)

- 系统中不应该出现n+1查询的问题。(无论评论有多少,都不应该增加查询的次数)

我现在的模型是这样的:

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

    child = generic.GenericRelation(
        'Comment',
        content_type_field='parent_content_type',
        object_id_field='parent_object_id'
    )

class Comment(models.Model):
    content = models.TextField()

    child = generic.GenericRelation(
        'self',
        content_type_field='parent_content_type',
        object_id_field='parent_object_id'
    )

    parent_content_type = models.ForeignKey(ContentType)
    parent_object_id = models.PositiveIntegerField()
    parent = generic.GenericForeignKey(
        "parent_content_type", "parent_object_id")

我的模型设计得对吗?我该如何获取某个帖子的所有评论(包括层级关系),而不出现n+1查询的问题呢?

注意:我知道mttp和其他模块,但我想学习这个系统。


编辑:我运行了“Post.objects.all().prefetch_related("child").get(pk=1)”这个命令,这样我得到了帖子和它的子评论。但是当我想获取子评论的子评论时,又会运行一个新的查询。我可以把命令改成...prefetch_related("child__child__child...")...,但这样每一层的父子关系都会运行新的查询。有没有人知道怎么解决这个问题?

1 个回答

2

如果你想通过一次查询获取某个帖子下的所有评论,那么最好让每条评论都链接到相关的帖子。同时,你可以用一个单独的链接来表示父评论。

简单来说:

class Post(models.Model):
    ...
    comments = models.ManyToManyField('Comment')
    # link to all comments, even children of comments

class Comment(models.Model):
    ...
    child_comments = models.ManyToManyField('Comment')
    # You may find it easier to organise these into a tree 
    # if you use a parent_comment ForeignKey. That way the
    # top level comments have no parent and can be easily spotted.

Post.objects.all().select_related('comments').get(pk=1)

在这种情况下,很多对很多的关系需要多花一点功夫来建立关联,因为它需要一个中间表。如果你想要一个纯粹的一对多关系,那么你需要在Comment上使用一个ForeignKey,但这样你就只能使用prefetch_related,而不能使用select_related,这会导致多一次数据库查询。

这样做的好处是,你不会有一个没有类型的外键引用(你的PostitiveIntegerField)。

接下来,你需要把评论整理成树状结构,但这超出了你问题的范围。

撰写回答