Jim
Jim

Reputation: 14270

How to Send Asynchronous Emails In Django Using Celery?

I'm trying to send asynchronous emails in a Django 1.6.2 application using django-celery-email and Celery 3.1.17 with RabbitMQ 3.5.0 as my message broker and result backend. Emails are being sent and received but I also get the error "django.core.mail... is not JSON serializable" in my Celery log when emails are sent. I'm using JSON for serialization in Celery as pickle is being deprecated. Is there some way I can change my configuration to prevent this error from occurring? Incidentally, I can send emails just fine when I don't use Celery's delay method.

Thanks.

# Stacktrace
Task app.tasks.send_email with id 7ac1eb8e-c090-4893-8147-1f204e463d12 raised exception:
'EncodeError(TypeError("<module \'django.core.mail\' from \'/Users/me/venv/django/lib/python2.7/site-packages/django/core/mail/__init__.pyc\'> is not JSON serializable",),)'


Task was called with args: [] kwargs: {}.

The contents of the full traceback was:

Traceback (most recent call last):
 File "/Users/me/venv/django/lib/python2.7/site-packages/celery/app/trace.py", line 283, in trace_task
   uuid, retval, SUCCESS, request=task_request,
 File "/Users/me/venv/django/lib/python2.7/site-packages/celery/backends/amqp.py", line 136, in store_result
   delivery_mode=self.delivery_mode,
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/messaging.py", line 161, in publish
   compression, headers)
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/messaging.py", line 237, in _prepare
   body) = dumps(body, serializer=serializer)
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/serialization.py", line 164, in dumps
   payload = encoder(data)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 35, in __exit__
   self.gen.throw(type, value, traceback)
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/serialization.py", line 59, in _reraise_errors
   reraise(wrapper, wrapper(exc), sys.exc_info()[2])
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/serialization.py", line 55, in _reraise_errors
   yield
 File "/Users/me/venv/django/lib/python2.7/site-packages/kombu/serialization.py", line 164, in dumps
   payload = encoder(data)
 File "/Users/me/venv/django/lib/python2.7/site-packages/anyjson/__init__.py", line 141, in dumps
   return implementation.dumps(value)
 File "/Users/me/venv/django/lib/python2.7/site-packages/anyjson/__init__.py", line 87, in dumps
   return self._encode(data)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 243, in dumps
   return _default_encoder.encode(obj)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 207, in encode
   chunks = self.iterencode(o, _one_shot=True)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 270, in iterencode
   return _iterencode(o, 0)
 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 184, in default
   raise TypeError(repr(o) + " is not JSON serializable")
EncodeError: <module 'django.core.mail' from '/Users/me/venv/django/lib/python2.7/site-packages/django/core/mail/__init__.pyc'> is not JSON serializable

# settings.py
# MAIL SETTINGS
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIL_HOST_USER = 'me'
EMAIL_HOST_PASSWORD ='password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
FAIL_SILENTLY = False

# CELERY SETTINGS
BROKER_URL = 'amqp://guest@localhost//'
CELERY_RESULT_BACKEND = 'amqp'
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT = ['json', ]

# DJANGO-CELERY-EMAIL
INSTALLED_APPS += ('djcelery_email', )
EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend'

# tasks.py
from __future__ import absolute_import

import smtplib
from conf.celery import app
from django.core import mail
from django.template.loader import render_to_string

@app.task
def send_email():
    to_email = '[email protected]'
    subject = 'Testing Celery/RabbitMQ'
    from_email = '[email protected]'
    message = 'This is a test of my Celery/RabbitMQ function.'
    recipient_list = []
    recipient_list.append(to_email)
    html_message = render_to_string('send_mail.html', {'message': message})
    try:
        mail.send_mail(subject, message, from_email, recipient_list, html_message)
    except smtplib.SMTPException, e:
        return 0
    return mail

# views.py
from app.tasks import send_email
def home_page(request, template):
    # Send mail synchronously
    #send_email()
    # Send email asynchronously.
    send_email.delay()
    return render(request, template)

Upvotes: 1

Views: 3174

Answers (1)

Saikiran Yerram
Saikiran Yerram

Reputation: 3092

You're getting the error as expected. Celery is setup to use JSON serialization and is using the built in json library that attempts to serialize django.core.mail which obviously doesn't support any form of serialization.

The reason it works when calling without delay is because it works like calling a typical function (in the same process).

If you have to return something from a task, you can return a dictionary of values or objects that support serialization. For custom serializations, you can use the serializer property and pass your custom decoder/encoder when calling the task.

In your case you can return something like {'success': True} and False value for any failures.

More options on using custom serializers

Upvotes: 2

Related Questions