【问题标题】:Limit number of objects of a self reference model with Prefetch Django使用 Prefetch Django 限制自引用模型的对象数
【发布时间】:2021-09-02 10:45:25
【问题描述】:

我有 2 个模型:发布和评论。像这样:

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

class Comment(models.Model)
    content = models.TextField()
    owner = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
    post_parent = models.ForeignKey(
        Post,
        on_delete=models.CASCADE,
        null=False,
        db_column='post_parent',
        related_name='comments',
    )
    comment_ref = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        related_name='children_comment'
    )

假设 cmets 可以有 1 个级别的子 cmets。使用帖子的 ID,我想获得 5 个最新的 cmets(不是子评论),每个都预取了 3 个最新的子 cmets。 搜索后我目前的实现是:

sub_comments = Comment.objects.filter(
                  comment_ref=OuterRef('pk'),
                  post_parent=post_id
               ).order_by('-timestamp').values_list('id', flat=True)[:3]
queryset = Comment.objects.filter(
               comment_ref=None,post_parent=post_id
           ).select_related('owner').prefetch_related(
                    Prefetch('children_comment', queryset=Comment.objects.filter(
                    id__in=Subquery(sub_comments)
                ), to_attr="cmt"))

那行不通。它不限制预取子 cmets 的数量。请帮助我:D

【问题讨论】:

  • 正如@dean 所说,我忘记了模型中的时间戳字段。

标签: django django-models limit self-reference prefetch


【解决方案1】:

Comment 表中需要一个日期字段,以便显示最新的 cmets 和子 cmets。

我将在这里介绍另一种模型,以便更容易为我个人查询和编写测试。 模型.py

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


class Comment(models.Model)
    content = models.TextField()
    owner = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
    post_parent = models.ForeignKey(
        Post,
        on_delete=models.CASCADE,
        null=False,
        db_column='post_parent',
        related_name='comments',
    )
    date_posted = models.DateTimeField()


class ChildComment(models.Model):
    parent_comment = models.ForeignKey(
        Comment,
        on_delete=models.CASCADE,
        null=True,
        related_name="child_comments"
    )

Views.py

post = Post.objects.get(id=<some_id>)
comments = Comment.objects.filter(
    post_parent_id=post.id
).prefetch_related(
    'child_comments'
).order_by(
    '-date_posted'
)[:5]

recent_comments = {}
for comment in comments:
    recent_comments[comment] = [
        comment.child_comments.all().order_by(
            '-parent_comment__date_posted'
        )[:3]
    ]

recent_comments 传递给模板context,然后在模板文件中像这样使用它:

{% for recent_comment, child_comments in recent_comments.items %}
    {{ recent_comment.content }}
    {% for child_comment in child_comments  %}
        {{ child_comment.content }}
    {% endfor %}
{% endfor %}

【讨论】:

  • 感谢 Dean 的帮助,但这不是我想要的。首先,我只想使用 1 个表格进行评论。其次,我要求的是限制 Prefetch 的对象,之后不要使用循环。我认为它似乎表现不佳。我找到了这个code.djangoproject.com/ticket/26780#comment:6。但在我的情况下,它不适用于自引用表。如果将 cmets 分成 2 个表,我认为这也更好
  • 我添加了一个新答案,就性能而言,您最多只能收到 5 个父 cmets,每个父 cmets 最多有 3 个子 cmets,因此性能会很快。如果发现任何明显的性能问题,我会说返回的行数很少,我会在这里更关注可读性而不是性能。
  • 是的,我想是的。但我认为我们正在寻找解决方案,而不是解决方法。谢谢^^
【解决方案2】:

您需要DateTimeField 才能在order_by 中获取最新的 cmets 和子 cmets,但此查询应该可以工作。如果您要返回数千个 cmets,这可能并不理想,但一次只能返回 5 个,这样会很快。

comments = Comment.objects.filter(
    post_parent_id=post.id
)[:5]
root_comment_ids = [comment.id for comment in comments]

child_comments = Comment.objects.filter(
    comment_ref_id__in=root_comment_ids
)[:3]

【讨论】:

    猜你喜欢
    • 2021-02-22
    • 1970-01-01
    • 2013-02-14
    • 2020-04-01
    • 2021-01-25
    • 1970-01-01
    • 2016-01-18
    • 2015-04-23
    • 1970-01-01
    相关资源
    最近更新 更多