@Jasper_Holton's profile photo

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)
This next part is the daemon. This is included in __init__.py of the new app tasks (python manage.py startapp tasks) and imported in the apps.py
# 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()
This is initialized in the AppConfig, by calling the method
# 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()
Putting this together, you can asynchronously send a scheduled email with the following code:
# 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
I hope this is useful as a solution for sending emails asynchronously. Below is a photo of a simple HTML email sending client I created using JavaScript. I can use this client to email subscribers on this site.


Pinterest logo
Tumblr logo VK logo

https://uglek.com/post/291/ -

© Uglek, 2022

Terms of Use and Privacy Policy