kamartem
kamartem

Reputation: 1034

Django: query latest posts from different categories

class Post(models.Model):
    title = ...
    category = models.ForeignKey(Category)
    date = .......

class Category(models.Model):
    title = ....

On the main page i want to display 5 posts with latest dates, but all posts must be from different categories. There is 50 categoriese, for example. Is it possible?

Upvotes: 1

Views: 2845

Answers (5)

Omid Raha
Omid Raha

Reputation: 10680

Using order_by and distinct:

Post.objects.all().order_by('category', 'date').distinct('category')

Then for performance adding index together on category and date fields.

But i think real production way is to use Redis to cache and update an id's list of latest posts of categories.

Upvotes: 1

James T Snell
James T Snell

Reputation: 1678

You bet it's possible.

Randomly select 5 categories

Grab the most recent post for each category

Perhaps something like:

randposts = {} #list for most recent posts of 5 randomly selected categories
for randomcat in Category.objects.order_by('?')[5:]:
    randposts.add(Post.objects.filter(category = randomcat).order_by('date')[0])

This may help: https://docs.djangoproject.com/en/dev/ref/models/querysets/

Upvotes: 1

Chris Pratt
Chris Pratt

Reputation: 239360

from django.db.models import Max

categories = Category.objects.annotate(most_recent=Max(post__date)).order_by('-most_recent')[:5]

posts = list()
for category in categories:
  posts.append(category.post_set.latest())

That annotates the categories with the date of the most recent post as the value of most_recent, which you can then order by to get the 5 most recent categories.

Then, just loop through the categories and pull out the latest post for each. Done.

Upvotes: 6

topynate
topynate

Reputation: 377

Cleaned up version of Doc's code:

catsused = {}
posts = {}

for post in Post.objects.order_by('date'):
    if post.category not in catsused:
        posts.add(post)
        catsused.add(post.category)
        if len(catsused) == 5:
            break

Upvotes: 0

James T Snell
James T Snell

Reputation: 1678

Okay, let's try this again..

catsused = {}
posts = {}
postindex = 0
posts.add(Post.objects.order_by('date')[postindex])
catsused.add(Post.objects.order_by('date')[postindex].category

for each in range(1,5):
    postindex = postindex + 1
    post = Post.objects.order_by('date')[postindex] 
    while post.category in catsused:
        postindex = postindex + 1
        post = Post.objects.order_by('date')[postindex] 
    posts.add(post)
    catsused.add(post.category)

posts ## Now contains your posts

That seems like horrible code to me, but I suppose something close to that will do the job. You'll wanna add some code to handle "postindex" going over the number of posts there are in the system too.

Upvotes: 1

Related Questions