A Django app that allows you to send email asynchronously in Django. Supports HTML email, database backed templates and logging.

Overview

Django Post Office

Django Post Office is a simple app to send and manage your emails in Django. Some awesome features are:

  • Allows you to send email asynchronously
  • Multi backend support
  • Supports HTML email
  • Supports inlined images in HTML email
  • Supports database based email templates
  • Supports multilingual email templates (i18n)
  • Built in scheduling support
  • Works well with task queues like RQ or Celery
  • Uses multiprocessing (and threading) to send a large number of emails in parallel

Dependencies

Optional Dependency

With this optional dependency, HTML emails are nicely rendered inside the Django admin backend. Without this library, all HTML tags will otherwise be stripped for security reasons.

Installation

Build Status PyPI version Software license

Install from PyPI (or manually download from PyPI):

pip install django-post_office

Add post_office to your INSTALLED_APPS in django's settings.py:

INSTALLED_APPS = (
    # other apps
    "post_office",
)

Run migrate:

python manage.py migrate

Set post_office.EmailBackend as your EMAIL_BACKEND in Django's settings.py:

EMAIL_BACKEND = 'post_office.EmailBackend'

Quickstart

Send a simple email is really easy:

from post_office import mail

mail.send(
    '[email protected]', # List of email addresses also accepted
    '[email protected]',
    subject='My email',
    message='Hi there!',
    html_message='Hi <strong>there</strong>!',
)

If you want to use templates, ensure that Django's admin interface is enabled. Create an EmailTemplate instance via admin and do the following:

from post_office import mail

mail.send(
    '[email protected]', # List of email addresses also accepted
    '[email protected]',
    template='welcome_email', # Could be an EmailTemplate instance or name
    context={'foo': 'bar'},
)

The above command will put your email on the queue so you can use the command in your webapp without slowing down the request/response cycle too much. To actually send them out, run python manage.py send_queued_mail. You can schedule this management command to run regularly via cron:

* * * * * (/usr/bin/python manage.py send_queued_mail >> send_mail.log 2>&1)

Usage

mail.send()

mail.send is the most important function in this library, it takes these arguments:

Argument Required Description
recipients Yes List of recipient email addresses
sender No Defaults to settings.DEFAULT_FROM_EMAIL, display name like John <[email protected]> is allowed
subject No Email subject (if template is not specified)
message No Email content (if template is not specified)
html_message No HTML content (if template is not specified)
template No EmailTemplate instance or name of template
language No Language in which you want to send the email in (if you have multilingual email templates).
cc No List of emails, will appear in cc field
bcc No List of emails, will appear in bcc field
attachments No Email attachments - a dict where the keys are the filenames and the values are files, file-like-objects or path to file
context No A dict, used to render templated email
headers No A dictionary of extra headers on the message
scheduled_time No A date/datetime object indicating when the email should be sent
expires_at No If specified, mails that are not yet sent won't be delivered after this date.
priority No high, medium, low or now (sent immediately)
backend No Alias of the backend you want to use, default will be used if not specified.
render_on_delivery No Setting this to True causes email to be lazily rendered during delivery. template is required when render_on_delivery is True. With this option, the full email content is never stored in the DB. May result in significant space savings if you're sending many emails using the same template.

Here are a few examples.

If you just want to send out emails without using database templates. You can call the send command without the template argument.

from post_office import mail

mail.send(
    ['[email protected]'],
    '[email protected]',
    subject='Welcome!',
    message='Welcome home, {{ name }}!',
    html_message='Welcome home, <b>{{ name }}</b>!',
    headers={'Reply-to': '[email protected]'},
    scheduled_time=date(2014, 1, 1),
    context={'name': 'Alice'},
)

post_office is also task queue friendly. Passing now as priority into send_mail will deliver the email right away (instead of queuing it), regardless of how many emails you have in your queue:

from post_office import mail

mail.send(
    ['[email protected]'],
    '[email protected]',
    template='welcome_email',
    context={'foo': 'bar'},
    priority='now',
)

This is useful if you already use something like django-rq to send emails asynchronously and only need to store email related activities and logs.

If you want to send an email with attachments:

from django.core.files.base import ContentFile
from post_office import mail

mail.send(
    ['[email protected]'],
    '[email protected]',
    template='welcome_email',
    context={'foo': 'bar'},
    priority='now',
    attachments={
        'attachment1.doc': '/path/to/file/file1.doc',
        'attachment2.txt': ContentFile('file content'),
        'attachment3.txt': {'file': ContentFile('file content'), 'mimetype': 'text/plain'},
    }
)

Template Tags and Variables

post-office supports Django's template tags and variables. For example, if you put Hello, {{ name }} in the subject line and pass in {'name': 'Alice'} as context, you will get Hello, Alice as subject:

from post_office.models import EmailTemplate
from post_office import mail

EmailTemplate.objects.create(
    name='morning_greeting',
    subject='Morning, {{ name|capfirst }}',
    content='Hi {{ name }}, how are you feeling today?',
    html_content='Hi <strong>{{ name }}</strong>, how are you feeling today?',
)

mail.send(
    ['[email protected]'],
    '[email protected]',
    template='morning_greeting',
    context={'name': 'alice'},
)

# This will create an email with the following content:
subject = 'Morning, Alice',
content = 'Hi alice, how are you feeling today?'
content = 'Hi <strong>alice</strong>, how are you feeling today?'

Multilingual Email Templates

You can easily create email templates in various different languages. For example:

template = EmailTemplate.objects.create(
    name='hello',
    subject='Hello world!',
)

# Add an Indonesian version of this template:
indonesian_template = template.translated_templates.create(
    language='id',
    subject='Halo Dunia!'
)

Sending an email using template in a non default languange is similarly easy:

mail.send(
    ['[email protected]'],
    '[email protected]',
    template=template, # Sends using the default template
)

mail.send(
    ['[email protected]'],
    '[email protected]',
    template=template,
    language='id', # Sends using Indonesian template
)

Inlined Images

Often one wants to render images inside a template, which are attached as inlined MIMEImage to the outgoing email. This requires a slightly modified Django Template Engine, keeping a list of inlined images, which later will be added to the outgoing message.

First we must add a special Django template backend to our list of template engines:

TEMPLATES = [
    {
        ...
    }, {
        'BACKEND': 'post_office.template.backends.post_office.PostOfficeTemplates',
        'APP_DIRS': True,
        'DIRS': [],
        'OPTIONS': {
            'context_processors': [
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.i18n',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.tz',
                'django.template.context_processors.request',
            ]
        }
    }
]

then we must tell Post-Office to use this template engine:

POST_OFFICE = {
    'TEMPLATE_ENGINE': 'post_office',
}

In templates used to render HTML for emails add

{% load post_office %}

<p>... somewhere in the body ...</p>
<img src="{% inline_image 'path/to/image.png' %}" />

Here the templatetag named inline_image is used to keep track of inlined images. It takes a single parameter. This can either be the relative path to an image file located in one of the static directories, or the absolute path to an image file, or an image-file object itself. Templates rendered using this templatetag, render a reference ID for each given image, and store these images inside the context of the adopted template engine. Later on, when the rendered template is passed to the mailing library, those images will be transferred to the email message object as MIMEImage-attachments.

To send an email containing both, a plain text body and some HTML with inlined images, use the following code snippet:

from django.core.mail import EmailMultiAlternatives

subject, body = "Hello", "Plain text body"
from_email, to_email = "[email protected]", "[email protected]"
email_message = EmailMultiAlternatives(subject, body, from_email, [to_email])
template = get_template('email-template-name.html', using='post_office')
context = {...}
html = template.render(context)
email_message.attach_alternative(html, 'text/html')
template.attach_related(email_message)
email_message.send()

To send an email containing HTML with inlined images, but without a plain text body, use this code snippet:

from django.core.mail import EmailMultiAlternatives

subject, from_email, to_email = "Hello", "[email protected]", "[email protected]"
template = get_template('email-template-name.html', using='post_office')
context = {...}
html = template.render(context)
email_message = EmailMultiAlternatives(subject, html, from_email, [to_email])
email_message.content_subtype = 'html'
template.attach_related(email_message)
email_message.send()

Custom Email Backends

By default, post_office uses django's smtp.EmailBackend. If you want to use a different backend, you can do so by configuring BACKENDS.

For example if you want to use django-ses:

# Put this in settings.py
POST_OFFICE = {
    ...
    'BACKENDS': {
        'default': 'smtp.EmailBackend',
        'ses': 'django_ses.SESBackend',
    }
}

You can then choose what backend you want to use when sending mail:

# If you omit `backend_alias` argument, `default` will be used
mail.send(
    ['[email protected]'],
    '[email protected]',
    subject='Hello',
)

# If you want to send using `ses` backend
mail.send(
    ['[email protected]'],
    '[email protected]',
    subject='Hello',
    backend='ses',
)

Management Commands

  • send_queued_mail - send queued emails, those aren't successfully sent will be marked as failed. Accepts the following arguments:
Argument Description
--processes or -p Number of parallel processes to send email. Defaults to 1
--lockfile or -L Full path to file used as lock file. Defaults to /tmp/post_office.lock
  • cleanup_mail - delete all emails created before an X number of days (defaults to 90).
Argument Description
--days or -d Email older than this argument will be deleted. Defaults to 90
--delete-attachments Flag to delete orphaned attachment records and files on disk. If not specified, attachments won't be deleted.

You may want to set these up via cron to run regularly:

* * * * * (cd $PROJECT; python manage.py send_queued_mail --processes=1 >> $PROJECT/cron_mail.log 2>&1)
0 1 * * * (cd $PROJECT; python manage.py cleanup_mail --days=30 --delete-attachments >> $PROJECT/cron_mail_cleanup.log 2>&1)

Settings

This section outlines all the settings and configurations that you can put in Django's settings.py to fine tune post-office's behavior.

Batch Size

If you may want to limit the number of emails sent in a batch (sometimes useful in a low memory environment), use the BATCH_SIZE argument to limit the number of queued emails fetched in one batch.

# Put this in settings.py
POST_OFFICE = {
    ...
    'BATCH_SIZE': 50,
}

Default Priority

The default priority for emails is medium, but this can be altered by setting DEFAULT_PRIORITY. Integration with asynchronous email backends (e.g. based on Celery) becomes trivial when set to now.

# Put this in settings.py
POST_OFFICE = {
    ...
    'DEFAULT_PRIORITY': 'now',
}

Override Recipients

Defaults to None. This option is useful if you want to redirect all emails to specified a few email for development purposes.

# Put this in settings.py
POST_OFFICE = {
    ...
    'OVERRIDE_RECIPIENTS': ['[email protected]', '[email protected]'],
}

Message-ID

The SMTP standard requires that each email contains a unique Message-ID. Typically the Message-ID consists of two parts separated by the @ symbol: The left part is a generated pseudo random number. The right part is a constant string, typically denoting the full qualified domain name of the sending server.

By default, Django generates such a Message-ID during email delivery. Since django-post_office keeps track of all delivered emails, it can be very useful to create and store this Message-ID while creating each email in the database. This identifier then can be looked up in the Django admin backend.

To enable this feature, add this to your Post-Office settings:

# Put this in settings.py
POST_OFFICE = {
    ...
    'MESSAGE_ID_ENABLED': True,
}

It can further be fine tuned, using for instance another full qualified domain name:

# Put this in settings.py
POST_OFFICE = {
    ...
    'MESSAGE_ID_ENABLED': True,
    'MESSAGE_ID_FQDN': 'example.com',
}

Otherwise, if MESSAGE_ID_FQDN is unset (the default), django-post_office falls back to the DNS name of the server, which is determined by the network settings of the host.

Retry

Not activated by default. You can automatically requeue failed email deliveries. You can also configure failed deliveries to be retried after a specific time interval.

# Put this in settings.py
POST_OFFICE = {
    ...
    'MAX_RETRIES': 4,
    'RETRY_INTERVAL': datetime.timedelta(minutes=15),  # Schedule to be retried 15 minutes later
}

Log Level

Logs are stored in the database and is browseable via Django admin. The default log level is 2 (logs both successful and failed deliveries) This behavior can be changed by setting LOG_LEVEL.

# Put this in settings.py
POST_OFFICE = {
    ...
    'LOG_LEVEL': 1, # Log only failed deliveries
}

The different options are:

  • 0 logs nothing
  • 1 logs only failed deliveries
  • 2 logs everything (both successful and failed delivery attempts)

Sending Order

The default sending order for emails is -priority, but this can be altered by setting SENDING_ORDER. For example, if you want to send queued emails in FIFO order :

# Put this in settings.py
POST_OFFICE = {
    ...
    'SENDING_ORDER': ['created'],
}

Context Field Serializer

If you need to store complex Python objects for deferred rendering (i.e. setting render_on_delivery=True), you can specify your own context field class to store context variables. For example if you want to use django-picklefield:

# Put this in settings.py
POST_OFFICE = {
    ...
    'CONTEXT_FIELD_CLASS': 'picklefield.fields.PickledObjectField',
}

CONTEXT_FIELD_CLASS defaults to jsonfield.JSONField.

Logging

You can configure post-office's logging from Django's settings.py. For example:

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "post_office": {
            "format": "[%(levelname)s]%(asctime)s PID %(process)d: %(message)s",
            "datefmt": "%d-%m-%Y %H:%M:%S",
        },
    },
    "handlers": {
        "post_office": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
            "formatter": "post_office"
        },
        # If you use sentry for logging
        'sentry': {
            'level': 'ERROR',
            'class': 'raven.contrib.django.handlers.SentryHandler',
        },
    },
    'loggers': {
        "post_office": {
            "handlers": ["post_office", "sentry"],
            "level": "INFO"
        },
    },
}

Threads

post-office >= 3.0 allows you to use multiple threads to dramatically speed up the speed at which emails are sent. By default, post-office uses 5 threads per process. You can tweak this setting by changing THREADS_PER_PROCESS setting.

This may dramatically increase the speed of bulk email delivery, depending on which email backends you use. In my tests, multi threading speeds up email backends that use HTTP based (REST) delivery mechanisms but doesn't seem to help SMTP based backends.

# Put this in settings.py
POST_OFFICE = {
    ...
    'THREADS_PER_PROCESS': 10,
}

Performance

Caching

if Django's caching mechanism is configured, post_office will cache EmailTemplate instances . If for some reason you want to disable caching, set POST_OFFICE_CACHE to False in settings.py:

## All cache key will be prefixed by post_office:template:
## To turn OFF caching, you need to explicitly set POST_OFFICE_CACHE to False in settings
POST_OFFICE_CACHE = False

## Optional: to use a non default cache backend, add a "post_office" entry in CACHES
CACHES = {
    'post_office': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

send_many()

send_many() is much more performant (generates less database queries) when sending a large number of emails. send_many() is almost identical to mail.send(), with the exception that it accepts a list of keyword arguments that you'd usually pass into mail.send():

from post_office import mail

first_email = {
    'sender': '[email protected]',
    'recipients': ['[email protected]'],
    'subject': 'Hi!',
    'message': 'Hi Alice!'
}
second_email = {
    'sender': '[email protected]',
    'recipients': ['[email protected]'],
    'subject': 'Hi!',
    'message': 'Hi Bob!'
}
kwargs_list = [first_email, second_email]

mail.send_many(kwargs_list)

Attachments are not supported with mail.send_many().

Running Tests

To run the test suite:

`which django-admin.py` test post_office --settings=post_office.test_settings --pythonpath=.

You can run the full test suite for all supported versions of Django and Python with:

tox

or:

python setup.py test

Integration with Celery

If your Django project runs in a Celery enabled configuration, you can use its worker to send out queued emails. Compared to the solution with cron (see above), or the solution with uWSGI timers (see below) this setup has the big advantage that queued emails are send immediately after they have been added to the mail queue. The delivery is still performed in a separate and asynchronous task, which prevents sending emails during the request/response-cycle.

If you configured Celery in your project and started the Celery worker, you should see something such as:

--------------- [email protected] v4.0 (latentcall)
--- ***** -----
-- ******* ---- [Configuration]
- *** --- * --- . broker:      amqp://[email protected]:5672//
- ** ---------- . app:         __main__:0x1012d8590
- ** ---------- . concurrency: 8 (processes)
- ** ---------- . events:      OFF (enable -E to monitor this worker)
- ** ----------
- *** --- * --- [Queues]
-- ******* ---- . celery:      exchange:celery(direct) binding:celery
--- ***** -----

[tasks]
. post_office.tasks.cleanup_expired_mails
. post_office.tasks.send_queued_mail

Delivering emails through the Celery worker must be explicitly enabled:

# Put this in settings.py
POST_OFFICE = {
    ...
    'CELERY_ENABLED': True,
}

Emails will then be delivered immediately after they have been queued. In order to make this happen, the project's celery.py setup shall invoke the autodiscoverttasks function. In case of a temporary delivery failure, we might want retrying to send those emails by a periodic task. This can be scheduled with a simple Celery beat configuration, for instance through

app.conf.beat_schedule = {
    'send-queued-mail': {
        'task': 'post_office.tasks.send_queued_mail',
        'schedule': 600.0,
    },
}

The email queue now will be processed every 10 minutes. If you are using Django Celery Beat, then use the Django-Admin backend and add a periodic taks for post_office.tasks.send_queued_mail.

Depending on your policy, you may also want to remove expired emails from the queue. This can be done by adding another periodic taks for post_office.tasks.cleanup_mail, which may run once a week or month.

Integration with uWSGI

If setting up Celery is too daunting and you use uWSGI as application server, then uWSGI decorators can act as a poor men's scheduler. Just add this short snipped to the project's wsgi.py file:

from django.core.wsgi import get_wsgi_application

application = get_wsgi_application()

# add this block of code
try:
    import uwsgidecorators
    from django.core.management import call_command

    @uwsgidecorators.timer(10)
    def send_queued_mail(num):
        """Send queued mail every 10 seconds"""
        call_command('send_queued_mail', processes=1)

except ImportError:
    print("uwsgidecorators not found. Cron and timers are disabled")

Alternatively you can also use the decorator @uwsgidecorators.cron(minute, hour, day, month, weekday). This will schedule a task at specific times. Use -1 to signal any time, it corresponds to the * in cron.

Please note that uwsgidecorators are available only, if the application has been started with uWSGI. However, Django's internal ./manange.py runserver also access this file, therefore wrap the block into an exception handler as shown above.

This configuration can be useful in environments, such as Docker containers, where you don't have a running cron-daemon.

Signals

Each time an email is added to the mail queue, Post Office emits a special Django signal. Whenever a third party application wants to be informed about this event, it shall connect a callback function to the Post Office's signal handler email_queued, for instance:

from django.dispatch import receiver
from post_office.signals import email_queued

@receiver(email_queued)
def my_callback(sender, emails, **kwargs):
    print("Added {} mails to the sending queue".format(len(emails)))

The Emails objects added to the queue are passed as list to the callback handler.

Changelog

Full changelog can be found here.

Created and maintained by the cool guys at Stamps, Indonesia's most elegant CRM/loyalty platform.

Comments
  • SMTPServerDisconnected error with send_mail and Celery

    SMTPServerDisconnected error with send_mail and Celery

    Hello, i am facing issue when i want to send_mail like this:

    @task(name='send_registration_mail')
    def send_registration_mail():
        # some stuff
        mail.send(
    	    subject=template.subject,
    	    message=message,
    	    html_message=rendered_content,
    	    priority='now'
    	    **kwargs
    	)
    

    This code mostly ends failed with SMTPServerDisconnected in log 'please run connect() first'. When i manualy requeue emails via admin and run send_queued_mail, emails are send.

    dependencies:

    django-post-office==3.0.3
    Django==1.10.7
    
    celery = 4.0.2
    
    opened by mihalikv 34
  • 502 bad gateway

    502 bad gateway

    Sometimes I get 502 error.

    What I see in logs: File "/usr/lib/python2.6/logging/init.py", line 679, in handle self.emit(record) File "/django/utils/log.py", line 113, in emit mail.mail_admins(subject, message, fail_silently=True, html_message=html_message) File "/django/core/mail/init.py", line 98, in mail_admins mail.send(fail_silently=fail_silently) File "/django/core/mail/message.py", line 255, in send return self.get_connection(fail_silently).send_messages([self]) File "/post_office/backends.py", line 43, in send_messages headers=headers, priority=PRIORITY.medium) File "/django/db/models/manager.py", line 149, in create return self.get_query_set().create(**kwargs) File "/django/db/models/query.py", line 416, in create obj.save(force_insert=True, using=self.db) File "/post_office/models.py", line 119, in save self.full_clean() File "/django/db/models/base.py", line 926, in full_clean raise ValidationError(errors) django.core.exceptions.ValidationError: {'from_email': [u'Enter a valid e-mail address.']}

    I have regular settings.DEFAULT_FROM_EMAIL and EMAIL_BACKEND = 'post_office.EmailBackend'

    django 1.5

    opened by russian-master 18
  • Allow sending to more than one recipient

    Allow sending to more than one recipient

    If I want to send an email to more then one recipient and just put to addresses into the recipients list I got two separate emails and not one with two recipients. How can I fix this?

    opened by plaetzchen 15
  • Recipients emails should support display name

    Recipients emails should support display name

    Hi there,

    I'm getting a ValidationError when sending mails with display names, e.g. John Smith [email protected]

    Want to fix it?

    Put the following lines in utils:

    import re
    from django.core.validators import EmailValidator
    
    class FullEmailValidator(EmailValidator):
        """ Simple validator that passes for email addresses bearing a display name
        i.e. John Smith <[email protected]>
        """
        def __call__(self, value):
            try:
                res = super(FullEmailValidator, self).__call__(value)
            except ValidationError:
                try:
                    split_address = re.match(r'(.+) \<(.+@.+)\>', value)
                    display_name, email = split_address.groups()
                    super(FullEmailValidator, self).__call__(email)
                except AttributeError:
                    raise ValidationError
    
    validate_email = FullEmailValidator()
    

    Also, in utils replace in validate_comma_separated_emails() the call to validate_email by validate_email_with_name:

    def validate_comma_separated_emails(value):
    """
    Validate every email address in a comma separated list of emails.
    """
        if not isinstance(value, (tuple, list)):
            raise ValidationError('Email list must be a list/tuple.')
    
        for email in value:
            try:
                validate_email_with_name(email)
            except ValidationError:
                raise ValidationError('Invalid email: %s' % email, code='invalid')
    

    cheers

    opened by LeGast00n 14
  • Inlined images template engine

    Inlined images template engine

    This is an extension of pull request #251. It allows users to create emails using inlined images. There is a special template engine, which shall be used in combination with a special templatetag. It keeps track on the attached images and adds them as inline-attachments.

    opened by jrief 12
  • Add EMAIL_TEST_MODE settings to allow email redirection in developeme…

    Add EMAIL_TEST_MODE settings to allow email redirection in developeme…

    If in the settings.py in django project have EMAIL_TEST_MODE = '[email protected]'

    All mail are redirect to [email protected]

    It is a idea from django-yubin project https://django-yubin.readthedocs.io/en/latest/settings.html

    opened by Houtmann 11
  • Deprecation warnings for Django 1.8

    Deprecation warnings for Django 1.8

    There are currently a number of deprecation warnings generated when using post_office with Django 1.8.

    ...lib/python2.7site-packages/post_office/settings.py:35: RemovedInDjango19Warning: 'get_cache' is deprecated in favor of 'caches'.
      return get_cache("default")
    
    ...lib/python2.7/site-packages/post_office/models.py:28: RemovedInDjango19Warning: Model class post_office.models.Email doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.
      class Email(models.Model):
    
    ...lib/python2.7/site-packages/post_office/models.py:147: RemovedInDjango19Warning: Model class post_office.models.Log doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.
      class Log(models.Model):
    
    ...lib/python2.7/site-packages/post_office/models.py:164: RemovedInDjango19Warning: Model class post_office.models.EmailTemplate doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.
      class EmailTemplate(models.Model):
    
    ...lib/python2.7/site-packages/post_office/models.py:200: RemovedInDjango19Warning: Model class post_office.models.Attachment doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.
    
    opened by andrewyager 9
  • how to include a text version of the email in html emails as well?

    how to include a text version of the email in html emails as well?

    Hi!

    When evaluating my emails using https://www.mail-tester.com/ I got the following:

    Message only has text/html MIME parts
    You should also include a text version of your message (text/plain)
    
    

    Looks like there's no text version of the email even when the EmailTemplate object has it. What can I do to add it when sending emails:

    This is what I use to send:

    
                res = mail.send(
                    to_email,
                    'Demo <[email protected]>',
                    template='template_name',
                    context=context,
                )
    
    opened by juancrescente 8
  • suggestion: template engine

    suggestion: template engine

    Hello,

    I have a suggestion for the template handling. The problem with the current solution is that the translations are not very well integrated into the django translation system. The translations have to be "hard-coded" in python files and can not be easily sent to an external translator as they would expect .po files.

    I propose here a solution which is fully integrated with the django translation system. Besides is it not incompatible with the current translation schema of post-office.

    This also allows to ship templates out-of-the-box with Django applications and eliminates the need to set them up manually. They will be also automatically updated.

    templates.py

    from django.db import connection
    from post_office.models import EmailTemplate
    from django.utils.translation import gettext_lazy as _
    
    from django.template.loader import render_to_string, get_template
    from django.utils.html import strip_tags
    
    def db_table_exists(table_name):
        return table_name in connection.introspection.table_names()
    
    if db_table_exists(  'post_office_emailtemplate' ):
    
        tFile =  get_template('myapp/email/generic.html')
        with open( str(tFile.template.origin), 'r') as file:
            tContentHTML = file.read()
            tContent =  strip_tags(tContentHTML)
    
        EmailTemplate.objects.update_or_create(
            name='generic',
            defaults={
            'subject'      :  _('Message from domain.com'),
            'description'  :  'Generic template',
            'html_content' :  tContentHTML,
            'content'      :  tContent}
            )
    
    opened by ghost 8
  • Prevent the need for updating VERSION on two different places

    Prevent the need for updating VERSION on two different places

    In my projects, I prefer to keep only one location which has to be updated on version jumps. Currently it is in 'post_office/init.py' and 'post_office/setup.py'.

    However, this requires to remove the from .backends import EmailBackend from the __init__.py file. @selwin please tell my if this is OK for you.

    opened by jrief 8
  • TypeError at /admin/post_office/email/add/

    TypeError at /admin/post_office/email/add/

    /site-packages/post_office/admin.py in _format_value, line 38 'NoneType' object is not iterable

    return ','.join([item for item in value]) 
    

    I clicked on the add mail button inside the admin. Django 1.11.2 Python 3.6.1 PostOffice 2.0.8

    opened by kakulukia 8
  • Django 4.1 compatibility

    Django 4.1 compatibility

    The requirement of the third party jsonfield does not play nice with the upgrade to Django 4.1.

    I open this issue, since I think jsonfield can be removed in favor of the default django JSONfield. What do you think?

    opened by gabn88 2
  • Add reply_to attribute to mail.send()

    Add reply_to attribute to mail.send()

    Currently it is not possible to add a reply to address to mail.send(). Instead it is necessary to use EmailMultiAlternatives. However this way is not so convenient and self explanatory.

    I would suggest to add the attribute reply_to to the mail.send() function.

    opened by sebastian-muthwill 1
  • Emails are rendered without context data

    Emails are rendered without context data

    Hello!

    All the {{ something }} template tags in the are being ignored and shown empty, instead of being replaced with the context data.

    In my setup, I'm using the django.core.mail.backends.console.EmailBackend backend, and DEFAULT_PRIORITY is now.

    The stdout log using the 3.6.2 version:

    Hi ha una nova sol·licitud d'alta avui  a les .
    
    Tipus: 
    Nom: 
    Accés a la fitxa: 
    

    Running the same after downgrading to the 3.6.1 version:

    Hi ha una nova sol·licitud d'alta avui 19/10/2022 a les 15:36.
    
    Tipus: Entitat
    Nom: prova via api nom comercial
    Accés a la fitxa: https://localhost/admin/partners/entity/8a12fb17-597b-4807-ae0c-259221aeef9c/change/
    

    I imagine that this other issue talks about the same, and the PR might fix it, but I'm not sure if my case is about re-rendering anything so I thought it could be useful to open this other issue, for those who might be googling about the problem.

    Many thanks to all the contributors for maintaining this awesome library.

    Cheers, Pere

    opened by perepicornell 2
  • HTML emails not stored correctly in database

    HTML emails not stored correctly in database

    Django is (sometimes) encoding the html part of EmailMessage as quoted_printable. In the following line we get this html message: https://github.com/ui/django-post_office/blob/13aaac0d1575fb4805d71bc7f2ee390741eaec41/post_office/backends.py#L45

    It might look like (message.as_string): 'Content-Type: multipart/alternative;\n boundary="===============5112651050379218236=="\nMIME-Version: 1.0\nSubject: 123\nFrom: [email protected]\nTo: [email protected]\nDate: Thu, 06 Oct 2022 20:34:52 -0000\nMessage-ID: <[email protected]>\n\n--===============5112651050379218236==\nContent-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\n\n123\n--===============5112651050379218236==\nContent-Type: text/html; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<!doctype html><html xmlns=3D"http://www.w3.org/1999/xhtml" xmlns:v=3D"urn:=\nschemas-microsoft-com:vml" xmlns:o=3D"urn:schemas-microsoft-com:office:offi=\nce"><head><title>{% load i18n %}{% load static %} Welkom bij PaReg</title><=\n!--[if !mso]><!-- --><meta http-equiv=3D"X-UA-Compatible" content=3D"IE=3De=\ndge"><!--<![endif]--><meta http-equiv=3D"Content-Type" content=3D"text/html=\n; charset=3DUTF-8"><meta name=3D"viewport" content=3D"width=3Ddevice-width,=\ninitial-scale=3D1"><style type=3D"text/css">#outlook a { padding:0; }\n .ReadMsgBody { width:100%; }\n .ExternalClass { width:100%; }\n .ExternalClass * { line-height:100%; }\n body { margin:0;padding:0;-webkit-text-size-adjust:100%;-ms-text-=\nsize-adjust:100%; }\n table, td { border-collapse:collapse;mso-table-lspace:0pt;mso-tab=\nle-rspace:0pt; }\n img { border:0;height:auto;line-height:100%; outline:none;text-de=\ncoration:none;-ms-interpolation-mode:bicubic; }\n p { display:block;margin:13px 0; }</style><!--[if !mso]><!--><sty=\nle type=3D"text/css">@media only screen and (max-width:480px) {\n @-ms-viewport { width:320px; }\n @viewport { width:320px; }\n }</style><!--<![endif]--><!--[if mso]>\n <xml>\n <o:OfficeDocumentSettings>\n <o:AllowPNG/>\n <o:PixelsPerInch>96</o:PixelsPerInch>\n </o:OfficeDocumentSettings>\n </xml>\n <![endif]--><!--[if lte mso 11]>\n <style type=3D"text/css">\n .outlook-group-fix { width:100% !important; }\n </style>\n <![endif]--><style type=3D"text/css">@media only screen and (min-wi=\ndth:480px) {\n .mj-column-per-100 { width:100% !important; max-width: 100%; }\n }</style><style type=3D"text/css">@media only screen and (max-width:4=\n80px) {\n table.full-width-mobile { width: 100% !important; }\n td.full-width-mobile { width: auto !important; }\n }</style></head><body style=3D"background-color:#E7E7E7;"><div style=3D=\n"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;ma=\nx-width:0px;opacity:0;overflow:hidden;">Welkom bij PaReg! Verifieer eerst u=\nw mailadres.</div><div style=3D"background-color:#E7E7E7;"><table align=3D"=\ncenter" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentati=\non" style=3D"background:#E7E7E7;background-color:#E7E7E7;width:100%;"><tbod=\ny><tr><td><!--[if mso | IE]><table align=3D"center" border=3D"0" cellpaddin=\ng=3D"0" cellspacing=3D"0" class=3D"" style=3D"width:600px;" width=3D"600" >=\n<tr><td style=3D"line-height:0px;font-size:0px;mso-line-height-rule:exactly=\n;"><![endif]--><div style=3D"Margin:0px auto;max-width:600px;"><table align=\n=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presen=\ntation" style=3D"width:100%;"><tbody><tr><td style=3D"direction:ltr;font-si=\nze:0px;padding:20px 0;padding-bottom:0;text-align:center;vertical-align:top=\n;"><!--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=\n=3D"0" cellspacing=3D"0"><tr><td class=3D"" style=3D"vertical-align:top;wid=\nth:600px;" ><![endif]--><div class=3D"mj-column-per-100 outlook-group-fix" =\nstyle=3D"font-size:13px;text-align:left;direction:ltr;display:inline-block;=\nvertical-align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cells=\npacing=3D"0" role=3D"presentation" style=3D"vertical-align:top;" width=3D"1=\n00%"><tr><td align=3D"center" style=3D"font-size:0px;padding:10px 25px;word=\n-break:break-word;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0"=\n role=3D"presentation" style=3D"border-collapse:collapse;border-spacing:0px=\n;"><tbody><tr><td style=3D"width:255px;"><img alt=3D"" height=3D"auto" src=\n=3D"https://www.equinem.com/static/img/pareg/icon_1024x1024.png" style=3D"b=\norder:0;display:block;outline:none;text-decoration:none;height:auto;width:1=\n00%;" width=3D"255"></td></tr></tbody></table></td></tr><tr><td align=3D"le=\nft" style=3D"font-size:0px;padding:10px 25px;word-break:break-word;"><div s=\ntyle=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, sans-serif;font-siz=\ne:16px;font-weight:400;line-height:24px;text-align:left;color:#000000;"></d=\niv></td></tr><tr><td align=3D"left" style=3D"font-size:0px;padding:10px 25p=\nx;word-break:break-word;"><div style=3D"font-family:\'Helvetica Neue\', Helve=\ntica, Arial, sans-serif;font-size:16px;font-weight:400;line-height:24px;tex=\nt-align:left;color:#000000;"></div></td></tr></table></div><!--[if mso | IE=\n]></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if mso=\n | IE]></td></tr></table><![endif]--></td></tr></tbody></table><!--[if mso =\n| IE]><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D=\n"0" class=3D"" style=3D"width:600px;" width=3D"600" ><tr><td style=3D"line-=\nheight:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div st=\nyle=3D"background:#22243f;background-color:#22243f;Margin:0px auto;max-widt=\nh:600px;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacin=\ng=3D"0" role=3D"presentation" style=3D"background:#22243f;background-color:=\n#22243f;width:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;pa=\ndding:20px 0;text-align:center;vertical-align:top;"><!--[if mso | IE]><tabl=\ne role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"0"><t=\nr><td class=3D"" style=3D"vertical-align:top;width:600px;" ><![endif]--><di=\nv class=3D"mj-column-per-100 outlook-group-fix" style=3D"font-size:13px;tex=\nt-align:left;direction:ltr;display:inline-block;vertical-align:top;width:10=\n0%;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presen=\ntation" style=3D"vertical-align:top;" width=3D"100%"></table></div><!--[if =\nmso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div><!-=\n-[if mso | IE]></td></tr></table><table align=3D"center" border=3D"0" cellp=\nadding=3D"0" cellspacing=3D"0" class=3D"body-section-outlook" style=3D"widt=\nh:600px;" width=3D"600" ><tr><td style=3D"line-height:0px;font-size:0px;mso=\n-line-height-rule:exactly;"><![endif]--><div class=3D"body-section" style=\n=3D"-webkit-box-shadow: 1px 4px 11px 0px rgba(0, 0, 0, 0.15); -moz-box-shad=\now: 1px 4px 11px 0px rgba(0, 0, 0, 0.15); box-shadow: 1px 4px 11px 0px rgba=\n(0, 0, 0, 0.15); Margin: 0px auto; max-width: 600px;"><table align=3D"cente=\nr" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" s=\ntyle=3D"width:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;pa=\ndding:20px 0;padding-bottom:0;padding-top:0;text-align:center;vertical-alig=\nn:top;"><!--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpad=\nding=3D"0" cellspacing=3D"0"><tr><td class=3D"" width=3D"600px" ><table ali=\ngn=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"" s=\ntyle=3D"width:600px;" width=3D"600" ><tr><td style=3D"line-height:0px;font-=\nsize:0px;mso-line-height-rule:exactly;"><![endif]--><div style=3D"backgroun=\nd:#ffffff;background-color:#ffffff;Margin:0px auto;max-width:600px;"><table=\n align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"=\npresentation" style=3D"background:#ffffff;background-color:#ffffff;width:10=\n0%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;padding:20px 0;pad=\nding-left:15px;padding-right:15px;text-align:center;vertical-align:top;"><!=\n--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=3D"0"=\n cellspacing=3D"0"><tr><td class=3D"" style=3D"vertical-align:top;width:570=\npx;" ><![endif]--><div class=3D"mj-column-per-100 outlook-group-fix" style=\n=3D"font-size:13px;text-align:left;direction:ltr;display:inline-block;verti=\ncal-align:top;width:100%;"><table border=3D"0" cellpadding=3D"0" cellspacin=\ng=3D"0" role=3D"presentation" style=3D"vertical-align:top;" width=3D"100%">=\n<tr><td align=3D"left" style=3D"font-size:0px;padding:10px 25px;word-break:=\nbreak-word;"><div style=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, =\nsans-serif;font-size:16px;font-weight:400;line-height:24px;text-align:left;=\ncolor:#22243f;"><h1>Welkom bij PaReg</h1></div></td></tr><tr><td align=3D"l=\neft" style=3D"font-size:0px;padding:10px 25px;word-break:break-word;"><div =\nstyle=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, sans-serif;font-si=\nze:16px;font-weight:400;line-height:24px;text-align:left;color:#637381;">Ha=\nllo {{user}},</div></td></tr><tr><td align=3D"left" style=3D"font-size:0px;=\npadding:10px 25px;word-break:break-word;"><div style=3D"font-family:\'Helvet=\nica Neue\', Helvetica, Arial, sans-serif;font-size:16px;font-weight:400;line=\n-height:24px;text-align:left;color:#637381;">Bevestig uw account door op on=\nderstaande knop te drukken.</div></td></tr><tr><td align=3D"center" vertica=\nl-align=3D"middle" style=3D"font-size:0px;padding:10px 25px;padding-bottom:=\n0;word-break:break-word;"><table border=3D"0" cellpadding=3D"0" cellspacing=\n=3D"0" role=3D"presentation" style=3D"border-collapse:separate;line-height:=\n100%;"><tr><td align=3D"center" bgcolor=3D"#22243f" role=3D"presentation" s=\ntyle=3D"border:none;border-radius:3px;cursor:auto;padding:10px 25px;backgro=\nund:#22243f;" valign=3D"middle"><a href=3D"{{ activate_url }}" style=3D"bac=\nkground:#22243f;color:#ffffff;font-family:\'Helvetica Neue\', Helvetica, Aria=\nl, sans-serif;font-size:13px;font-weight:normal;line-height:120%;Margin:0;t=\next-decoration:none;text-transform:none;" target=3D"_blank">Verifieer email=\nadres</a></td></tr></table></td></tr><tr><td align=3D"center" style=3D"font=\n-size:0px;padding:10px 25px;padding-top:0;padding-bottom:0;word-break:break=\n-word;"><div style=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, sans-=\nserif;font-size:12px;font-weight:400;line-height:24px;text-align:center;col=\nor:#000000;">{{activate_url}}<br>Kopieer dit in een browser als de knop nie=\nt werkt.</div></td></tr></table></div><!--[if mso | IE]></td><td class=3D""=\n style=3D"vertical-align:top;width:570px;" ><![endif]--><div class=3D"mj-co=\nlumn-per-100 outlook-group-fix" style=3D"font-size:13px;text-align:left;dir=\nection:ltr;display:inline-block;vertical-align:top;width:100%;"><table bord=\ner=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D=\n"vertical-align:top;" width=3D"100%"><tr><td align=3D"left" style=3D"font-s=\nize:0px;padding:10px 25px;padding-top:40px;padding-bottom:0px;word-break:br=\neak-word;"><div style=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, sa=\nns-serif;font-size:16px;font-weight:400;line-height:24px;text-align:left;co=\nlor:#637381;">Wij wensen u namens alle sponsoren en het hele team van PaReg=\n veel gemak bij het registreren van de locatie van de paarden!</div></td></=\ntr></table></div><!--[if mso | IE]></td><td class=3D"" style=3D"vertical-al=\nign:top;width:570px;" ><![endif]--><div class=3D"mj-column-per-100 outlook-=\ngroup-fix" style=3D"font-size:13px;text-align:left;direction:ltr;display:in=\nline-block;vertical-align:top;width:100%;"><table border=3D"0" cellpadding=\n=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"vertical-align:top;=\n" width=3D"100%"><tr><td align=3D"left" style=3D"font-size:0px;padding:10px=\n 25px;padding-top:40px;padding-bottom:0px;word-break:break-word;"><div styl=\ne=3D"font-family:\'Helvetica Neue\', Helvetica, Arial, sans-serif;font-size:1=\n6px;font-weight:400;line-height:24px;text-align:left;color:#637381;">Voor v=\nragen kunt u ons via email bereiken: <a href=3D"mailto:[email protected]">info@=\npareg.nl</a></div></td></tr></table></div><!--[if mso | IE]></td></tr></tab=\nle><![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr>=\n</table></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[=\nif mso | IE]></td></tr></table><![endif]--><table align=3D"center" border=\n=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"w=\nidth:100%;"><tbody><tr><td><!--[if mso | IE]><table align=3D"center" border=\n=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"" style=3D"width:600px;=\n" width=3D"600" ><tr><td style=3D"line-height:0px;font-size:0px;mso-line-he=\night-rule:exactly;"><![endif]--><div style=3D"Margin:0px auto;max-width:600=\npx;"><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"=\n0" role=3D"presentation" style=3D"width:100%;"><tbody><tr><td style=3D"dire=\nction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top=\n;"><!--[if mso | IE]><table role=3D"presentation" border=3D"0" cellpadding=\n=3D"0" cellspacing=3D"0"><tr><td class=3D"" width=3D"600px" ><table align=\n=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=3D"0" class=3D"" sty=\nle=3D"width:600px;" width=3D"600" ><tr><td style=3D"line-height:0px;font-si=\nze:0px;mso-line-height-rule:exactly;"><![endif]--><div style=3D"Margin:0px =\nauto;max-width:600px;"><table align=3D"center" border=3D"0" cellpadding=3D"=\n0" cellspacing=3D"0" role=3D"presentation" style=3D"width:100%;"><tbody><tr=\n><td style=3D"direction:ltr;font-size:0px;padding:20px 0;text-align:center;=\nvertical-align:top;"><!--[if mso | IE]><table role=3D"presentation" border=\n=3D"0" cellpadding=3D"0" cellspacing=3D"0"><tr><td class=3D"" style=3D"vert=\nical-align:top;width:600px;" ><![endif]--><div class=3D"mj-column-per-100 o=\nutlook-group-fix" style=3D"font-size:13px;text-align:left;direction:ltr;dis=\nplay:inline-block;vertical-align:top;width:100%;"><table border=3D"0" cellp=\nadding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><tbody>=\n<tr><td style=3D"vertical-align:top;padding:0;"><table border=3D"0" cellpad=\nding=3D"0" cellspacing=3D"0" role=3D"presentation" width=3D"100%"><!-- <mj-=\nsocial font-size=3D"15px" icon-size=3D"30px" mode=3D"horizontal" padding=3D=\n"0" align=3D"center">\n <mj-social-element name=3D"facebook" href=3D"https://www.facebo=\nok.com/EquineM1/" background-color=3D"#A1A0A0">\n </mj-social-element>\n </mj-social> --><!-- <mj-text color=3D"#445566" font-size=3D"11px=\n" font-weight=3D"bold" align=3D"center">\n View this email in your browser\n </mj-text> --><tr><td align=3D"center" style=3D"font-size:0px;pad=\nding:10px 25px;word-break:break-word;"><div style=3D"font-family:\'Helvetica=\n Neue\', Helvetica, Arial, sans-serif;font-size:11px;font-weight:400;line-he=\night:16px;text-align:center;color:#445566;">{% blocktrans trimmed %} You ar=\ne receiving this email because you signed up for PaReg. {% endblocktrans %}=\n</div></td></tr><tr><td align=3D"center" style=3D"font-size:0px;padding:10p=\nx 25px;word-break:break-word;"><div style=3D"font-family:\'Helvetica Neue\', =\nHelvetica, Arial, sans-serif;font-size:11px;font-weight:400;line-height:16p=\nx;text-align:center;color:#445566;">&copy; EquineM B.V., {% trans \'All Righ=\nts Reserved.\' %}</div></td></tr></table></td></tr></tbody></table></div><!-=\n-[if mso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></di=\nv><!--[if mso | IE]></td></tr></table></td></tr><tr><td class=3D"" width=3D=\n"600px" ><table align=3D"center" border=3D"0" cellpadding=3D"0" cellspacing=\n=3D"0" class=3D"" style=3D"width:600px;" width=3D"600" ><tr><td style=3D"li=\nne-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--><div=\n style=3D"Margin:0px auto;max-width:600px;"><table align=3D"center" border=\n=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentation" style=3D"w=\nidth:100%;"><tbody><tr><td style=3D"direction:ltr;font-size:0px;padding:20p=\nx 0;padding-top:0;text-align:center;vertical-align:top;"><!--[if mso | IE]>=\n<table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D"=\n0"><tr><td class=3D"" style=3D"width:600px;" ><![endif]--><div class=3D"mj-=\ncolumn-per-100 outlook-group-fix" style=3D"font-size:0;line-height:0;text-a=\nlign:left;display:inline-block;width:100%;direction:ltr;"><!--[if mso | IE]=\n><table role=3D"presentation" border=3D"0" cellpadding=3D"0" cellspacing=3D=\n"0"><tr><td style=3D"vertical-align:top;width:600px;" ><![endif]--><div cla=\nss=3D"mj-column-per-100 outlook-group-fix" style=3D"font-size:13px;text-ali=\ngn:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">=\n<table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"presentatio=\nn" width=3D"100%"><tbody><tr><td style=3D"vertical-align:top;padding-right:=\n0;"><table border=3D"0" cellpadding=3D"0" cellspacing=3D"0" role=3D"present=\nation" width=3D"100%"><tr><td align=3D"center" style=3D"font-size:0px;paddi=\nng:10px 25px;word-break:break-word;"><div style=3D"font-family:\'Helvetica N=\neue\', Helvetica, Arial, sans-serif;font-size:11px;font-weight:bold;line-hei=\nght:16px;text-align:center;color:#445566;"><a class=3D"footer-link" href=3D=\n"{% url \'site1:privacy\' %}" style=3D"color: #888888;">{% trans \'Privacy\' %}=\n</a></div></td></tr></table></td></tr></tbody></table></div><!--[if mso | I=\nE]></td></tr></table><![endif]--></div><!--[if mso | IE]></td></tr></table>=\n<![endif]--></td></tr></tbody></table></div><!--[if mso | IE]></td></tr></t=\nable></td></tr></table><![endif]--></td></tr></tbody></table></div><!--[if =\nmso | IE]></td></tr></table><![endif]--></td></tr></tbody></table></div></b=\nody></html>\n--===============5112651050379218236==--\n'

    When we send this email post_office does not store that it is encoded as quoted printable. And sends it as 7bit, see line https://github.com/ui/django-post_office/blob/a20c3e705da6705452ad501d6c70615b35344965/post_office/models.py#L133-L139 Thus rendering the email unreadable for the user.

    I'm not sure how to fix this. There are 2 options:

    1. First decode the html message when storing in the database (in the backend)
    2. Store that the html message is stored as quoted_printable and set the correct headers in prepare_email_message
    opened by gabn88 6
  • Poor performance in EmailAdmin view.

    Poor performance in EmailAdmin view.

    There is a performance issue when viewing an Email in the EmailAdmin when there are a lot Emails and Attachments in the database. This is caused by customizing the get_queryset method of AttachmentInline to try to filter out inline attachments by their header values.

    In the post_office.admin.AttachmentInline.get_queryset method, Django has not yet applied the Email FK filter to the queryset. That happens later when the inline formset is being created.

    That means that the query being run in the get_queryset method actually fetches and iterates over every single Email Attachment object in the database, regardless of the Email it is for. In the loop, it also references the attachment property, but without a select_related on the query, it will have to fetch each attachment separately for every iteration.

    If you have a lot of Emails and Attachments in the database, this causes performance issues.

    opened by barqshasbite 0
Releases(v3.6.3)
  • v3.6.3(Oct 27, 2022)

    • Fixed an issue where emails may not be rendered with context. Thanks @zagl!
    • Fixed a few packaging issues. Thanks @zagl and @adamchainz!
    • send_messages() now mimics Django's SMTP Backend return value. Thanks @JiriKr!
    Source code(tar.gz)
    Source code(zip)
  • v3.6.2(Oct 12, 2022)

    • Improvement to attachment handling in admin interface. Thanks @petrprikryl!
    • Fixed a bug where HTML body is not displayed in admin interface. Thanks @robbieadi!
    • Explicitly specify default_auto_field to supress migration warnings. Thanks @CirXe0N!
    • Fixed a bug where email.template is not saved in certain cases. Thanks @franciscobmacedo!
    Source code(tar.gz)
    Source code(zip)
  • v3.6.1(Jul 4, 2022)

    • Support for bleach >= 5.0. Thanks @franciscobmacedo!
    • Ensure that Reply-To headers are correctly set. Thanks @christophmeissner!
    • Add a Resend button in admin to easily resend an email. Thanks @domdinicola!
    Source code(tar.gz)
    Source code(zip)
  • v3.6.0(Dec 21, 2021)

    • Support for Django 4.0. Thanks @domdinicola!
    • cleanup_mail now deletes emails in batches, which is much nicer to DB when deleting millions of emails. Thanks @stevemc4!
    • Failure to send an email are now logged as an exception. Thanks @SaturnFromTitan!
    • Added es locale. Thanks @ahmontero!
    • Fixed admin template discovery issue for case-sensitive filesystems. Thanks @fasih!
    • Fixes: SMTPServerDisconnected error. Thanks @weimens!
    • Various maintenance work by @jrief and @mogost.
    Source code(tar.gz)
    Source code(zip)
  • v3.5.2(Nov 5, 2020)

  • v3.5.0(Oct 31, 2020)

    • Added the capability to configure retries via MAX_RETRIES and RETRY_INTERVAL configuration settings. Thanks @Houtmann and @jrief!
    • The admin interface has been improved to show previews of emails. If you want HTML emails to be rendered, please install bleach. Thanks @jrief!
    • Add Message-ID to the Email model. This allows administrators to trace back emails. Thanks @jrief!
    • Added CELERY_ENABLED settings. Thanks @elineda!
    • Fixes an issue that prevents PDS attachments from being sent. Thanks @patroqueeet!
    Source code(tar.gz)
    Source code(zip)
Owner
User Inspired
User Inspired
A small system for writing via email.

A small system for writing via email.

0 Nov 24, 2021
A SMTP server for use as a pytest fixture that implements encryption and authentication.

SMTPDFix: Test email, locally A simple SMTP server based on aiosmtpd for use as a fixture with pytest that supports encryption and authentication. All

James Warne 11 Sep 03, 2022
Automated email sending application.

autoMail Automated email sending application. This application sends email to a user when added to database. Email message contains the temperature of

Bhat Owais 1 Feb 12, 2022
Collection of emails sent from the Hungarian gov and Viktor Orbán to the citizens of Hungary

Public list of Hungary and Viktor Orbán's emails since March 2021 Collection of emails sent from the Hungarian government and Viktor Orbán to the citi

Miguel Sozinho Ramalho 1 Mar 28, 2022
Django module to easily send templated emails using django templates, or using a transactional mail provider (mailchimp, silverpop, etc.)

Django-Templated-Email Info: A Django oriented templated email sending class Author: Bradley Whittington (http://github.com/bradwhittington, http://tw

Vinta Software 659 Dec 27, 2022
This Tool Is For Sending Emails From A Terminal(Termux/Kali) etc.

This is a Basic python script to send emails from a Terminal(Termux/Kali) are the only tested currently.

AnonyVox 2 Apr 04, 2022
This simple python script uses cv2 to create and mail certificates to participants of workshops.

This simple python script uses cv2 to create and mail certificates to participants of workshops. Just collect the names and email ids of participants in a csv file (i used google docs), and place it

Sounder Rajendran 0 Dec 19, 2022
Automatically Send Custom Named Certificates via Mail

Welcome to Certificate Launchpad 🚀 Automatically Send Custom Named Certificates via Email Intro After any event, sending certificates to attendees or

Dc7 16 Oct 16, 2022
An offline Phishing Email Analyzer.

An offline Phishing Email Analyzer.

Kamran Saifullah (Frog Man) 11 Oct 19, 2022
A Django email backend for Amazon's Simple Email Service

Django-SES Info: A Django email backend for Amazon's Simple Email Service Author: Harry Marr (http://github.com/hmarr, http://twitter.com/harrymarr) C

882 Dec 29, 2022
Use Django admin to manage drip campaign emails using querysets on Django's User model.

Django Drip Drip campaigns are pre-written sets of emails sent to customers or prospects over time. Django Drips lets you use the admin to manage drip

Zapier 630 Nov 16, 2022
A simple free API that allows you to extract abuse emails from IPs.

Abuse-Email-API A simple free API that allows you to extract abuse emails from IPs. also isnt worth 500 dollars :) Requirements A Debian based OS The

Keratin 1 Dec 20, 2021
You take an email and password from the combo list file and check it on mail.com

Brute-Force-mail tool information: Combo Type: email:pass Domains: All domains of the site Url: https://www.mail.com Api: ☑️ Proxy: No ☑️ The correct

6 Jun 05, 2022
Tempmail API aswell as a SMTP server.

Tempmail API/Server Tempmail API aswell as a SMTP server. Website · Report Bug · Request Feature Setup Firstly create a mongodb account, and proceed t

femboy.party 16 Mar 09, 2022
Mailrise is an SMTP server that converts the emails it receives into Apprise notifications

Mailrise is an SMTP server that converts the emails it receives into Apprise notifications. The intended use case is as an email relay for a home lab or network. By accepting ordinary email, Mailrise

Ryan Young 293 Jan 07, 2023
Email-bomber - Email bomber unlike other email bombers u don't need your gmail email id to use this

Email-bomber - Email bomber unlike other email bombers u don't need your gmail email id to use this

rfeferfefe 82 Dec 17, 2022
A CLI client for sending text emails. (Currently only gmail supported)

emailCLI A CLI client for sending text emails. (Currently only gmail supported)

Amethist 3 Dec 17, 2021
A news curator and newsletter subscription package for Django

django-newsfeed What is django-newsfeed? django-newsfeed is a news curator and newsletter subscription package for django. It can be used to create a

Maksudul Haque 179 Nov 14, 2022
Django SMTP Protocol with Gmail

Django SMTP Protocol with Gmail This is the free service from gmail to send and receive emails. What we need for this things done, Python/pip install

Mehedi Hasan 3 Dec 13, 2022
A python script that helps you understand why your E-Mail ended up in Spam

decode-spam-headers.py Whether you are trying to understand why a specific e-mail ended up in SPAM/Junk for your daily Administrative duties or for yo

Mariusz Banach 316 Jan 05, 2023