我有一个模型Page
,上面可以有Post
个。我想要做的是获取每个Page
,以及该页面上的最新Post
。如果Page
没有Post
,我仍然需要该页面。 (听起来很熟悉?这是SQL中的LEFT JOIN
。
以下是我目前的情况:
Page.objects.annotate(most_recent_post=Max('post__post_time'))
这只会获得Page
次,但不会获得Post
次。我怎样才能得到Post
?
型号:
class Page(models.Model):
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add = True)
enabled = models.BooleanField(default = True)
class Post(models.Model):
user = models.ForeignKey(User)
page = models.ForeignKey(Page)
post_time = models.DateTimeField(auto_now_add = True)
答案 0 :(得分:2)
根据两者之间的关系,您应该能够轻松follow the relationships,并使用select_related
提高效果
考虑到这一点:
class Page(models.Model):
...
class Post(models.Model):
page = ForeignKey(Page, ...)
您可以使用posts
有效地关注转发关系(即获取所有select_related
及其相关网页):
Post.objects.select_related('page').all()
这将导致只有一个(更大的)查询,其中预取了所有页面对象。
在反向情况下(与您一样),您想要获取所有pages
及其关联的posts
,select_related
将无效。有关您可以执行的操作的详细信息,请参阅this,this和this问题。
答案 1 :(得分:1)
可能您最好的选择是使用django文档中描述的技术:Following Links Backward。
完成后:
pages = Page.objects.annotate(most_recent_post=Max('post__post_time'))
posts = [page.post_set.filter(post_time=page.most_recent_post) for page in pages]
然后帖子[0]应该有最新的帖子[0]等。我不知道这是否是最有效的解决方案,但这是另一篇文章中提到的关于缺少左连接的解决方案在django。
答案 2 :(得分:1)
您可以创建一个database view,其中包含所有Page
列以及必要的最新Post
列:
CREATE VIEW `testapp_pagewithrecentpost` AS
SELECT testapp_page.*, testapp_post.* -- I suggest as few post columns as possible here
FROM `testapp_page` LEFT JOIN `testapp_page`
ON test_page.id = test_post.page_id
AND test_post.post_time =
( SELECT MAX(test_post.post_time)
FROM test_post WHERE test_page.id = test_post.page_id );
然后,您需要创建一个标记为managed = False
的模型(以便manage.py sync
不会中断)。您还可以使用inheritance from abstract Model来避免列重复:
class PageWithRecentPost(models.Model): # Or extend abstract BasePost ?
# Page columns goes here
# Post columns goes here
# We use LEFT JOIN, so all columns from the
# 'post' model will need blank=True, null=True
class Meta:
managed = False # Django will not handle creation/reset automatically
通过这样做,您可以执行您最初想要的操作,因此只需一个查询从两个表中获取:
pages_with_recent_post = PageWithRecentPost.objects.filter(...)
for page in pages_with_recent_post:
print page.name # Page column
print page.post_time # Post column
然而,这种方法并非没有缺点:
Page/Post
中的更改需要重新创建视图。