Продвинутый Django 5 для продолжающих

Прогресс по курсу:  0/193

9.4 Периодические задания
2 из 2 шагов пройдено

Периодические задачи с 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 задачи, и функция отправки успешно отработала. Теперь давайте откроем нашу почту:


Мы видим что все прекрасно работает, в следующем шаге мы сделаем данную задачу как периодическую.


Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

Почему такое может быть на windows, все задачи выполняются, но очень долго, письма приходят на почту, но после этого в консоли ошибка
[2024-01-13 16:12:44,508: ERROR/MainProcess] Task publish.tasks.send_view_count_report[cab74e9e-bdb5-42a0-9460-7097c95ce4bf] raised unexpected: ConnectionError('Error 11002 connecting to localhost:6379. Lookup timed out.')

Redis запущен, выше в логах так
[2024-01-13 16:09:31,885: INFO/MainProcess] Connected to redis://localhost:6379/0
[2024-01-13 16:09:40,927: INFO/MainProcess] mingle: searching for neighbors
[2024-01-13 16:10:07,126: INFO/MainProcess] mingle: all alone
[2024-01-13 16:10:15,175: INFO/MainProcess] pidbox: Connected to redis://localhost:6379/0.
[2024-01-13 16:10:31,281: INFO/MainProcess] celery@DESKTOP-274TGCA ready.
[2024-01-13 16:11:41,234: INFO/MainProcess] Task publish.tasks.send_view_count_report[cab74e9e-bdb5-42a0-9460-7097c95ce4bf] received

@Николай_Петров, попробуйте заменить localhost на 127.0.0.1 в настройках, так-же может помочь очистка кэша DNS, выполните в командной строке ipconfig /flushdns.