Периодические задачи с Celery
Вот еще один общий сценарий. Большинство зрелых веб-приложений отправляют своим пользователям электронные письма c периодичностью. Некоторые распространенные примеры электронных писем с периодической отправкой:
- Ежемесячные отчеты
- Уведомления о деятельности (лайки, запросы в друзья и т. д.)
- Напоминания для выполнения определенных действий («Не забудьте активировать свою учетную запись»)
Вот что мы будем делать в нашем приложении. Мы посчитаем, сколько раз просматривался каждый пост и будем отправлять ежедневный отчет автору. Каждый день мы собираемся пройтись по всем пользователей, загружать их посты и отправлять электронное письмо с таблицей, содержащей посты и количество просмотров.
Давайте изменим модель Post, чтобы мы могли выполнять подсчет просмотров:
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.contrib.auth import get_user_model
User = get_user_model()
class Post(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
created = models.DateTimeField('Created Date', default=timezone.now)
title = models.CharField('Title', max_length=200)
content = models.TextField('Content')
slug = models.SlugField('Slug')
view_count = models.IntegerField("View Count", default=0)
def get_absolute_url(self):
return reverse("home", args=[str(self.id)])
def __str__(self):
return '"%s" by %s' % (self.title, self.author)
Как всегда, когда мы меняем модель, нам нужно выполнить миграцию базы данных:
python manage.py makemigrations
python manage.py migrate
Давайте также изменим Django представление view_post для подсчета просмотров:
def view_post(request, slug):
post = get_object_or_404(Post, slug=slug)
post.view_count += 1
post.save()
return render(request, 'post.html', context={'post': post})
Осталось отобразить view_count в шаблоне. Добавим вывод количества просмотров в шаблоне templates/post.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p>Published by {{ post.author.first_name }} on {{ post.created }}</p>
<p>Viewed {{ post.view_count }} times</p>
</body>
</html>
Сделайте несколько просмотров поста и посмотрите, как увеличивается счетчик:
Давайте создадим задачу Celery. Поскольку речь идет о постах, мы разместим её в publish/tasks.py:
from django.template import Template, Context
from django.core.mail import send_mail
from django.contrib.auth import get_user_model
from publish.celery import app
from publish.models import Post
REPORT_TEMPLATE = """
Here's how you did till now:
{% for post in posts %}
"{{ post.title }}": viewed {{ post.view_count }} times |
{% endfor %}
"""
@app.task
def send_view_count_report():
for user in get_user_model().objects.filter(is_verified=True):
posts = Post.objects.filter(author=user)
if not posts:
continue
template = Template(REPORT_TEMPLATE)
send_mail(
'Your Django_celery Project Activity',
template.render(context=Context({'posts': posts})),
'admin@localhost.ru',
[user.email],
fail_silently=False,
)
Каждый раз, когда Вы вносите изменения в задачи Celery, не забудьте перезапустить процесс Celery. Celery должен обнаружить и перезагрузить задачи.
Первым делом создадим пост от нашего пользователя, которого мы активировали через Email:
Проверим пост, и обновим несколько раз, чтобы увеличить количество просмотров:
Перед созданием задачи, выполняющейся периодически, мы должны проверить это в Django Shell, чтобы убедиться, что все работает как ожидается:
python manage.py shell
from publish.tasks import send_view_count_report
send_view_count_report.delay()
Проверим работу Celery:
Мы видим что у нас теперь в Celery числится 2 задачи, и функция отправки успешно отработала. Теперь давайте откроем нашу почту:
Мы видим что все прекрасно работает, в следующем шаге мы сделаем данную задачу как периодическую.