0

I'm developing a scoring system for posts on a website. It considers other relational fields (comments, views, and reactions_emojis) to have a more insightful way to order the results.

But since it's not desirable to have popular posts, but too old, on the front page, I decided to consider the post creation time.

The problem is, the DateTime value is too precise, and ordering by it would completely ignore the scoring system and return a simple chronological feed.

While testing some solutions, I tried the toordinal built function to have a single value representing the days passed since January 1 of year 1. My idea was to concatenate this value with the post_hour and the result of (post_minute // 14). This way, there would be a 14 minutes window in which all posts would be ordered exclusively by their scores. It looks good enough, and any adjustments would be simple to make.

Looking along with some SO posts and Django documentation, I found that I could do this by passing the attribute I was trying to access with a dunder inside an F expression:

posts = Post.objects.all().annotate(
            score=((F("view_count")/20) + (Count("post_emojis")/10) + (Count("post_comments")/5)),
            ordinal_time=(F('created_at__toordinal'))
        )        

This returns the following error:

Cannot resolve keyword 'toordinal' into field. Join on 'created_at' not permitted.

That's the first time I'm using annotate, so I'm kind of lost right now.

EDIT: Got it! I'll post this as an answer after some more testing and assuring this won't slow down too much the querying.

posts = Post.objects.all().annotate(
            score=((F("view_count")/20) + (Count("post_emojis")/10) + (Count("post_comments")/5)),
            year=(Extract(expression="created_at", lookup_name='year')),
            month=(Extract(expression="created_at", lookup_name='month')),
            day=(Extract(expression="created_at", lookup_name='day')),
            hour=(Extract(expression="created_at", lookup_name='hour')),
            minute=(Extract(expression="created_at", lookup_name='minute')),
            divided_minute=(Cast((F("minute") / 14), output_field=IntegerField())),
            time=Concat("year", "month", "day", "hour", "divided_minute")
        )

andrepz
  • 443
  • 6
  • 16
  • 1
    What exactly is the `__toordinal` supposed to do? This is not a builtin lookup, hence you should define your own. – Willem Van Onsem Nov 11 '21 at 17:12
  • Hi Willem, I've updated the questions with a solution. Do you know if this multiple annotations can slow down significantly the query? Thank You – andrepz Nov 11 '21 at 17:29

0 Answers0