Kacey  Hudson

Kacey Hudson

1616576520

Dockerizing Django with Postgres, Redis and Celery

In this article, we are going to build a dockerized Django application with Redis, celery, and Postgres to handle asynchronous tasks. Basically, the main idea here is to configure Django with docker containers, especially with Redis and celery. At the end of this tutorial, you will able to create your own asynchronous apps easily by using the initial configuration of this project. Before starting, you’ll need a basic understanding of Django, Docker, and Celery to run some important commands.

#django #python

What is GEEK

Buddha Community

Dockerizing Django with Postgres, Redis and Celery
Ahebwe  Oscar

Ahebwe Oscar

1620177818

Django admin full Customization step by step

Welcome to my blog , hey everyone in this article you learn how to customize the Django app and view in the article you will know how to register  and unregister  models from the admin view how to add filtering how to add a custom input field, and a button that triggers an action on all objects and even how to change the look of your app and page using the Django suit package let’s get started.

Database

Custom Titles of Django Admin

Exclude in Django Admin

Fields in Django Admin

#django #create super user django #customize django admin dashboard #django admin #django admin custom field display #django admin customization #django admin full customization #django admin interface #django admin register all models #django customization

鈴木  治

鈴木 治

1660942320

如何使用 Docker 設置 Django、Celery 和 Redis

在構建和擴展 Django 應用程序時,您不可避免地需要在後台定期自動運行某些任務。

一些例子:

  • 生成定期報告
  • 清除緩存
  • 發送批量電子郵件通知
  • 運行夜間維護作業

這是構建和擴展不屬於 Django 核心的 Web 應用程序所需的少數功能之一。幸運的是,Celery 提供了一個強大的解決方案,它很容易實現,稱為 Celery Beat。

在下面的文章中,我們將向您展示如何使用 Docker 設置 Django、Celery 和 Redis,以便使用 Celery Beat 定期運行自定義 Django Admin 命令。

目標

在本教程結束時,您應該能夠:

  1. 使用 Docker 將 Django、Celery 和 Redis 容器化
  2. 將 Celery 集成到 Django 應用程序並創建任務
  3. 編寫自定義 Django Admin 命令
  4. 安排一個自定義的 Django Admin 命令通過 Celery Beat 定期運行

項目設置

django-celery-beat存儲庫中克隆基礎項目,然後查看基礎分支:

$ git clone https://github.com/testdrivenio/django-celery-beat --branch base --single-branch
$ cd django-celery-beat

由於我們總共需要管理四個進程(Django、Redis、worker 和調度程序),我們將使用 Docker 通過將它們連接起來來簡化我們的工作流程,以便它們都可以通過一個命令從一個終端窗口運行.

從項目根目錄,創建鏡像並啟動 Docker 容器:

$ docker-compose up -d --build

接下來,應用遷移:

$ docker-compose exec web python manage.py migrate

構建完成後,導航到http://localhost:1337以確保應用程序按預期運行。您應該看到以下文本:

Orders

No orders found!

在繼續之前快速瀏覽一下項目結構:

├── .gitignore
├── docker-compose.yml
└── project
    ├── Dockerfile
    ├── core
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── entrypoint.sh
    ├── manage.py
    ├── orders
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── products.json
    ├── requirements.txt
    └── templates
        └── orders
            └── order_list.html

想學習如何構建這個項目?查看Dockerizing Django with Postgres、Gunicorn 和 Nginx博客文章。

芹菜和 Redis

現在,我們需要為 Celery、Celery Beat 和 Redis 添加容器。

我們首先將依賴項添加到requirements.txt文件中:

Django==3.2.4
celery==5.1.2
redis==3.5.3

接下來,將以下內容添加到docker-compose.yml文件的末尾:

redis:
  image: redis:alpine
celery:
  build: ./project
  command: celery -A core worker -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis
celery-beat:
  build: ./project
  command: celery -A core beat -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis

我們還需要更新 Web 服務的depends_on部分:

web:
  build: ./project
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - ./project/:/usr/src/app/
  ports:
    - 1337:8000
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis # NEW

完整的 docker-compose.yml文件現在應該如下所示:

version: '3.8'

services:
  web:
    build: ./project
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./project/:/usr/src/app/
    ports:
      - 1337:8000
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  redis:
    image: redis:alpine
  celery:
    build: ./project
    command: celery -A core worker -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  celery-beat:
    build: ./project
    command: celery -A core beat -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis

在構建新容器之前,我們需要在 Django 應用程序中配置 Celery。

芹菜配置

設置

在“core”目錄中,創建一個celery.py文件並添加以下代碼:

import os

from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")

app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

這裡發生了什麼事?

  1. 首先,我們為環境變量設置一個默認值,DJANGO_SETTINGS_MODULE以便 Celery 知道如何找到 Django 項目。
  2. 接下來,我們創建了一個名為 的新 Celery 實例,core並將值分配給名為 的變量app
  3. 然後我們從設置對像中加載 celery 配置值django.conf。我們曾經namespace="CELERY"防止與其他 Django 設置發生衝突。CELERY_換句話說,Celery 的所有配置設置都必須以 為前綴。
  4. 最後,app.autodiscover_tasks()告訴 Celery 從settings.INSTALLED_APPS.

將以下代碼添加到core/__init__.py

from .celery import app as celery_app

__all__ = ("celery_app",)

最後,使用以下 Celery 設置更新core/settings.py文件,以便它可以連接到 Redis:

CELERY_BROKER_URL = "redis://redis:6379"
CELERY_RESULT_BACKEND = "redis://redis:6379"

構建新容器以確保一切正常:

$ docker-compose up -d --build

查看每個服務的日誌以查看它們是否已準備就緒,沒有錯誤:

$ docker-compose logs 'web'
$ docker-compose logs 'celery'
$ docker-compose logs 'celery-beat'
$ docker-compose logs 'redis'

如果一切順利,我們現在有四個容器,每個容器都有不同的服務。

現在我們已經準備好創建一個示例任務來查看它是否可以正常工作。

創建任務

創建一個名為core/tasks.py的新文件,並為僅記錄到控制台的示例任務添加以下代碼:

from celery import shared_task
from celery.utils.log import get_task_logger


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")

安排任務

settings.py文件的末尾,使用 Celery Beat 添加以下代碼以安排sample_task每分鐘運行一次:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
}

在這裡,我們使用CELERY_BEAT_SCHEDULE設置定義了一個週期性任務。我們為任務命名,sample_task然後聲明了兩個設置:

  1. task聲明要運行的任務。
  2. schedule設置任務應該運行的時間間隔。這可以是整數、timedelta 或 crontab。我們為我們的任務使用了一個 crontab 模式來告訴它每分鐘運行一次。您可以在此處找到有關 Celery 日程安排的更多信息。

確保添加導入:

from celery.schedules import crontab

import core.tasks

重新啟動容器以引入新設置:

$ docker-compose up -d --build

完成後,查看容器中的 celery 日誌:

$ docker-compose logs -f 'celery'

您應該會看到類似於以下內容的內容:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task

我們可以看到 Celery 拿起了我們的示例任務,core.tasks.sample_task.

每分鐘您都應該在日誌中看到以“剛剛運行的示例任務”結尾的一行:

celery_1  | [2021-07-01 03:06:00,003: INFO/MainProcess]
              Task core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7] received
celery_1  | [2021-07-01 03:06:00,004: INFO/ForkPoolWorker-8]
              core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7]:
              The sample task just ran.

自定義 Django 管理命令

Django 提供了許多內置django-admin命令,例如:

  • migrate
  • startproject
  • startapp
  • dumpdata
  • makemigrations

除了內置命令外,Django 還為我們提供了創建自己的自定義命令的選項:

自定義管理命令對於運行獨立腳本或從 UNIX crontab 或 Windows 計劃任務控制面板定期執行的腳本特別有用。

因此,我們將首先配置一個新命令,然後使用 Celery Beat 自動運行它。

首先創建一個名為orders/management/commands/my_custom_command.py的新文件。然後,添加運行所需的最少代碼:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        pass

BaseCommand一些方法可以被覆蓋,但唯一需要的方法是handle. handle是自定義命令的入口點。換句話說,當我們運行命令時,就會調用這個方法。

為了測試,我們通常只添加一個快速打印語句。但是,建議stdout.write按照 Django 文檔使用:

當您使用管理命令並希望提供控制台輸出時,您應該寫入 self.stdout 和 self.stderr,而不是直接打印到 stdout 和 stderr。通過使用這些代理,測試您的自定義命令變得更加容易。另請注意,您不需要以換行符結束消息,它將自動添加,除非您指定結束參數。

所以,添加一個self.stdout.write命令:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        self.stdout.write("My sample command just ran.") # NEW

要進行測試,請從命令行運行:

$ docker-compose exec web python manage.py my_custom_command

你應該看到:

My sample command just ran.

有了這個,讓我們把一切聯繫在一起!

使用 Celery Beat 安排自定義命令

現在我們已經啟動了容器,測試了我們可以安排任務定期運行,並編寫了自定義 Django Admin 示例命令,是時候配置 Celery Beat 以定期運行自定義命令了。

設置

在項目中,我們有一個非常基本的應用程序,稱為訂單。它包含兩個模型,ProductOrder。讓我們創建一個自定義命令,發送當天確認訂單的電子郵件報告。

首先,我們將通過此項目中包含的夾具將一些產品和訂單添加到數據庫中:

$ docker-compose exec web python manage.py loaddata products.json

接下來,通過 Django Admin 界面添加一些示例訂單。為此,首先創建一個超級用戶:

$ docker-compose exec web python manage.py createsuperuser

出現提示時填寫用戶名、電子郵件和密碼。然後在 Web 瀏覽器中導航到http://127.0.0.1:1337/admin 。使用您剛剛創建的超級用戶登錄並創建幾個訂單。確保至少有一個confirmed_date今天。

讓我們為我們的電子郵件報告創建一個新的自定義命令。

創建一個名為orders/management/commands/email_report.py的文件:

from datetime import timedelta, time, datetime

from django.core.mail import mail_admins
from django.core.management import BaseCommand
from django.utils import timezone
from django.utils.timezone import make_aware

from orders.models import Order

today = timezone.now()
tomorrow = today + timedelta(1)
today_start = make_aware(datetime.combine(today, time()))
today_end = make_aware(datetime.combine(tomorrow, time()))


class Command(BaseCommand):
    help = "Send Today's Orders Report to Admins"

    def handle(self, *args, **options):
        orders = Order.objects.filter(confirmed_date__range=(today_start, today_end))

        if orders:
            message = ""

            for order in orders:
                message += f"{order} \n"

            subject = (
                f"Order Report for {today_start.strftime('%Y-%m-%d')} "
                f"to {today_end.strftime('%Y-%m-%d')}"
            )

            mail_admins(subject=subject, message=message, html_message=None)

            self.stdout.write("E-mail Report was sent.")
        else:
            self.stdout.write("No orders confirmed today.")

在代碼中,我們使用confirmed_date今天的 a 查詢數據庫中的訂單,將訂單組合成單個消息作為電子郵件正文,並使用 Django 的內置mail_admins命令將電子郵件發送給管理員。

添加一個虛擬管理員電子郵件並將其設置EMAIL_BACKEND為使用控制台後端,以便在設置文件中將電子郵件發送到標準輸出:

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEFAULT_FROM_EMAIL = "noreply@email.com"
ADMINS = [("testuser", "test.user@email.com"), ]

現在應該可以從終端運行我們的新命令了。

$ docker-compose exec web python manage.py email_report

輸出應該類似於:

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
From: root@localhost
To: test.user@email.com
Date: Thu, 01 Jul 2021 12:15:50 -0000
Message-ID: <162514175053.40.5705892371538583115@5140844ebb15>

Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice

-------------------------------------------------------------------------------
E-mail Report was sent.

芹菜節拍

我們現在需要創建一個定期任務來每天運行這個命令。

向core/tasks.py添加一個新任務:

from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.management import call_command # NEW


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")


# NEW
@shared_task
def send_email_report():
    call_command("email_report", )

因此,首先我們添加了一個call_command導入,用於以編程方式調用 django-admin 命令。在新任務中,我們使用call_command自定義命令的名稱作為參數。

要安排此任務,請打開core/settings.py文件,並更新CELERY_BEAT_SCHEDULE設置以包含新任務:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
    "send_email_report": {
        "task": "core.tasks.send_email_report",
        "schedule": crontab(hour="*/1"),
    },
}

在這裡,我們向被CELERY_BEAT_SCHEDULE調用的send_email_report. 正如我們對之前的任務所做的那樣,我們聲明了它應該運行哪個任務——例如core.tasks.send_email_report——並使用 crontab 模式來設置循環。

重新啟動容器以確保新設置生效:

$ docker-compose up -d --build

打開與服務關聯的日誌celery

$ docker-compose logs -f 'celery'

您應該看到send_email_report列出的:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task
celery_1  |   . core.tasks.send_email_report

大約一分鐘後,您應該會看到電子郵件報告已發送:

celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] Content-Type: text/plain; charset="utf-8"
celery_1  | MIME-Version: 1.0
celery_1  | Content-Transfer-Encoding: 7bit
celery_1  | Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
celery_1  | From: root@localhost
celery_1  | To: test.user@email.com
celery_1  | Date: Thu, 01 Jul 2021 12:22:00 -0000
celery_1  | Message-ID: <162514212006.17.6080459299558356876@55b9883c5414>
celery_1  |
celery_1  | Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
celery_1  | Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice
celery_1  |
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] -------------------------------------------------------------------------------
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8]
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] E-mail Report was sent.

結論

在本文中,我們指導您為 Celery、Celery Beat 和 Redis 設置 Docker 容器。然後,我們展示瞭如何使用 Celery Beat 創建自定義 Django Admin 命令和定期任務以自動運行該命令。

尋找更多?

  1. 設置Flower以監控和管理 Celery 工作和工人
  2. 使用單元測試和集成測試來測試 Celery 任務

repo中獲取代碼。

來源:  https ://testdriven.io

#django #celery #docker #redis 

Jarrod  Douglas

Jarrod Douglas

1660934940

Comment Configurer Django, Celery et Redis avec Docker

Lorsque vous créez et mettez à l'échelle une application Django, vous devrez inévitablement exécuter certaines tâches périodiquement et automatiquement en arrière-plan.

Quelques exemples:

  • Génération de rapports périodiques
  • Vider le cache
  • Envoi de notifications par e-mail par lots
  • Exécution de tâches de maintenance nocturnes

C'est l'une des rares fonctionnalités requises pour créer et faire évoluer une application Web qui ne fait pas partie du cœur de Django. Heureusement, Celery fournit une solution puissante et assez facile à mettre en œuvre appelée Celery Beat.

Dans l'article suivant, nous allons vous montrer comment configurer Django, Celery et Redis avec Docker afin d'exécuter régulièrement une commande Django Admin personnalisée avec Celery Beat.

Objectifs

À la fin de ce didacticiel, vous devriez être en mesure de :

  1. Conteneurisez Django, Celery et Redis avec Docker
  2. Intégrez Celery dans une application Django et créez des tâches
  3. Écrire une commande Django Admin personnalisée
  4. Planifiez une commande Django Admin personnalisée à exécuter périodiquement via Celery Beat

Configuration du projet

Clonez le projet de base à partir du référentiel django-celery-beat , puis consultez la branche de base :

$ git clone https://github.com/testdrivenio/django-celery-beat --branch base --single-branch
$ cd django-celery-beat

Étant donné que nous devrons gérer quatre processus au total (Django, Redis, travailleur et planificateur), nous utiliserons Docker pour simplifier notre flux de travail en les connectant afin qu'ils puissent tous être exécutés à partir d'une fenêtre de terminal avec une seule commande .

À partir de la racine du projet, créez les images et lancez les conteneurs Docker :

$ docker-compose up -d --build

Ensuite, appliquez les migrations :

$ docker-compose exec web python manage.py migrate

Une fois la construction terminée, accédez à http://localhost:1337 pour vous assurer que l'application fonctionne comme prévu. Vous devriez voir le texte suivant :

Orders

No orders found!

Jetez un coup d'œil à la structure du projet avant de continuer :

├── .gitignore
├── docker-compose.yml
└── project
    ├── Dockerfile
    ├── core
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── entrypoint.sh
    ├── manage.py
    ├── orders
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── products.json
    ├── requirements.txt
    └── templates
        └── orders
            └── order_list.html

Vous voulez apprendre à construire ce projet ? Consultez l' article de blog Dockerizing Django avec Postgres, Gunicorn et Nginx .

Céleri et Redis

Maintenant, nous devons ajouter des conteneurs pour Celery, Celery Beat et Redis.

Nous allons commencer par ajouter les dépendances au fichier requirements.txt :

Django==3.2.4
celery==5.1.2
redis==3.5.3

Ensuite, ajoutez ce qui suit à la fin du fichier docker-compose.yml :

redis:
  image: redis:alpine
celery:
  build: ./project
  command: celery -A core worker -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis
celery-beat:
  build: ./project
  command: celery -A core beat -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis

Nous devons également mettre à jour la section du service Webdepends_on :

web:
  build: ./project
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - ./project/:/usr/src/app/
  ports:
    - 1337:8000
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis # NEW

Le fichier docker-compose.yml complet devrait maintenant ressembler à ceci :

version: '3.8'

services:
  web:
    build: ./project
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./project/:/usr/src/app/
    ports:
      - 1337:8000
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  redis:
    image: redis:alpine
  celery:
    build: ./project
    command: celery -A core worker -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  celery-beat:
    build: ./project
    command: celery -A core beat -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis

Avant de créer les nouveaux conteneurs, nous devons configurer Celery dans notre application Django.

Configuration Céleri

Installer

Dans le répertoire "core", créez un fichier celery.py et ajoutez le code suivant :

import os

from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")

app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

Qu'est-ce qu'il se passe ici?

  1. Tout d'abord, nous définissons une valeur par défaut pour la DJANGO_SETTINGS_MODULEvariable d'environnement afin que Celery sache comment trouver le projet Django.
  2. Ensuite, nous avons créé une nouvelle instance Celery, avec le nom core, et attribué la valeur à une variable appelée app.
  3. Nous avons ensuite chargé les valeurs de configuration du céleri à partir de l'objet de paramètres de django.conf. Nous avions l'habitude namespace="CELERY"d'empêcher les conflits avec d'autres paramètres de Django. CELERY_En d'autres termes, tous les paramètres de configuration de Celery doivent être précédés de .
  4. Enfin, app.autodiscover_tasks()indique à Celery de rechercher des tâches Celery à partir d'applications définies dans settings.INSTALLED_APPS.

Ajoutez le code suivant à core/__init__.py :

from .celery import app as celery_app

__all__ = ("celery_app",)

Enfin, mettez à jour le fichier core/settings.py avec les paramètres Celery suivants afin qu'il puisse se connecter à Redis :

CELERY_BROKER_URL = "redis://redis:6379"
CELERY_RESULT_BACKEND = "redis://redis:6379"

Construisez les nouveaux conteneurs pour vous assurer que tout fonctionne :

$ docker-compose up -d --build

Jetez un œil aux journaux de chaque service pour voir qu'ils sont prêts, sans erreur :

$ docker-compose logs 'web'
$ docker-compose logs 'celery'
$ docker-compose logs 'celery-beat'
$ docker-compose logs 'redis'

Si tout s'est bien passé, nous avons maintenant quatre conteneurs, chacun avec des services différents.

Nous sommes maintenant prêts à créer un exemple de tâche pour voir si cela fonctionne comme il se doit.

Créer une tâche

Créez un nouveau fichier appelé core/tasks.py et ajoutez le code suivant pour un exemple de tâche qui se connecte simplement à la console :

from celery import shared_task
from celery.utils.log import get_task_logger


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")

Planifier la tâche

À la fin de votre fichier settings.py , ajoutez le code suivant pour planifier sample_taskune exécution une fois par minute, en utilisant Celery Beat :

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
}

Ici, nous avons défini une tâche périodique à l'aide du paramètre CELERY_BEAT_SCHEDULE . Nous avons donné un nom à la tâche, sample_task, puis avons déclaré deux paramètres :

  1. taskdéclare quelle tâche exécuter.
  2. scheduledéfinit l'intervalle d'exécution de la tâche. Il peut s'agir d'un entier, d'un timedelta ou d'un crontab. Nous avons utilisé un modèle crontab pour notre tâche pour lui dire de s'exécuter une fois par minute. Vous pouvez trouver plus d'informations sur la programmation de Celery ici .

Assurez-vous d'ajouter les importations :

from celery.schedules import crontab

import core.tasks

Redémarrez le conteneur pour extraire les nouveaux paramètres :

$ docker-compose up -d --build

Une fois cela fait, jetez un œil aux bûches de céleri dans le conteneur :

$ docker-compose logs -f 'celery'

Vous devriez voir quelque chose de similaire à :

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task

Nous pouvons voir que Celery a récupéré notre exemple de tâche, core.tasks.sample_task.

Chaque minute, vous devriez voir une ligne dans le journal qui se termine par "L'exemple de tâche vient d'être exécuté." :

celery_1  | [2021-07-01 03:06:00,003: INFO/MainProcess]
              Task core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7] received
celery_1  | [2021-07-01 03:06:00,004: INFO/ForkPoolWorker-8]
              core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7]:
              The sample task just ran.

Commande d'administration Django personnalisée

Django fournit un certain nombre de django-admincommandes intégrées, telles que :

  • migrate
  • startproject
  • startapp
  • dumpdata
  • makemigrations

En plus des commandes intégrées, Django nous donne également la possibilité de créer nos propres commandes personnalisées :

Les commandes de gestion personnalisées sont particulièrement utiles pour l'exécution de scripts autonomes ou pour les scripts exécutés périodiquement à partir de la crontab UNIX ou du panneau de configuration des tâches planifiées de Windows.

Donc, nous allons d'abord configurer une nouvelle commande, puis utiliser Celery Beat pour l'exécuter automatiquement.

Commencez par créer un nouveau fichier appelé orders/management/commands/my_custom_command.py . Ensuite, ajoutez le code minimal requis pour qu'il s'exécute :

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        pass

Le BaseCommanda quelques méthodes qui peuvent être remplacées, mais la seule méthode requise est handle. handleest le point d'entrée des commandes personnalisées. En d'autres termes, lorsque nous exécutons la commande, cette méthode est appelée.

Pour tester, nous ajoutons normalement une déclaration d'impression rapide. Cependant, il est recommandé d'utiliser à stdout.writela place selon la documentation de Django :

Lorsque vous utilisez des commandes de gestion et souhaitez fournir une sortie de console, vous devez écrire dans self.stdout et self.stderr, au lieu d'imprimer directement dans stdout et stderr. En utilisant ces proxys, il devient beaucoup plus facile de tester votre commande personnalisée. Notez également que vous n'avez pas besoin de terminer les messages avec un caractère de nouvelle ligne, il sera ajouté automatiquement, sauf si vous spécifiez le paramètre de fin.

Alors, ajoutez une self.stdout.writecommande :

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        self.stdout.write("My sample command just ran.") # NEW

Pour tester, depuis la ligne de commande, exécutez :

$ docker-compose exec web python manage.py my_custom_command

Tu devrais voir:

My sample command just ran.

Sur ce, lions le tout ensemble !

Planifier une commande personnalisée avec Celery Beat

Maintenant que nous avons lancé les conteneurs, testé que nous pouvions programmer une tâche à exécuter périodiquement et écrit un exemple de commande personnalisée Django Admin, il est temps de configurer Celery Beat pour exécuter la commande personnalisée périodiquement.

Installer

Dans le projet, nous avons une application très basique appelée commandes. Il contient deux modèles, Productet Order. Créons une commande personnalisée qui envoie un rapport par e-mail des commandes confirmées de la journée.

Pour commencer, nous allons ajouter quelques produits et commandes à la base de données via le luminaire inclus dans ce projet :

$ docker-compose exec web python manage.py loaddata products.json

Ensuite, ajoutez quelques exemples de commandes via l'interface Django Admin. Pour ce faire, créez d'abord un superutilisateur :

$ docker-compose exec web python manage.py createsuperuser

Remplissez le nom d'utilisateur, l'e-mail et le mot de passe lorsque vous y êtes invité. Accédez ensuite à http://127.0.0.1:1337/admin dans votre navigateur Web. Connectez-vous avec le superutilisateur que vous venez de créer et créez quelques commandes. Assurez-vous qu'au moins un a un confirmed_dated'aujourd'hui.

Créons une nouvelle commande personnalisée pour notre rapport par e-mail.

Créez un fichier nommé orders/management/commands/email_report.py :

from datetime import timedelta, time, datetime

from django.core.mail import mail_admins
from django.core.management import BaseCommand
from django.utils import timezone
from django.utils.timezone import make_aware

from orders.models import Order

today = timezone.now()
tomorrow = today + timedelta(1)
today_start = make_aware(datetime.combine(today, time()))
today_end = make_aware(datetime.combine(tomorrow, time()))


class Command(BaseCommand):
    help = "Send Today's Orders Report to Admins"

    def handle(self, *args, **options):
        orders = Order.objects.filter(confirmed_date__range=(today_start, today_end))

        if orders:
            message = ""

            for order in orders:
                message += f"{order} \n"

            subject = (
                f"Order Report for {today_start.strftime('%Y-%m-%d')} "
                f"to {today_end.strftime('%Y-%m-%d')}"
            )

            mail_admins(subject=subject, message=message, html_message=None)

            self.stdout.write("E-mail Report was sent.")
        else:
            self.stdout.write("No orders confirmed today.")

Dans le code, nous avons interrogé la base de données pour les commandes avec un confirmed_dated'aujourd'hui, combiné les commandes en un seul message pour le corps de l'e-mail et utilisé la mail_adminscommande intégrée de Django pour envoyer les e-mails aux administrateurs.

Ajoutez un e-mail d'administrateur factice et configurez le EMAIL_BACKENDpour qu'il utilise le backend de la console afin que l'e-mail soit envoyé à stdout, dans le fichier de paramètres :

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEFAULT_FROM_EMAIL = "noreply@email.com"
ADMINS = [("testuser", "test.user@email.com"), ]

Il devrait maintenant être possible d'exécuter notre nouvelle commande depuis le terminal.

$ docker-compose exec web python manage.py email_report

Et la sortie devrait ressembler à ceci :

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
From: root@localhost
To: test.user@email.com
Date: Thu, 01 Jul 2021 12:15:50 -0000
Message-ID: <162514175053.40.5705892371538583115@5140844ebb15>

Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice

-------------------------------------------------------------------------------
E-mail Report was sent.

Battement de céleri

Nous devons maintenant créer une tâche périodique pour exécuter cette commande quotidiennement.

Ajoutez une nouvelle tâche à core/tasks.py :

from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.management import call_command # NEW


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")


# NEW
@shared_task
def send_email_report():
    call_command("email_report", )

Donc, nous avons d'abord ajouté une call_commandimportation, qui est utilisée pour appeler par programme les commandes django-admin. Dans la nouvelle tâche, nous avons ensuite utilisé le call_commandavec le nom de notre commande personnalisée comme argument.

Pour planifier cette tâche, ouvrez le fichier core/settings.py et mettez à jour le CELERY_BEAT_SCHEDULEparamètre pour inclure la nouvelle tâche :

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
    "send_email_report": {
        "task": "core.tasks.send_email_report",
        "schedule": crontab(hour="*/1"),
    },
}

Ici, nous avons ajouté une nouvelle entrée au fichier CELERY_BEAT_SCHEDULEappelé send_email_report. Comme nous l'avons fait pour notre tâche précédente, nous avons déclaré la tâche à exécuter -- par exemple, core.tasks.send_email_report-- et utilisé un modèle crontab pour définir la récurrence.

Redémarrez les conteneurs pour vous assurer que les nouveaux paramètres deviennent actifs :

$ docker-compose up -d --build

Ouvrez les journaux associés au celeryservice :

$ docker-compose logs -f 'celery'

Vous devriez voir la send_email_reportliste :

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task
celery_1  |   . core.tasks.send_email_report

Environ une minute plus tard, vous devriez voir que le rapport par e-mail est envoyé :

celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] Content-Type: text/plain; charset="utf-8"
celery_1  | MIME-Version: 1.0
celery_1  | Content-Transfer-Encoding: 7bit
celery_1  | Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
celery_1  | From: root@localhost
celery_1  | To: test.user@email.com
celery_1  | Date: Thu, 01 Jul 2021 12:22:00 -0000
celery_1  | Message-ID: <162514212006.17.6080459299558356876@55b9883c5414>
celery_1  |
celery_1  | Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
celery_1  | Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice
celery_1  |
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] -------------------------------------------------------------------------------
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8]
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] E-mail Report was sent.

Conclusion

Dans cet article, nous vous avons guidé dans la configuration des conteneurs Docker pour Celery, Celery Beat et Redis. Nous avons ensuite montré comment créer une commande Django Admin personnalisée et une tâche périodique avec Celery Beat pour exécuter cette commande automatiquement.

Vous en voulez plus ?

  1. Configurer Flower pour surveiller et administrer les tâches et les travailleurs de Celery
  2. Tester une tâche Celery avec des tests unitaires et d'intégration

Récupérez le code du repo .

Source :  https://testdrive.io

#django #celery #docker #redis 

Wayne  Richards

Wayne Richards

1660927680

Cómo Configurar Django, Celery Y Redis Con Docker

A medida que crea y escala una aplicación de Django, inevitablemente necesitará ejecutar ciertas tareas de forma periódica y automática en segundo plano.

Algunos ejemplos:

  • Generación de informes periódicos
  • Borrando caché
  • Envío de notificaciones por correo electrónico por lotes
  • Ejecución de trabajos de mantenimiento nocturno

Esta es una de las pocas funciones necesarias para crear y escalar una aplicación web que no forma parte del núcleo de Django. Afortunadamente, Celery proporciona una solución poderosa, que es bastante fácil de implementar, llamada Celery Beat.

En el siguiente artículo, le mostraremos cómo configurar Django, Celery y Redis con Docker para ejecutar periódicamente un comando personalizado de administración de Django con Celery Beat.

Objetivos

Al final de este tutorial, debería ser capaz de:

  1. Contenga Django, Celery y Redis con Docker
  2. Integre Celery en una aplicación de Django y cree tareas
  3. Escribir un comando de administración de Django personalizado
  4. Programe un comando de administración de Django personalizado para que se ejecute periódicamente a través de Celery Beat

Configuración del proyecto

Clone el proyecto base del repositorio django-celery-beat y luego revise la rama base :

$ git clone https://github.com/testdrivenio/django-celery-beat --branch base --single-branch
$ cd django-celery-beat

Dado que necesitaremos administrar cuatro procesos en total (Django, Redis, Worker y Scheduler), usaremos Docker para simplificar nuestro flujo de trabajo conectándolos para que todos puedan ejecutarse desde una ventana de terminal con un solo comando. .

Desde la raíz del proyecto, cree las imágenes y active los contenedores de Docker:

$ docker-compose up -d --build

A continuación, aplique las migraciones:

$ docker-compose exec web python manage.py migrate

Una vez que se complete la compilación, vaya a http://localhost:1337 para asegurarse de que la aplicación funcione como se espera. Deberías ver el siguiente texto:

Orders

No orders found!

Eche un vistazo rápido a la estructura del proyecto antes de continuar:

├── .gitignore
├── docker-compose.yml
└── project
    ├── Dockerfile
    ├── core
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── entrypoint.sh
    ├── manage.py
    ├── orders
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── products.json
    ├── requirements.txt
    └── templates
        └── orders
            └── order_list.html

¿Quieres aprender a construir este proyecto? Consulte la publicación de blog Dockerizing Django con Postgres, Gunicorn y Nginx .

Apio y Redis

Ahora, necesitamos agregar contenedores para Celery, Celery Beat y Redis.

Comenzaremos agregando las dependencias al archivo requirements.txt :

Django==3.2.4
celery==5.1.2
redis==3.5.3

A continuación, agregue lo siguiente al final del archivo docker-compose.yml :

redis:
  image: redis:alpine
celery:
  build: ./project
  command: celery -A core worker -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis
celery-beat:
  build: ./project
  command: celery -A core beat -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis

También necesitamos actualizar la depends_onsección del servicio web:

web:
  build: ./project
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - ./project/:/usr/src/app/
  ports:
    - 1337:8000
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis # NEW

El archivo completo docker-compose.yml ahora debería verse así:

version: '3.8'

services:
  web:
    build: ./project
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./project/:/usr/src/app/
    ports:
      - 1337:8000
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  redis:
    image: redis:alpine
  celery:
    build: ./project
    command: celery -A core worker -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  celery-beat:
    build: ./project
    command: celery -A core beat -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis

Antes de construir los nuevos contenedores, debemos configurar Celery en nuestra aplicación Django.

Configuración de apio

Configuración

En el directorio "núcleo", cree un archivo apio.py y agregue el siguiente código:

import os

from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")

app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

¿Que esta pasando aqui?

  1. Primero, establecemos un valor predeterminado para la DJANGO_SETTINGS_MODULEvariable de entorno para que Celery sepa cómo encontrar el proyecto Django.
  2. A continuación, creamos una nueva instancia de Celery, con el nombre corey le asignamos el valor a una variable llamada app.
  3. Luego cargamos los valores de configuración de apio desde el objeto de configuración de django.conf. Solíamos namespace="CELERY"evitar conflictos con otras configuraciones de Django. Todos los ajustes de configuración para Celery deben tener el prefijo CELERY_, en otras palabras.
  4. Finalmente, app.autodiscover_tasks()le dice a Celery que busque las tareas de Celery desde las aplicaciones definidas en settings.INSTALLED_APPS.

Agrega el siguiente código a core/__init__.py :

from .celery import app as celery_app

__all__ = ("celery_app",)

Por último, actualice el archivo core/settings.py con la siguiente configuración de Celery para que pueda conectarse a Redis:

CELERY_BROKER_URL = "redis://redis:6379"
CELERY_RESULT_BACKEND = "redis://redis:6379"

Construya los nuevos contenedores para asegurarse de que todo funcione:

$ docker-compose up -d --build

Eche un vistazo a los registros de cada servicio para ver que están listos, sin errores:

$ docker-compose logs 'web'
$ docker-compose logs 'celery'
$ docker-compose logs 'celery-beat'
$ docker-compose logs 'redis'

Si todo salió bien, ahora tenemos cuatro contenedores, cada uno con diferentes servicios.

Ahora estamos listos para crear una tarea de muestra para ver que funciona como debería.

Crear una tarea

Cree un nuevo archivo llamado core/tasks.py y agregue el siguiente código para una tarea de muestra que solo se registra en la consola:

from celery import shared_task
from celery.utils.log import get_task_logger


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")

Programar la tarea

Al final de su archivo settings.py , agregue el siguiente código para programar sample_taskque se ejecute una vez por minuto, usando Celery Beat:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
}

Aquí, definimos una tarea periódica usando la configuración CELERY_BEAT_SCHEDULE . Le dimos a la tarea un nombre, sample_tasky luego declaramos dos configuraciones:

  1. taskdeclara qué tarea ejecutar.
  2. scheduleestablece el intervalo en el que debe ejecutarse la tarea. Puede ser un número entero, un timedelta o un crontab. Usamos un patrón crontab para nuestra tarea para indicarle que se ejecute una vez por minuto. Puede encontrar más información sobre la programación de Celery aquí .

Asegúrate de agregar las importaciones:

from celery.schedules import crontab

import core.tasks

Reinicie el contenedor para obtener la nueva configuración:

$ docker-compose up -d --build

Una vez hecho esto, eche un vistazo a los troncos de apio en el contenedor:

$ docker-compose logs -f 'celery'

Debería ver algo similar a:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task

Podemos ver que Celery recogió nuestra tarea de muestra, core.tasks.sample_task.

Cada minuto, debería ver una fila en el registro que termina con "La tarea de muestra acaba de ejecutarse":

celery_1  | [2021-07-01 03:06:00,003: INFO/MainProcess]
              Task core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7] received
celery_1  | [2021-07-01 03:06:00,004: INFO/ForkPoolWorker-8]
              core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7]:
              The sample task just ran.

Comando de administración de Django personalizado

Django proporciona una serie de django-admincomandos integrados, como:

  • migrate
  • startproject
  • startapp
  • dumpdata
  • makemigrations

Junto con los comandos incorporados, Django también nos da la opción de crear nuestros propios comandos personalizados :

Los comandos de administración personalizados son especialmente útiles para ejecutar secuencias de comandos independientes o secuencias de comandos que se ejecutan periódicamente desde el crontab de UNIX o desde el panel de control de tareas programadas de Windows.

Entonces, primero configuraremos un nuevo comando y luego usaremos Celery Beat para ejecutarlo automáticamente.

Comience creando un nuevo archivo llamado orders/management/commands/my_custom_command.py . Luego, agregue el código mínimo requerido para que se ejecute:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        pass

BaseCommandtiene algunos métodos que se pueden anular, pero el único método que se requiere es handle. handlees el punto de entrada para los comandos personalizados. En otras palabras, cuando ejecutamos el comando, se llama a este método.

Para probar, normalmente agregaríamos una declaración de impresión rápida. Sin embargo, se recomienda usar en su stdout.writelugar según la documentación de Django:

Cuando utilice comandos de administración y desee proporcionar una salida de consola, debe escribir en self.stdout y self.stderr, en lugar de imprimir directamente en stdout y stderr. Al usar estos servidores proxy, se vuelve mucho más fácil probar su comando personalizado. Tenga en cuenta también que no necesita finalizar los mensajes con un carácter de nueva línea, se agregará automáticamente, a menos que especifique el parámetro de finalización.

Entonces, agregue un self.stdout.writecomando:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        self.stdout.write("My sample command just ran.") # NEW

Para probar, desde la línea de comando, ejecute:

$ docker-compose exec web python manage.py my_custom_command

Debería ver:

My sample command just ran.

Con eso, ¡unamos todo junto!

Programa un Comando Personalizado con Celery Beat

Ahora que hicimos girar los contenedores, probamos que podemos programar una tarea para que se ejecute periódicamente y escribimos un comando de muestra de Django Admin personalizado, es hora de configurar Celery Beat para ejecutar el comando personalizado periódicamente.

Configuración

En el proyecto tenemos una aplicación muy básica llamada pedidos. Contiene dos modelos, Producty Order. Vamos a crear un comando personalizado que envíe un informe por correo electrónico de los pedidos confirmados del día.

Para empezar, agregaremos algunos productos y pedidos a la base de datos a través del accesorio incluido en este proyecto:

$ docker-compose exec web python manage.py loaddata products.json

A continuación, agregue algunos pedidos de muestra a través de la interfaz de administración de Django. Para hacerlo, primero crea un superusuario:

$ docker-compose exec web python manage.py createsuperuser

Complete el nombre de usuario, el correo electrónico y la contraseña cuando se le solicite. Luego navegue a http://127.0.0.1:1337/admin en su navegador web. Inicie sesión con el superusuario que acaba de crear y cree un par de pedidos. Asegúrese de que al menos uno tenga una confirmed_datede hoy.

Vamos a crear un nuevo comando personalizado para nuestro informe de correo electrónico.

Cree un archivo llamado orders/management/commands/email_report.py :

from datetime import timedelta, time, datetime

from django.core.mail import mail_admins
from django.core.management import BaseCommand
from django.utils import timezone
from django.utils.timezone import make_aware

from orders.models import Order

today = timezone.now()
tomorrow = today + timedelta(1)
today_start = make_aware(datetime.combine(today, time()))
today_end = make_aware(datetime.combine(tomorrow, time()))


class Command(BaseCommand):
    help = "Send Today's Orders Report to Admins"

    def handle(self, *args, **options):
        orders = Order.objects.filter(confirmed_date__range=(today_start, today_end))

        if orders:
            message = ""

            for order in orders:
                message += f"{order} \n"

            subject = (
                f"Order Report for {today_start.strftime('%Y-%m-%d')} "
                f"to {today_end.strftime('%Y-%m-%d')}"
            )

            mail_admins(subject=subject, message=message, html_message=None)

            self.stdout.write("E-mail Report was sent.")
        else:
            self.stdout.write("No orders confirmed today.")

En el código, consultamos la base de datos en busca de pedidos con confirmed_datefecha de hoy, combinamos los pedidos en un solo mensaje para el cuerpo del correo electrónico y usamos el mail_adminscomando integrado de Django para enviar los correos electrónicos a los administradores.

Agregue un correo electrónico de administrador ficticio y configure el EMAIL_BACKENDpara usar el backend de la consola , de modo que el correo electrónico se envíe a stdout, en el archivo de configuración:

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEFAULT_FROM_EMAIL = "noreply@email.com"
ADMINS = [("testuser", "test.user@email.com"), ]

Ahora debería ser posible ejecutar nuestro nuevo comando desde la terminal.

$ docker-compose exec web python manage.py email_report

Y la salida debería ser similar a esto:

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
From: root@localhost
To: test.user@email.com
Date: Thu, 01 Jul 2021 12:15:50 -0000
Message-ID: <162514175053.40.5705892371538583115@5140844ebb15>

Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice

-------------------------------------------------------------------------------
E-mail Report was sent.

Batido de apio

Ahora necesitamos crear una tarea periódica para ejecutar este comando diariamente.

Agregue una nueva tarea a core/tasks.py :

from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.management import call_command # NEW


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")


# NEW
@shared_task
def send_email_report():
    call_command("email_report", )

Entonces, primero agregamos una call_commandimportación, que se usa para llamar mediante programación a los comandos django-admin. En la nueva tarea, usamos el call_commandcon el nombre de nuestro comando personalizado como argumento.

Para programar esta tarea, abra el archivo core/settings.py y actualice la CELERY_BEAT_SCHEDULEconfiguración para incluir la nueva tarea:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
    "send_email_report": {
        "task": "core.tasks.send_email_report",
        "schedule": crontab(hour="*/1"),
    },
}

Aquí agregamos una nueva entrada al CELERY_BEAT_SCHEDULEllamado send_email_report. Como hicimos con nuestra tarea anterior, declaramos qué tarea debería ejecutar, por ejemplo, core.tasks.send_email_reporty usamos un patrón crontab para establecer la recurrencia.

Reinicie los contenedores para asegurarse de que la nueva configuración se active:

$ docker-compose up -d --build

Abra los registros asociados con el celeryservicio:

$ docker-compose logs -f 'celery'

Debería ver la send_email_reportlista:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task
celery_1  |   . core.tasks.send_email_report

Aproximadamente un minuto después, debería ver que se envía el informe por correo electrónico:

celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] Content-Type: text/plain; charset="utf-8"
celery_1  | MIME-Version: 1.0
celery_1  | Content-Transfer-Encoding: 7bit
celery_1  | Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
celery_1  | From: root@localhost
celery_1  | To: test.user@email.com
celery_1  | Date: Thu, 01 Jul 2021 12:22:00 -0000
celery_1  | Message-ID: <162514212006.17.6080459299558356876@55b9883c5414>
celery_1  |
celery_1  | Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
celery_1  | Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice
celery_1  |
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] -------------------------------------------------------------------------------
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8]
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] E-mail Report was sent.

Conclusión

En este artículo lo guiamos a través de la configuración de contenedores Docker para Celery, Celery Beat y Redis. Luego mostramos cómo crear un comando Django Admin personalizado y una tarea periódica con Celery Beat para ejecutar ese comando automáticamente.

¿Buscando por mas?

  1. Configure Flower para monitorear y administrar trabajos y trabajadores de Celery
  2. Probar una tarea de Celery con pruebas unitarias y de integración

Tome el código del repositorio .

Fuente:  https://testdriven.io

#django #celery #docker #redis 

Как настроить Django, Celery и Redis с помощью Docker

При создании и масштабировании приложения Django вам неизбежно потребуется периодически и автоматически запускать определенные задачи в фоновом режиме.

Некоторые примеры:

  • Создание периодических отчетов
  • Очистка кеша
  • Отправка пакетных уведомлений по электронной почте
  • Выполнение ночных работ по техническому обслуживанию

Это одна из немногих функций, необходимых для создания и масштабирования веб-приложения, которое не является частью ядра Django. К счастью, Celery предоставляет мощное и достаточно простое в реализации решение под названием Celery Beat.

В следующей статье мы покажем вам, как настроить Django, Celery и Redis с помощью Docker, чтобы периодически запускать пользовательскую команду администратора Django с помощью Celery Beat.

Цели

К концу этого урока вы должны уметь:

  1. Контейнеризируйте Django, Celery и Redis с помощью Docker
  2. Интегрируйте Celery в приложение Django и создавайте задачи
  3. Напишите пользовательскую команду администратора Django
  4. Запланируйте периодическое выполнение пользовательской команды администратора Django через Celery Beat.

Настройка проекта

Клонируйте базовый проект из репозитория django-celery-beat , а затем проверьте базовую ветку:

$ git clone https://github.com/testdrivenio/django-celery-beat --branch base --single-branch
$ cd django-celery-beat

Поскольку нам нужно будет управлять четырьмя процессами в общей сложности (Django, Redis, worker и планировщик), мы будем использовать Docker, чтобы упростить наш рабочий процесс, подключив их так, чтобы их можно было запускать из одного окна терминала с помощью одной команды. .

В корне проекта создайте образы и разверните контейнеры Docker:

$ docker-compose up -d --build

Затем примените миграции:

$ docker-compose exec web python manage.py migrate

После завершения сборки перейдите по адресу http://localhost:1337 , чтобы убедиться, что приложение работает должным образом. Вы должны увидеть следующий текст:

Orders

No orders found!

Прежде чем двигаться дальше, взгляните на структуру проекта:

├── .gitignore
├── docker-compose.yml
└── project
    ├── Dockerfile
    ├── core
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── entrypoint.sh
    ├── manage.py
    ├── orders
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py
    ├── products.json
    ├── requirements.txt
    └── templates
        └── orders
            └── order_list.html

Хотите узнать, как построить этот проект? Ознакомьтесь с сообщением в блоге Dockerizing Django с Postgres, Gunicorn и Nginx .

Сельдерей и Редис

Теперь нам нужно добавить контейнеры для Celery, Celery Beat и Redis.

Мы начнем с добавления зависимостей в файл requirements.txt :

Django==3.2.4
celery==5.1.2
redis==3.5.3

Затем добавьте следующее в конец файла docker-compose.yml :

redis:
  image: redis:alpine
celery:
  build: ./project
  command: celery -A core worker -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis
celery-beat:
  build: ./project
  command: celery -A core beat -l info
  volumes:
    - ./project/:/usr/src/app/
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis

Нам также необходимо обновить depends_onраздел веб-сервиса:

web:
  build: ./project
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - ./project/:/usr/src/app/
  ports:
    - 1337:8000
  environment:
    - DEBUG=1
    - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
    - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  depends_on:
    - redis # NEW

Полный файл docker-compose.yml теперь должен выглядеть так:

version: '3.8'

services:
  web:
    build: ./project
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./project/:/usr/src/app/
    ports:
      - 1337:8000
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  redis:
    image: redis:alpine
  celery:
    build: ./project
    command: celery -A core worker -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis
  celery-beat:
    build: ./project
    command: celery -A core beat -l info
    volumes:
      - ./project/:/usr/src/app/
    environment:
      - DEBUG=1
      - SECRET_KEY=dbaa1_i7%*3r9-=z-+_mz4r-!qeed@(-a_r(g@k8jo8y3r27%m
      - DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
    depends_on:
      - redis

Перед созданием новых контейнеров нам нужно настроить Celery в нашем приложении Django.

Конфигурация сельдерея

Настраивать

В каталоге «core» создайте файл celery.py и добавьте следующий код:

import os

from celery import Celery


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")

app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

Что тут происходит?

  1. Во-первых, мы устанавливаем значение по умолчанию для DJANGO_SETTINGS_MODULEпеременной среды, чтобы Celery знал, как найти проект Django.
  2. Затем мы создали новый экземпляр Celery с именем coreи присвоили значение переменной с именем app.
  3. Затем мы загрузили значения конфигурации сельдерея из объекта настроек из файла django.conf. Раньше мы namespace="CELERY"предотвращали конфликты с другими настройками Django. CELERY_Другими словами, все параметры конфигурации для Celery должны иметь префикс .
  4. Наконец, app.autodiscover_tasks()указывает Celery искать задачи Celery в приложениях, определенных в файлах settings.INSTALLED_APPS.

Добавьте следующий код в core/__init__.py :

from .celery import app as celery_app

__all__ = ("celery_app",)

Наконец, обновите файл core/settings.py со следующими настройками Celery, чтобы он мог подключаться к Redis:

CELERY_BROKER_URL = "redis://redis:6379"
CELERY_RESULT_BACKEND = "redis://redis:6379"

Создайте новые контейнеры, чтобы убедиться, что все работает:

$ docker-compose up -d --build

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

$ docker-compose logs 'web'
$ docker-compose logs 'celery'
$ docker-compose logs 'celery-beat'
$ docker-compose logs 'redis'

Если все прошло хорошо, у нас теперь есть четыре контейнера, каждый с разными сервисами.

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

Создать задачу

Создайте новый файл с именем core/tasks.py и добавьте следующий код для примера задачи, которая просто регистрируется в консоли:

from celery import shared_task
from celery.utils.log import get_task_logger


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")

Расписание задачи

В конце файла settings.pysample_task добавьте следующий код, чтобы запланировать запуск раз в минуту с помощью Celery Beat:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
}

Здесь мы определили периодическую задачу, используя настройку CELERY_BEAT_SCHEDULE . Мы дали задаче имя, sample_taskа затем объявили две настройки:

  1. taskобъявляет, какую задачу выполнять.
  2. scheduleустанавливает интервал, в течение которого задача должна выполняться. Это может быть целое число, timedelta или crontab. Мы использовали шаблон crontab для нашей задачи, чтобы заставить ее запускаться каждую минуту. Вы можете найти больше информации о расписании Celery здесь .

Обязательно добавьте импорт:

from celery.schedules import crontab

import core.tasks

Перезапустите контейнер, чтобы получить новые настройки:

$ docker-compose up -d --build

После этого взгляните на журналы сельдерея в контейнере:

$ docker-compose logs -f 'celery'

Вы должны увидеть что-то похожее на:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task

Мы видим, что Celery подхватил нашу тестовую задачу, core.tasks.sample_task.

Каждую минуту вы должны видеть в журнале строку, заканчивающуюся словами «Только что запущен пример задачи»:

celery_1  | [2021-07-01 03:06:00,003: INFO/MainProcess]
              Task core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7] received
celery_1  | [2021-07-01 03:06:00,004: INFO/ForkPoolWorker-8]
              core.tasks.sample_task[b8041b6c-bf9b-47ce-ab00-c37c1e837bc7]:
              The sample task just ran.

Пользовательская команда администратора Django

Django предоставляет ряд встроенных django-adminкоманд, таких как:

  • migrate
  • startproject
  • startapp
  • dumpdata
  • makemigrations

Наряду со встроенными командами Django также дает нам возможность создавать собственные пользовательские команды :

Пользовательские команды управления особенно полезны для запуска автономных сценариев или сценариев, которые периодически выполняются из crontab UNIX или из панели управления запланированными задачами Windows.

Итак, мы сначала настроим новую команду, а затем воспользуемся Celery Beat для ее автоматического запуска.

Начните с создания нового файла с именем orders/management/commands/my_custom_command.py . Затем добавьте минимальный необходимый код для его запуска:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        pass

Есть BaseCommandнесколько методов , которые можно переопределить, но требуется только один метод handle. handleявляется точкой входа для пользовательских команд. Другими словами, когда мы запускаем команду, вызывается этот метод.

Для проверки мы обычно просто добавляем оператор быстрой печати. Однако вместо этого рекомендуется использовать stdout.writeдокументацию Django:

Когда вы используете команды управления и хотите предоставить консольный вывод, вы должны писать в self.stdout и self.stderr, а не печатать напрямую в stdout и stderr. Используя эти прокси, становится намного проще протестировать пользовательскую команду. Также обратите внимание, что вам не нужно заканчивать сообщения символом новой строки, он будет добавлен автоматически, если вы не укажете конечный параметр.

Итак, добавляем self.stdout.writeкоманду:

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "A description of the command"

    def handle(self, *args, **options):
        self.stdout.write("My sample command just ran.") # NEW

Для проверки из командной строки запустите:

$ docker-compose exec web python manage.py my_custom_command

Тебе следует увидеть:

My sample command just ran.

С этим, давайте свяжем все вместе!

Запланируйте пользовательскую команду с помощью Celery Beat

Теперь, когда мы развернули контейнеры, проверили, что мы можем запланировать периодическое выполнение задачи, и написали пример пользовательской команды администратора Django, пришло время настроить Celery Beat для периодического запуска пользовательской команды.

Настраивать

В проекте у нас есть очень простое приложение под названием заказы. Он содержит две модели Productи Order. Давайте создадим пользовательскую команду, которая отправляет по электронной почте отчет о подтвержденных заказах за день.

Для начала добавим в базу несколько товаров и заказов через фикстуру, включенную в этот проект:

$ docker-compose exec web python manage.py loaddata products.json

Затем добавьте несколько образцов заказов через интерфейс администратора Django. Для этого сначала создайте суперпользователя:

$ docker-compose exec web python manage.py createsuperuser

При появлении запроса введите имя пользователя, адрес электронной почты и пароль. Затем перейдите по адресу http://127.0.0.1:1337/admin в веб-браузере. Войдите в систему с помощью только что созданного суперпользователя и создайте пару заказов. Убедитесь, что хотя бы один confirmed_dateиз них имеет сегодня.

Давайте создадим новую пользовательскую команду для нашего отчета по электронной почте.

Создайте файл с именем orders/management/commands/email_report.py :

from datetime import timedelta, time, datetime

from django.core.mail import mail_admins
from django.core.management import BaseCommand
from django.utils import timezone
from django.utils.timezone import make_aware

from orders.models import Order

today = timezone.now()
tomorrow = today + timedelta(1)
today_start = make_aware(datetime.combine(today, time()))
today_end = make_aware(datetime.combine(tomorrow, time()))


class Command(BaseCommand):
    help = "Send Today's Orders Report to Admins"

    def handle(self, *args, **options):
        orders = Order.objects.filter(confirmed_date__range=(today_start, today_end))

        if orders:
            message = ""

            for order in orders:
                message += f"{order} \n"

            subject = (
                f"Order Report for {today_start.strftime('%Y-%m-%d')} "
                f"to {today_end.strftime('%Y-%m-%d')}"
            )

            mail_admins(subject=subject, message=message, html_message=None)

            self.stdout.write("E-mail Report was sent.")
        else:
            self.stdout.write("No orders confirmed today.")

В коде мы запросили в базе данных заказы с confirmed_dateсегодняшним днем, объединили заказы в одно сообщение для тела электронной почты и использовали встроенную mail_adminsкоманду Django для отправки электронных писем администраторам.

Добавьте фиктивный адрес электронной почты администратора и установите EMAIL_BACKENDдля использования серверную часть консоли , чтобы электронная почта отправлялась на стандартный вывод в файле настроек:

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEFAULT_FROM_EMAIL = "noreply@email.com"
ADMINS = [("testuser", "test.user@email.com"), ]

Теперь можно запустить нашу новую команду из терминала.

$ docker-compose exec web python manage.py email_report

И вывод должен выглядеть примерно так:

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
From: root@localhost
To: test.user@email.com
Date: Thu, 01 Jul 2021 12:15:50 -0000
Message-ID: <162514175053.40.5705892371538583115@5140844ebb15>

Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice

-------------------------------------------------------------------------------
E-mail Report was sent.

Сельдерей Бит

Теперь нам нужно создать периодическую задачу для ежедневного запуска этой команды.

Добавьте новую задачу в core/tasks.py :

from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.management import call_command # NEW


logger = get_task_logger(__name__)


@shared_task
def sample_task():
    logger.info("The sample task just ran.")


# NEW
@shared_task
def send_email_report():
    call_command("email_report", )

Итак, сначала мы добавили call_commandимпорт, который используется для программного вызова команд django-admin. Затем в новой задаче мы использовали call_commandимя нашей пользовательской команды в качестве аргумента.

Чтобы запланировать эту задачу, откройте файл core/settings.py и обновите CELERY_BEAT_SCHEDULEпараметр, чтобы включить новую задачу:

CELERY_BEAT_SCHEDULE = {
    "sample_task": {
        "task": "core.tasks.sample_task",
        "schedule": crontab(minute="*/1"),
    },
    "send_email_report": {
        "task": "core.tasks.send_email_report",
        "schedule": crontab(hour="*/1"),
    },
}

Здесь мы добавили новую запись в CELERY_BEAT_SCHEDULEвызываемый файл send_email_report. Как и для предыдущей задачи, мы объявили, какую задачу она должна запускать, например, core.tasks.send_email_report--, и использовали шаблон crontab для установки повторения.

Перезапустите контейнеры, чтобы новые настройки стали активными:

$ docker-compose up -d --build

Откройте журналы, связанные с celeryсервисом:

$ docker-compose logs -f 'celery'

Вы должны увидеть в send_email_reportсписке:

celery_1  |  -------------- [queues]
celery_1  |                 .> celery           exchange=celery(direct) key=celery
celery_1  |
celery_1  |
celery_1  | [tasks]
celery_1  |   . core.tasks.sample_task
celery_1  |   . core.tasks.send_email_report

Через минуту или около того вы должны увидеть, что отчет по электронной почте отправлен:

celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] Content-Type: text/plain; charset="utf-8"
celery_1  | MIME-Version: 1.0
celery_1  | Content-Transfer-Encoding: 7bit
celery_1  | Subject: [Django] Order Report for 2021-07-01 to 2021-07-02
celery_1  | From: root@localhost
celery_1  | To: test.user@email.com
celery_1  | Date: Thu, 01 Jul 2021 12:22:00 -0000
celery_1  | Message-ID: <162514212006.17.6080459299558356876@55b9883c5414>
celery_1  |
celery_1  | Order: 3947963f-1860-44d1-9b9a-4648fed04581 - product: Coffee
celery_1  | Order: ff449e6e-3dfd-48a8-9d5c-79a145d08253 - product: Rice
celery_1  |
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] -------------------------------------------------------------------------------
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8]
celery_1  |
celery_1  | [2021-07-01 12:22:00,071: WARNING/ForkPoolWorker-8] E-mail Report was sent.

Вывод

В этой статье мы провели вас через настройку контейнеров Docker для Celery, Celery Beat и Redis. Затем мы показали, как создать пользовательскую команду администратора Django и периодическую задачу с Celery Beat для автоматического запуска этой команды.

Ищете больше?

  1. Настройте Flower для мониторинга и управления заданиями и работниками Celery .
  2. Протестируйте задачу Celery как с помощью модульных, так и с интеграционными тестами .

Возьмите код из репозитория .

Источник:  https://testdriven.io

#django #celery #docker #redis