我正在为一个简单的论坛创建一个数据库模型。用户应该能够创建线程,添加帖子并使用帖子发布图像。
在视图中我想显示所有线程并且:
我认为如果不对n
个线程执行n
查询,这是不可能的,所以真正的问题是如何重新设计数据库以使其成为可能。
class Thread(models.Model):
sticky = models.BooleanField()
...
class Post(models.Model):
thread = models.ForeignKey('Thread')
image = models.OneToOneField('Image', null=True, blank=True, default=None)
date = models.DateTimeField()
...
class Image(models.Model):
image = models.ImageField(...)
...
此时我知道如何计算帖子和图片,但我不知道如何同时获取第一篇文章。我考虑在链接到第一个Thread
的{{1}}模型中添加其他字段。
我的查询迫使我分别下载第一篇文章:
Post
答案 0 :(得分:1)
您可以使用Subquery
在最新相关对象的单个字段上进行注释:
comments = Comment.objects.filter(
post=OuterRef('pk')
).order_by('-timestamp').values('timestamp')
Post.objects.annotate(
last_comment_time=Subquery(comments[:1])
)
您可以用这种方式在几个字段上进行注释,但这会影响性能(每个相关的子查询分别运行,并且对于每一行,这比N + 1查询要好,但比单连接要差)。
您可以在单个字段上构建JSON对象,然后在以下位置添加注释:
comments = Comment.objects.filter(
post=OuterRef('pk')
).annotate(
data=models.expressions.Func(
models.Value('author'), models.F('author'),
models.Value('timestamp'), models.F('timestamp'),
function='jsonb_build_object',
output_field=JSONField()
),
).order_by('-timestamp').values('data')
(甚至有可能将整个对象作为JSON来获取,然后在Django中重新添加它,但这有点hacky)。
另一种解决方案是分别获取最新评论,然后将其与帖子组合:
comments = Comment.objects.filter(
...
).distinct('post').order_by('post', '-timestamp')
posts = Post.objects.filter(...).order_by('pk')
for post, comment in zip(posts, comments):
pass
您需要确保此处的帖子和评论的顺序相同:这些查询是。如果每个帖子都没有评论,这也会失败。
一种解决方法是将评论放入以帖子ID为键的字典中,然后为每个帖子获取匹配的帖子。
comments = {
comment.post_id: comment
for comment in Comment.objects.distinct('post').order_by('post', '-timestamp')
}
for post in Post.objects.filter(...):
top_comment = comments.get(post.pk)
# whatever
答案 1 :(得分:0)
您正在寻找的部分内容是select_related。您还需要像预期的那样使用annotate
。
# I assume you have thread_id given to you.
last_reply = Post.objects.annotate(
thread_images=Count('thread__post_set__image__id', distinct=True),
replies=Count('thread__post_set__id', distinct=True),
).select_related('thread').filter(thread__id=thread_id).order_by('-post_date').first()