Использование ORM django для усреднения по полю postgres "timestamp without time zone"

Я пытаюсь усреднить поле postgres timestamp without time zone с помощью отличного ORM от django следующим образом:

from django.db.models import Avg
ModelName.objects.filter(a_field='some value').aggregate(Avg('time'))

Однако я получаю:

function avg(timestamp without time zone) does not exist
LINE 1: SELECT AVG("model_name"."time") AS "time__avg" FROM "m...
           ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Есть ли способ сделать это с помощью ORM от django?
Если нет, то как это обойти?

У меня была похожая проблема, когда я хотел найти среднее время, затраченное на голосование за определенный пункт. Но postgres не позволял брать среднее значение времени по датам. Это приводило к следующей ошибке:

django.db.utils.ProgrammingError: function avg(timestamp with time zone) does not exist
LINE 1: SELECT "votes_item"."name", AVG("votes_vote"."datetime") AS ...
                                    ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Для упрощения рассмотрим следующие таблицы, где Vote имеет внешнее ключевое отношение к Item:

Таблица предметов:

id: pK name: char datetime(time at which the item was inserted)
1 Apple 22-06-23 11:25:33
2 Orange 22-06-22 01:22:18

Таблица голосования:

id: pK user: Fk (user who voted for the item) item: Fk (the item the user voted) vote: (1 for positive vote and -1 negative vote) datetime (time at which the item was voted)
1 1 1 1 2022-06-22 11:26:18
2 3 1 1 2022-06-21 12:26:36
3 2 1 1 2022-06-26 01:20:59

Я хотел узнать среднее время, в течение которого пользователи голосовали за каждый элемент. Например: все среднее время, затраченное пользователями на голосование Apple (т.е. аннотирование)

Поскольку функция postgres avg не принимает данные непосредственно в формате datetime, сначала преобразуйте их в секунды, затем возьмите среднее значение и преобразуйте его обратно в формат datetime.

Чтобы упростить ситуацию, создайте два класса, как показано ниже.

from django.db import models

class Epoch(models.expressions.Func):

    template = 'EXTRACT(epoch FROM %(expressions)s)::FLOAT'
    output_field = models.FloatField()


class DateTimeFromFloat(models.expressions.Func):

    template = 'To_TIMESTAMP(%(expressions)s)::TIMESTAMP at time zone \'UTC\''
    output_field = models.DateTimeField()

читайте больше о Func в этом отличном ответе

Теперь я хотел получить среднее время, в течение которого каждый пункт был проголосован положительно

Поэтому я использую

Item.objects.filter(vote__vote=1).annotate(avg_time=DateTimeFromFloat(Avg(Epoch('vote__datetime')))).values('avg_time', 'name')

важная часть:

annotate(avg_time=DateTimeFromFloat(Avg(Epoch('vote__datetime'))))

output

<QuerySet [{'name': 'Apple', 'avg_time': datetime.datetime(2022, 6, 23, 8, 24, 37, 666667, tzinfo=datetime.timezone.utc)}]>

Вы можете выполнить аналогичную операцию, используя aggregate.

Вернуться на верх