4

I want to mimic the functionality that Gmail has, where you can choose to send an email at a certain time (maybe 3:34 am tomorrow) in Django.

I looked at something like django-crontab (https://pypi.org/project/django-crontab/).

I came up with an idea to use django-crontab to achieve this:

  1. Make a crontab that runs every minute
  2. Every minute, check if there are any emails that need to be sent
  3. Send out those emails

This feels a bit hacky and over-engineered. Is there a better way? Thanks!

  • How about `celery`? You can shedule sending email with sheduled celery task. And it will allow to manage autoretries if sending fails. – Timofey Katalnikov Aug 23 '20 at 21:29
  • @TimofeyKatalnikov I will look into it and get back to you! Thanks!! –  Aug 23 '20 at 21:30
  • @TimofeyKatalnikov I am watching this video: https://www.youtube.com/watch?v=b-6mEAr1m-A and it says I need a celery server. Whereas `django-cron` does not need that, right? –  Aug 23 '20 at 21:33
  • I'm sure `celery` is used almost everywhere with `django` to manage sheduled task. Usually it is additional docker container with celery worker and something like `rabbitmq` to manage task queue. It is a common practice. If you sure that you just need shedule only email sending, so `django-crontab` fits, but i still recommend to use `celery`. – Timofey Katalnikov Aug 23 '20 at 21:42
  • @TimofeyKatalnikov In that case, I think it would be best to learn how `celery` works. Would you happen to be familiar with AWS EC2? Thanks again for your help!! –  Aug 23 '20 at 21:44
  • I'm not familiar with AWS EC2. To talk about sending emails, i doubt that cron can manage mail sending retries (without complex manipulations), so if it fails nothing will happend and mail will not be sent. – Timofey Katalnikov Aug 23 '20 at 21:48

1 Answers1

6

You can check out celery and how to integrate it with django. Once done, task scheduling is easy,first add your gmail configuration in settings.py as follows:

EMAIL_BACKEND = 'django_smtp_ssl.SSLEmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'your_email'
EMAIL_HOST_PASSWORD = 'your password'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_PORT = 465

Then in your tasks.py you can add the function for scheduling emails as follows:

from django.template.loader import render_to_string
from django.core.mail import EmailMessage


@periodic_task(
run_every=(crontab(hour=3, minute=34)), #runs exactly at 3:34am every day
name="Dispatch_scheduled_mail",
reject_on_worker_lost=True,
ignore_result=True)
def schedule_mail():
    message = render_to_string('app/schedule_mail.html')
    mail_subject = 'Scheduled Email'
    to_email = getmail
    email = EmailMessage(mail_subject, message, to=[to_email])
    email.send()

Then finally your email template 'schedule_mail.html'

{% autoescape off %}
Hello ,

This is a test email
if you are seeing this, your email got delivered!

Regards,
Coding Team.
{% endautoescape %}

And the command to run celery service as beat:

celery -A yourapp beat --loglevel=info

replace 'yourapp' with name of your app. Cheers!!

Kennoh
  • 137
  • 7
  • Thanks so much for this answer!! I need to make it so that someone can choose on my frontend when the email will be sent. So the cron job needs to be set programmatically whenever someone enters it on my frontend. Does that make sense? How could I do this? –  Aug 24 '20 at 17:33
  • 1
    You are welcome,don't forget to up-vote the answer.There is no way to pass argument dynamically in Celery Beat – Kennoh Aug 24 '20 at 17:43
  • I will, but would love if you could answer my question above. –  Aug 24 '20 at 17:46
  • 1
    I don't think there is a way of passing time arguments dynamically in celery beat because beat first registers scheduled tasks on start.Only way is programmatically as i have shown above.May b you can try invoking the tasks when time is provided from the frontend then perform calculations and delay the email function to the calculated time – Kennoh Aug 24 '20 at 17:49
  • I see. How do you think that Gmail does it? Do they have a crontab running every minute? –  Aug 24 '20 at 17:55
  • 1
    It's more of a logical problem than a programming question.I think their logic is crontab running every minute then checking if the time coincides with the available queues of scheduled emails.If yes, then send the email and remove it from the queue else pass – Kennoh Aug 24 '20 at 18:01
  • I see, this makes sense! Thank you so much for your help so far. One question: are you familiar with AWS? –  Aug 24 '20 at 18:15
  • 1
    Well not so much.I have deployed on aws ec2 instance once.I mostly work with digital ocean.But i would presume if its Iaas and not Paas infrastructure then its nearly similar. – Kennoh Aug 24 '20 at 18:23
  • Ok, will I need a separate server for celery? Thanks so much for your help Kennoh? –  Aug 24 '20 at 18:31
  • You are welcome.And no, you don't need a separate server.Just use supervisor or circus or systemd script alongside gunicorn to run your celery app and your will be fine. – Kennoh Aug 24 '20 at 18:35