###################################### YOUR_SITE.YOUR_APP.models.py ###################################### from django.core.mail import send_mail import smtplib class QueuedEmail(models.Model): """ DATABASE EMAIL QUEUE -- QueuedEmail stashes your sites' messages in a table, so you don't have to wait on synchronous calls to django.core.mail.send_mail. Mail stays in the database after sending for as long as you leave them there, which provides a paper trail and a basis for analytics. I wrote it for use with an invitation-only site, where the flushqueuedemail.py script is run with a cron job. USAGE EXAMPLE -- This simple bit here will queue a new message from within any python code: def some_view_function(request): (...) invitation = FSQueuedEmail( toaddress=invite.sentto, subject="Welcome", body=u''' Hello, Welcome to the site, %s. Your username is %s. Regards, The Management ''' % ( request.user.get_full_name(), request.user.username, ) ) invitation.save() (...) You can pass either an instance of django.contrib.auth.models.User in the 'to' param, or use a string with an email address in it with 'toaddress'. One can easily integrate templating, or compose emails programmatically, or embed the email text for quick development and testing. Add this model to your site, and flush the queue with the attached command-line tool (or write your own, it's like one query and one loop, and hey it could be smaller.) Enjoy! -fish2000 """ class Meta: abstract = False verbose_name = "queued email message" verbose_name_plural = "queued email messages" status = models.IntegerField(verbose_name="Status", editable=True, null=False, default=0, choices=( (-1, 'SMTP Fail'), (0, 'Queued'), (1, 'Sent OK'), (2, 'Unexpected Error'), )) createdate = models.DateTimeField('Created on', default=datetime.now, blank=True, editable=False) modifydate = models.DateTimeField('Last modified on', default=datetime.now, blank=True, editable=False) senddate = models.DateTimeField('Send after', default=datetime.now, blank=True, editable=True) subject = models.CharField(verbose_name="Subject", default="", unique=False, blank=True, max_length=255) body = models.TextField(verbose_name="Body", default="", unique=False, blank=True) to = models.ForeignKey(User, default=None, unique=False, blank=True, null=True, verbose_name="To (User)") toaddress = models.EmailField(verbose_name="To (Additional address or addresses)", default=None, unique=False, blank=True, null=True, max_length=255) def save(self, force_insert=False, force_update=False): self.modifydate = datetime.now() super(QueuedEmail, self).save(force_insert, force_update) def sendit(self): if self.senddate < datetime.now(): if self.subject and self.body: if self.toaddress: try: self.status = send_mail( # one can insert [sitename] or somesuch # at the start of the subject here "%s" % self.subject, self.body, settings.EMAIL_HOST_USER, [self.toaddress] ) except smtplib.SMTPException: self.status = -1 self.save() else: self.save() elif self.to and self.to.email: try: self.status = send_mail( "%s" % self.subject, self.body, settings.EMAIL_HOST_USER, [self.to.email] ) except smtplib.SMTPException: self.status = -1 self.save() else: self.save() return self.status ###################################### flushqueuedemail.py ###################################### #!/usr/bin/env python # encoding: utf-8 import sys, os, getopt from django.core.management import setup_environ import settings setup_environ(settings) from django.db.models import Q from YOUR_SITE.models import QueuedEmail class Usage(Exception): def __init__(self, msg): self.msg = msg def main(argv=None): if argv is None: argv = sys.argv try: try: opts, args = getopt.getopt(argv[1:], "hv", ["help", "verbose"]) except getopt.error, msg: raise Usage(msg) for option, value in opts: if option in ("-v", "--verbose"): verbose = True if option in ("-h", "--help"): raise Usage("QueuedEmail queue flush tool. Use -v for verbose.") except Usage, err: print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg) print >> sys.stderr, "\t for help use --help" return 2 mailqueue = FSQueuedEmail.objects.filter( Q(status=0) & (Q(to__isnull=False) | Q(toaddress__isnull=False) & Q(subject__isnull=False)) ) if verbose: print "###\t Mail queue contains %s items to be sent.\n" % len(mailqueue) for qm in mailqueue: if verbose: print "###\t Sending message..." print "---\t TO:\t\t\t %s" % qm.to print "---\t TOADDRESS:\t\t %s" % qm.toaddress print "---\t SUBJECT:\t\t %s" % qm.subject # output will be 1 if sending was successful # messages will be saved to the DB with the # same number for their status. if qm.sendit() == 1: if verbose: print "---\t Mail sent OK\n" else: print "###\t MAIL FAIL\n" if __name__ == "__main__": sys.exit(main())