This is what I use to send simple status emails from my sites. Instead of a django.core.mail.send_mail call, which can take an irrritatingly, nondeterministically long time to return (regardless of error state), you can stow the emails in the database and rely on a separate interpreter process send them off (using a per-minute cron job or what have you). You then also have a record of everything you've sent via email from your code without having to configure your outbound SMTP server to store them or anything like that.
Usage notes are in the docstring; share and enjoy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | ###################################### 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())
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Nice script but I think there is already a reusable app for this purpose. Search for django-mailer which handles the same usecase.
#
Please login first before commenting.