最后N个帖子每个类别SQL到ORM

时间:2017-10-01 13:54:29

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

我在完成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。

2 个答案:

答案 0 :(得分:1)

您的解决方案已基本完成。

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发布后更新)

答案 1 :(得分:0)

我认为你想要最后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 %}