Аннотирование определенных атрибутов поля даты и времени — Не удается преобразовать ключевое слово «toordinal» в поле. Регистрация на «created_at» не разрешена

#python #django #django-orm #python-datetime #django-annotate

Вопрос:

Я разрабатываю систему подсчета очков для сообщений на веб-сайте. Он рассматривает другие реляционные поля (комментарии, представления и reactions_emojis), чтобы иметь более понятный способ упорядочения результатов.

Но так как не желательно, чтобы на первой странице были популярные посты, но слишком старые, я решил рассмотреть время создания поста.

Проблема в том, что значение даты и времени слишком точное, и упорядочение по нему полностью игнорировало бы систему подсчета очков и возвращало бы простую хронологическую ленту.

Во время тестирования некоторых решений я попробовал встроенную функцию toordinal, чтобы иметь единственное значение, представляющее дни, прошедшие с 1 января 1 года. Моя идея состояла в том, чтобы объединить это значение с post_hour и результатом (post_minute // 14). Таким образом, будет 14-минутное окно, в котором все сообщения будут упорядочены исключительно по их результатам. Это выглядит достаточно хорошо, и любые корректировки будут несложными.

Просматривая некоторые сообщения SO и документацию Django, я обнаружил, что могу сделать это, передав атрибут, к которому я пытался получить доступ, с помощью dunder внутри выражения F:

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

Это возвращает следующую ошибку:

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

Это первый раз, когда я использую аннотацию, так что сейчас я немного растерян.

ПРАВКА: Понял! Я опубликую это в качестве ответа после еще некоторого тестирования и заверения, что это не слишком замедлит запросы.

 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")  )   

Комментарии:

1. Что именно __toordinal предполагается делать? Это не встроенный поиск, поэтому вы должны определить свой собственный.

2. Привет, Виллем, я обновил вопросы с решением. Знаете ли вы, может ли это несколько аннотаций значительно замедлить выполнение запроса? Спасибо