Как агрегировать пользовательскую функцию модели, если она не является частью базы данных в django?

У меня есть проект, который мне нужно открыть и закрыть билеты. Итак, это моя модель билета:

class Ticket(models.Model):
 issue = models.CharField(max_length=100)
 user = models.ForeignKey('Users', blank=True, null=True, related_name="tickets")
 date_opened = models.DateTimeField('Date opened')
 date_closed = models.DateTimeField('Date closed', blank=True, null=True)

 def __str__(self):
 return self.issue

 def time_to_solve(self):
 time_to_solve = self.date_opened - self.date_closed
 out = [ time_to_solve.hours//60 ]
 return '{:d} hours'.format(*out)

и я хочу рассчитать среднее значение разницы во времени между date_opened и date_closed.

В моем view.py я создал представление:

class Dashboard(ListView):
 model = Ticket
 template_name = 'assets/dashboard.html'
 def get_context_data(self, **kwargs):
 context = super(Dashboard, self).get_context_data(**kwargs)
 context['time_to_complete'] = Q(status__contains='closed')).aggregate(time_opened = Avg('time_to_solve'))
 return context

К сожалению, это не работает, потому что "time_to_solve" не является частью базы данных.

Как я могу это достичь?

3 ответа

Я не думаю, что вы можете сделать это напрямую с ORM. Вы можете сделать это в Python, но это будет извлекать все закрытые строки Билета из базы данных. Если вы хотите сделать это в SQL, вам нужно будет выразить свой запрос как необработанный SQL. Если вы используете PostgreSQL, вы можете найти это полезным: работа с датами и временем в PostgreSQL.


Найден ответ от друга в #irc - django на Freenode:

average = Ticket.objects.extra(
 select={ 'date_difference': 'AVG(time_to_sec(TIMEDIFF(date_closed,date_opened)))'}).first().date_difference
 context['average'] = "{:.2f}".format(average/86400)
 return context

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


Вы можете только заполнить поля модели, но это не сложно сделать в python:

tickets = Ticket.objects.filter(status__contains='closed') 
average = sum(map(lambda x: x.time_to_solve(), tickets)) / tickets.count()

В этом случае time_to_solve должен возвращать что-то вроде количества секунд, и вы можете отформатировать его по мере необходимости.

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

licensed under cc by-sa 3.0 with attribution.