Django. Автоматический вызов метода при обращении к модели

172 просмотра
0
0 Комментариев

В проекте на Django, есть обычная модель со своими полями и методами, где данные привязаны к конкретному пользователю, ничего необычного. Появилась необходимость сделать так, чтобы через определённый промежуток времени одно из полей обновляло свои данные по формуле. Для примера возьмём поле rank(рейтинг), которое должно каждые 3 часа увеличиваться, скажем на 100. Так как нет явной необходимости, чтобы в БД всегда находились самые свежие данные, решил не делать обновление пока нет вывода на экран этих данных. Для большего понимания: если надо провести какие-то манипуляции с данными при сохранении, то, можно переопределить метод save. А как сделать подобное при запросе типа «select»?


Добавить комментарий

1 Ответы

Python Опубликовано 12.12.2018
0

Никогда не редактируйте объекты неявно! За изменение базы при обычном select я бы больно побил. При select все нормальные люди ожидают, что данные будут лишь получены, а не изменены.

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

from datetime import datetime, timedelta
 
class User(models.Model):
    # ... всякие другие поля ...
    rank = models.PositiveIntegerField(default=0)
    registered_at = models.DateTimeField(auto_now_add=True)
 
    # при регистрации здесь ставим дату регистрации плюс 3 часа (в UTC)
    next_rank_update_at = models.DateTimeField()
 
    def update_and_get_rank_value(self, save=True):
        changed = False
 
        # Алгоритм подсчёта рейтинга можно оптимизировать, но мне лень
        now = datetime.utcnow()
        while self.next_rank_update_at <= now:
            self.rank += 100
            self.next_rank_update_at += timedelta(hours=3)
            changed = True
 
        if changed and save:
            self.save()
 
        return self.rank

Тогда в шаблонах можно выводить рейтинг примерно так:

<b>Рейтинг пользователя: {{ user.update_and_get_rank_value }}</b>

Когда вывода на экран нет, метод никем не вызывается, и рейтинг в базе будет старый. Когда вывод понадобится, будет вызван метод из шаблона, рейтинг пересчитается и данные в базе обновятся — всё как вы хотели 🙂

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

from datetime import datetime
 
class User(models.Model):
    registered_at = models.DateTimeField(auto_now_add=True)
 
    def get_rank_value(self):
        delta = datetime.utcnow() - self.registered_at
        return delta.total_seconds() // 3600 // 3 * 100

Добавить комментарий
Напишите свой ответ на данный вопрос.
Scroll Up