#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. Привет, Виллем, я обновил вопросы с решением. Знаете ли вы, может ли это несколько аннотаций значительно замедлить выполнение запроса? Спасибо