Celery & Django

Celery es un framework para el manejo de tareas basado en colas, basandoce en el patrón de diseño Productor consumidor donde el productor le envía trabajo a realizar al consumidor celery se encarga de manejarnos tanto el manejo de la cola de trabajos (jobs) como los workers (consumidores / trabajadores) que serán los que realicen dicha tarea. En este post estaremos integrando Django & Celery y algunos casos de usos para los que son buenos.

Podemos representar celery en el siguiente diagrama:

celery_django_integracion

Donde podemos ver que Celery en quien encola el job y es quien maneja los workers para procesar dichos jobs.

Entre las características principales de Celery se encuentran:

  • Poder ejecutar tareas de forma asíncronas.
  • Ejecutar tareas de forma recurentes algo asi como crontabs.
  • Ejecutar tareas con una fecha en especifico.

Celery es uno de los frameowrks mas importantes que he conocido en el entorno python, todo proyecto que quiera realizar una actividad en background lo primero que debe pensar es en Celery. Celery se integra bien con django y es lo que veremos ahora.

Instalacion

Primero creamos nuestro proyecto django

django-admin startproject django_celery_tutorial

Luego creamos nuestro entorno dentro del proyecto django y lo activamos

cd django_celery_tutorial
virtualenv env
source env/bin/activate

Ahora instalamos los paquetes celery y djcelery

pip install celery
pip install django-celery

Luego tendremos que agregar ‘djcelery’ a nuestro archivo config, en el area de INSTALLED_APPS

#settings.py

INSTALLED_APPS = [

    'djcelery',
]

Si probamos en nuestra terminal python manage.py veremos que tendremos unos nuevos comandos de djacelery

[djcelery]
    celery
    celerybeat
    celerycam
    celeryd
    celeryd_detach
    celeryd_multi
    celerymon
    djcelerymon

Ahora vamos a crear una aplicación simple para nuestro proyecto, la misma tendrá la funcionalidad de enviar un correo electrónico utilizando sendmail de Linux.

django-admin startapp app_mail

Luego tendremos que agregar nuestra aplicacion ya creada a nuestras INSTALLED_APPS

Una vez instaladas vamos a generar y correr las migraciones.

python manage.py makemigrations
python manage.py migrate

Como estamos creando un proytecto simple la base de datos que este proyecto tendra sera sqlite.

Creamos una vista simple que solo imprima un texto para fines de prueba y ejecucion de Celery.

//app_mail/views.py
from django.shortcuts import render, HttpResponse

# Create your views here.
def index(request):
    return HttpResponse("Hola")

Y agregaremos esa vista a nuestro archivo de url.py

//django_celery_tutorial/urls.py
from django.conf.urls import url
from django.contrib import admin

from app_mail.views import index

urlpatterns = [
    url(r'^index/', index),
    url(r'^$', index),
    url(r'^admin/', admin.site.urls),
]

Corremos nuestra aplicacion

python manage.py runserver

Y tendremos nuestro “Hola” mostrado en pantalla.

Configurar Celery

Para configurar nuestro proyecto Django junto a Celery tenemos que crear un archivo llamado celery.py donde colocaremos la creacion de nuestra aplicacion celery y demas configuraciones.

from __future__ import absolute_import

import os

from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_celery_tutorial.settings') #Nota 1

from django.conf import settings  # Nota 2

app = Celery('CeleryApp') #Nota 3

app.config_from_object('django.conf:settings') #Nota 4
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) #Nota 5

app.conf.update(
    BROKER_URL = 'django://', #Nota 6
)

Bueno eso que vimos ahi arriba seria nuestra aplicacion Celery, detallares los puntos importantes de el codigo colocado ahi arriba.

#Nota 1 Lo qu sucede en esta linea es que estamos configurando la variable de entorno ‘DJANGO_SETTINGS_MODULE’ para que tome / use el modulo de configuracion que le estamos pasando en este caso el unico que tenemos ‘django_celery_tutorial.settings‘.

#Nota 2 Esta linea lo que hace es importar nuestro archivo de configuracion pero via django, la cual es la mejor forma de hacerlo ya que si tenemos varios archivos de configuracion simplemente estaremos improtando el que esta corriendo la aplicacion (En este ejemplo de celery estaremos importando el que seteamos previamente o el que pasemos por parametro al iniciar celery)

#Nota 3 Creamos nuestra aplicacion celery y le pasamos un nombre

#Nota 4 Inicializamos nuestra app celery con la configuracion de nuestro proyecto Django

#Nota 5 Como su nombre lo dice gracias a esta linea Celery auto-inspeccionara nuestras app y buscara metodos con la anotacion ‘@tasks’ de celery.

Ahora procederemos a en nuestra aplicacion Django llamada ‘app_mail’ crearemos un archivo llamado ‘tasks.py’ donde colocaremos nuestras tareas a ejecutar.

#app_mail/tasks.py
from django_celery_tutorial.celery import app

@app.task
def prueba_suma(x, y):
    return x + y
    
@app.task
def prueba_resta(x, y):
    return x - y

Una vez creadas neustras majestuosas tareas, podemos proceder a correr nuestro proyecto django y nuestra app celery (Son 2 procesos aparte).

En una terminal (django app)

python manage.py runserver 8080

En otra terminal

python manage.py celery worker -A django_celery_tutorial.celery --loglevel=info

Es bueno correr la aplicacion con el log level info asi podremos ver las tareas que estas disponibles para ser ejecutadas en los worers de celery. Si todo se realizo bien en output de nuestra app celery seria algo como esto:

celery_django_integracion

Para llamar una de estas tareas desde nuestro proyecto django solo tenemos que importar los metodos de nuestro archivo ‘tasks.py‘ y ejecutarlos (Si, asi de facil). Como solo tenemos una vista simple y de prueba ejecutaremos la llamada ahi.

from django.shortcuts import render, HttpResponse
from tasks import prueba_suma
    
# Create your views here.
def index(request):
    
    resultado = prueba_suma(5, 6)
    
    return HttpResponse("Hola")

Si ejecutamos nuestro metodo de esa forma se ejecutar en el foreground (Mientras el request este vivo) y no es lo que queremos para que se ejecute en los procesos de celery tendremos que hacerlo un poco diferente.

from django.shortcuts import render, HttpResponse
from tasks import prueba_suma
    
# Create your views here.
def index(request):
    
    resultado = prueba_suma.delay(5, 6)
    
    return HttpResponse("Hola")

De esta forma podremos ejecutarla en el background y podremos ver que en el output de celery tendremos algo como esto:

celery_django_integracion

Donde nos muestra cuando la tarea entro a la cola de trabajo y cuando se ejecuto.

Ahora vamos a crear una tarea que nos permita enviar una correo, por que un correo bueno es un muy buen ejemplo de cosas que tenemos que hacer de forma asíncrona, nuestro usuarios no pueden estar esperando en el hilo principal de la aplicación a que enviemos un correo.

Tendremos que configurar los datos SMTP de nuestra aplicacion django

#settings.py
EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = "";
SERVER_EMAIL = "";
EMAIL_HOST_USER = ""; 
EMAIL_HOST_PASSWORD = "";

Y luego nuestra tarea para enviar correos:

#...
@app.task
def enviar_mail(asunto, contenido, destinatario):
    send_mail(asunto, contenido, 'noreply@mail.com', [destinatario], fail_silently=False)

Para ejcutar dicha llamada solo tenemos que importarla y ejecutarla con delay para que se ejecute en los procesos de celery.

from tasks import enviar_mail

enviar_mail.delay("Asunto", "Contenido mensaje", "mail@mail.com")

Cabe destacar que Celery puede usar redis, RabbitMQ y otras opciones mas como Job Queue pero para realizar un ejemplo mas simple lo hice con el mismo Django Database.

Y hasta aqui este tutorial, espero que al igual que me sirvio a mi conocer esta herramienta le pueda servir a alguien mas y compartir el conocimiento.

Aqui coloco el codigo del ejemplo completo Github

Referencia

Celery
Django
Django Celery

Share on Google+Share on LinkedInShare on RedditShare on TumblrTweet about this on TwitterShare on Facebook
  • Luis Alberto Romero Calderon

    Primero 🙂

  • Pingback: Cıvata()