【问题标题】:Last N posts per category SQL to ORM每个类别的最后 N 个帖子 SQL 到 ORM
【发布时间】:2018-03-12 18:57:55
【问题描述】:

[在完成 Django 官方教程和 Django Girls 教程之后],我开始了我的第一个 Django 博客应用程序。我在理解 Django ORM 时遇到了一些麻烦。我有这个有效的 SQL 查询,但我觉得它效率很低,我宁愿在继续之前学习正确的方法。

Post.objects.raw(('SELECT * FROM (SELECT id, category, body, slug, author, title, 
published, row_number() OVER (PARTITION BY category) as rownum FROM post) tmp 
WHERE rownum < 5'))

基本上我想为每个帖子类别显示最后 5 行。上面的代码已经可以工作了,问题是当我循环遍历模板中的每个帖子时,它会在调用 Post 的 get_absolute_url 方法时为每个帖子运行额外的查询。我使用 Django 的 {% url %} 标记修复了这个问题,但仍然运行了 7 个额外的查询,我想将其限制为最多 2-3 个。

我有一个这样的模型:

class Post(models.Model):
    title = models.CharField(max_length=250)
    category = models.CharField(max_length=30, choices=CATEGORY_CHOICES)
    tags = TaggableManager()
    slug = models.SlugField(max_length=250, unique_for_date='published')
    author = models.CharField(max_length=50)
    body = HTMLField('body')
    published = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES,
                             default='draft')

    class Meta:
        ordering = ('-published',)
        db_table = 'post'

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post_detail', args=[self.category, self.slug])

我尝试了几个小时通过 ORM 让它工作,但无法让它工作。我最终在原始 SQL 中执行此操作,但我不知道会运行 20 多个附加查询。主要目标是显示按发布日期排序的每个类别的最后 5 个帖子。我正在使用 PostgreSQL。

【问题讨论】:

  • +1。您的解决方案几乎完成了。需要一个小修复来获得正常的对象,实际上最后 5 个帖子不是随机的 5 个帖子。

标签: sql django django-models django-queryset django-orm


【解决方案1】:

我想你想要最后 3 个帖子,如果是这样你可以切片

def mayview(request):
    last_posts = Post.objects.all().order_by('-created_at')[:3]  # list of last posts
    return render(request, 'path/to/template', {'last_posts': last_posts})

在您的模板中使用 for 循环解压列表:

{%for post in last_posts%}

<h1>{{post.title}}</h1>
<p>{{post.body}}</p>
<small>{{post.created_by}}</small>

{% endfor %}

【讨论】:

  • posts = Post.objects.all().annotate(Count("category")).order_by('category', '-published') 这更像是我需要的,但我需要如果我使用切片,每个类别有 5 个帖子,那么它只返回最后 5 个帖子。因此,如果有 4 个类别并且每个类别中有 n 个帖子,那么使用上面的代码排序的每个类别只需要 5 个帖子。
【解决方案2】:

您的解决方案几乎完成了。

from django.db.models.expressions import RawSQL

qs = Post.objects.filter(pk__in=RawSQL(
    'SELECT id FROM '
    '  (SELECT id, '
    '    row_number() OVER (PARTITION BY category ORDER BY published DESC) as rownum '
    '   FROM post) tmp '
    'WHERE rownum < 5',
    []
)).order_by('category', '-published')

Window functions 可以在 Django 2.0 中使用,而不是 RawSQL。 (应在2.0发布后更新)

【讨论】:

    猜你喜欢
    • 2011-03-14
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 2015-03-16
    • 2016-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多