Asynchronous Database Driven Email Daemon and Client for Django Websites I created a Django site in 2020. One of the things I have needed for this site for a while is a good email editor and scheduler, so I can edit, see, and schedule emails for the site on the go, and they are sent out automatically on time. I wrote some software to do just this. The first part of the code is a database model. This stores the email and passes it to the daemon. It contains a send at date and time, recipients list, subject, HTML content, and a method which calls the send email function.
# app/models.py class ScheduledEmail(models.Model): id = models.AutoField(primary_key=True) recipients = models.ManyToManyField(User, related_name='email_recipients') send_at = models.DateTimeField(default=timezone.now) subject = models.CharField(blank=True, max_length=100, default='') html_content = models.TextField() def send(self): send_users_email(self.recipients.all(),self.subject,self.html_content)
# tasks/__init__.py import threading import time from blog.models import ScheduledEmail import datetime from django.template import Template, Context def elapsed_minute(): # Get time elapsed since last minute now = datetime.datetime.now() time1 = now time2 = now time2 = time2.replace(second=0,microsecond=0) return (time1-time2).total_seconds() def send_pending_emails(): # Send emails due for sending emails = ScheduledEmail.objects.filter(send_at__lt=datetime.datetime.now()) for email in emails: email.send() email.delete() def async_tasks(): # Run tasks asynchronously sleep_time = 60 - elapsed_minute() # Seconds left in minute time.sleep(sleep_time) # Sleep until the turn of the minute while True: send_pending_emails() time.sleep(60) def start_async(): # Start a new thread for our tasks async_thread = threading.Thread(target=async_tasks, name="Uglek async tasks", args=()) async_thread.start()
# app/apps.py import sys ... from tasks import start_async, send_pending_emails, async_tasks if not ('shell' in sys.argv or 'migrate' in sys.argv or 'makemigrations' in sys.argv): start_async()
# wherever you send email email = ScheduledEmail.objects.create(send_at=dateandtime, subject=subject, html_content=html) # Create a new scheduled email email.recipients.set(users) # Set the recipients to a list of users email.save() # Save the email to the database