How to send emails using RabbitMq and Django - django

I am new to RabbitMQ at the same time, if possible would someone also link me to a good tutorial for my task?

Here's a good tutorial for setup:
https://simpleisbetterthancomplex.com/tutorial/2017/08/20/how-to-use-celery-with-django.html#installing-rabbitmq-on-ubuntu-1604
More on RabbitMQ installation: https://www.rabbitmq.com/install-debian.html
After setting up you can send email creating a celery task.
ref: https://medium.com/#juwelariful1/send-mail-in-django-with-gmail-and-smtp-include-celery-and-gmail-configuration-4b07ae4f8542
More info on django docs:
https://docs.djangoproject.com/en/3.2/topics/email/
tasks.py
from celery import shared_task
from django.core.mail.message import EmailMultiAlternatives
#shared_task
def send_email_to(subject, body, from_email, to_email):
try:
email = EmailMultiAlternatives(subject, body, from_email, [to_email])
email.content_subtype = 'html'
email.send()
except:
pass

Related

Is celery recommended for email sending only?

I have an api with django rest framework.
The api is for user signup. After signup api will send verification email to the user but sending email takes a little time so for this purpose i want to send email in background.
For this requirement what should be the approach ?
This should be your approach to achieve the task which you want to execute.
Install celery
create a celery.py file in your project folder, where your settings.py file is located(recommended but not necessary)
and paste the following code into your file.
Replace example with your project name
from celery import Celery
import os
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings')
app = Celery('example')
app.config_from_object(settings, namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
Add these lines in your settings:
CELERY_BROKER_URL = 'redis://{}:{}'.format(REDIS_SERVER_HOST, REDIS_SERVER_PORT)
CELERY_RESULT_BACKEND = 'redis://{}:{}'.format(REDIS_SERVER_HOST, REDIS_SERVER_PORT)
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
Make sure to start the Redis server and assign values to REDIS_SERVER_HOST and REDIS_SERVER_PORT variables with appropriate values.
Open init.py file of your project directory and paste this code
from .celery import app as celery_app
__all__ = ['celery_app']
create a task.py file in your app directory and write a function that sends an email
For ex:
from example import celery_app
from django.core.mail import send_mail
#celery_app.task
def send_celery_email(self, recipient_list):
# your actual mail function
send_mail("subject", "message", from_email = 'test#gmail.com', recipient_list = [recipient_list])
Start your celery server using
celery -A project worker --loglevel=info
call this_function from the views as a normal function and pass the required arguments.
from .task import send_celery_email
send_celery_email.delay(recipient_list = [])
Note: This is just a roadmap of the workflow, actual code may vary according to your requirements and version of celery.
Also checkout the documentation

Django Sendgrid Heroku setup

I use a Django / Sendgrid / Heroku setup which was working well but it stopped with no apparent reason.
my django settings:
EMAIL_HOST="smtp.sendgrid.net"
EMAIL_HOST_PASSWORD="..."
EMAIL_HOST_USER="..."
EMAIL_PORT=587
EMAIL_USE_TLS=True
with this settings, if I send simple emails with the django library...
from django.core.mail import send_mail
send_mail(
'Subject here',
'Here is the message.',
from_email_address,
[to_email_address],
fail_silently=False,
)
I get this error
--> SMTPServerDisconnected: Connection unexpectedly closed
I can still send emails using the sendgrid python library
import sendgrid
sg = sendgrid.SendGridAPIClient(apikey)
sg.send(data)
Is there anything I am missing?
Just figured this out:
Sendgrid dynamically provides a password through the SENDGRID_PASSWORD Heroku environment variable. However the variable used for the email settings is EMAIL_HOST_PASSWORD which was not updated after our password changed.
Be sure that both variables are identical

Sending Email With Celery Using Django Default Email Backend

I am trying to send confirmation email to my registered user using Django and Celery. I am using RabbitMQ as the Broker. Whenever I'm executing the code, the celery log shows it is receiving the task and executing successfully, But I am not receiving any emails.
tasks.py
from celery.task import Task
from django.core.mail import send_mail
from django.template import loader
from django.utils.html import strip_tags
from config.celery import app
from config.settings import default
class SendConfirmationEmail(Task):
def __init__(self, *args, **kwargs):
self.user_name = kwargs.get('username')
self.user_id = kwargs.get('id')
self.user_hash = kwargs.get('hash')
self.user_email = kwargs.get('email')
def send_email(self):
confirm_mail = loader.render_to_string('mail/confirmation.html',
{'user': self.user_name, 'id': self.user_id,
'hash': self.user_hash,
'domain': default.SITE_URL})
text_email = strip_tags(confirm_mail)
send_mail(
subject='Confirm Your E-mail',
message=text_email,
from_email='no-reply#mysite.com',
recipient_list=[self.user_email],
fail_silently=False,
html_message=confirm_mail
)
def run(self, *args, **kwargs):
self.send_email()
app.register_task(SendConfirmationEmail())
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.siteuser.models import User
from apps.siteuser.tasks import SendConfirmationEmail
#receiver(post_save, sender=User)
def create_employee_details(sender, instance, created, **kwargs):
if created:
task = SendConfirmationEmail(username=instance.first_name, id=instance.id, hash=instance.hash,
email=instance.email)
task.delay()
settings for Email & Celery:
CELERY_BROKER_URL = 'amqp://username:password#localhost:5672/vhost'
EMAIL_HOST = 'smtp.mailtrap.io'
EMAIL_HOST_USER = MY_USERNAME
EMAIL_HOST_PASSWORD = MY_PASSWORD
EMAIL_PORT = 2525
Celery log:
[2018-05-09 11:50:41,191: INFO/MainProcess] Received task: apps.user.tasks.SendConfirmationEmail[e63c0f5f-7b81-4065-85c1-9ef87acc792a]
[2018-05-09 11:50:41,197: INFO/ForkPoolWorker-1] Task apps.user.tasks.SendConfirmationEmail[e63c0f5f-7b81-4065-85c1-9ef87acc792a] succeeded in 0.004487647999667388s: None
NOTE: I have sent mail without Celery and it was working fine. But The Problem started after trying with Celery.I am using Mailtrap for development purpose.
This is how I generally call a celery task
Task.py
from celery import shared_task
#shared_task
def task1(*args,**kwargs):
pass
Caller.py
from task import task1
task1.delay(a,b,c...)
Is celery discovery configured correctly?

Sending mail using flask + blueprint

I have a question about the structure of blueprint
My flask structure looks like
app/
main/
__init__.py
mail.py
__init.py
manage.py
config.py
I register blueprint in __init__.py, for app/__init__.py
from flask_mail import Mail
from flask import Flask
mail = Mail()
def create_app(config_name='develop'):
app = Flask(__name__)
from config import cfg # load EMAIL config from config.py
app.config.from_object(cfg[config_name])
cfg[config_name].init_app(app)
from .main import main # register blueprint
app.register_blueprint(main)
mail.init_app(app) # load related mail config???
return app
Put configs in config.py
class Config():
MAIL_SERVER='<My e-mail SMTP>'
MAIL_DEFAULT_SENDER=('TOPIC', 'mailID')
MAIL_USERNAME='<EMail>'
MAIL_PASSWORD='<EMail Password>'
It returns smtplib.SMTPSenderRefused error when I write codes like this in app/main/mail.py
#main.route('/mail')
def sendmail():
receivers = ['<Receiver 1>', '<Receiver 2>']
app = current_app._get_current_object() # <class 'flask.app.Flask>
mail = Mail(app)
with mail.connect() as conn:
for receiver in receivers:
msg = Message(
subject='subject test',
recipients=[receiver],
html='<h1>Hi~~</h1>')
mail.send(msg)
return 'ok'
It raise a 553 error
smtplib.SMTPSenderRefused: (553, b'Domain name required.', '=?utf-8?q?<TOPIC>?= <mailID>')
I did load config in app/__init__.py, but why I couldn't find MAIL_SERVER and related config in app/main/mail.py?
But if I reload config again in app/main/mail.py, it sends mail successfully
app.config.update(
MAIL_SERVER='<SMTP>',
MAIL_DEFAULT_SENDER=('<TOPIC>', 'mailID'),
MAIL_USERNAME='<email>',
MAIL_PASSWORD='<PASSWORD>'
)
I don't know why I have to do it twice
You should use app_context() like this:
from flask import current_app
from flask_mail import Message, Mail
with current_app.app_context():
mail = Mail()
mail.send(msg)
more info https://flask.palletsprojects.com/en/1.1.x/extensiondev/
The following code is render HTML template for sending mail body including styles or formatted HTML.
from <project-module> import mail
token = user.get_reset_token()
msg = Message('Password Reset Request', sender='<sender-mail>', recipients=[user.email])
msg.html = render_template('email_templates/password_reset.html',
home_link=url_for('home.index', _external=True),
reset_link=url_for(
'account.reset_token', token=token, _external=True),
name=user.name,
email=user.email)
mail.send(msg)
As you mentioned above code. I have been twice initialize Mail object, which is un-necessary.
#main.route('/mail')
def sendmail():
receivers = ['<Receiver 1>', '<Receiver 2>']
mail = Mail()
with mail.connect() as conn:
for receiver in receivers:
msg = Message(
subject='subject test',
recipients=[receiver],
html='<h1>Hi~~</h1>')
mail.send(msg)
return 'ok'
Not a direct answer to your question, but actually I don't think you need to initialize the mail twice.
If you would initialize the mail in app/main/mail.py, there's no point having mail.init_app(app) in app/__init__.py because you never imported it.
Otherwise, in app/main/mail.py I would do import app.mail without creating another mail so that you won't have this config issue in the first place.

Django EmailMessage not sending/timeout

im trying to use django.core.mail to send emails using the default backend and it doesn't seem to be working. I've set up the email credentials, server, and port number in the settings file but whenever I try to run the send() method of an email message the command hangs indefinitely.
views.py
from django.core.mail import send_mail
def sending_email(request):
message = ""
subject = ""
send_mail(subject, message, from_email, ['to_email',])
Add this in settings.py
# Sending mail
EMAIL_USE_TLS = True
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='your gmail account'
EMAIL_HOST_PASSWORD='your gmail password'
I was having the same issue when trying to send via smtp.gmail.com with use_tls=True. It turns out I had the wrong port set. Here's what I'm doing now and it works:
from django.core.mail import get_connection
from django.core.mail.message import EmailMessage
connection = get_connection(use_tls=True, host='smtp.gmail.com', port=587,username='YourEmail#gmail.com', password='YourPassword')
EmailMessage('test', 'test', 'addr#from.com', ['addr#to.com'], connection=connection).send()