Enviando e-mail

Embora Python torne o envio de email relativamente simples através do módulo smtplib, o Django fornece alguns “wrappers” em torno dele. Esses wrappers, algo como “capas”, “invólucros” em tradução livre para o português, são fornecidos para tornar o envio de email mais rápido, para tornar mais simples o teste de email enviado durante o desenvolvimento, e para fornecer suporte a plataformas que não podem usar SMTP.

O código pode ser encontrado no módulo django.core.mail.

Exemplo rápido

Em duas linhas:

from django.core.mail import send_mail

send_mail(
    'Subject here',
    'Here is the message.',
    'from@example.com',
    ['to@example.com'],
    fail_silently=False,
)

O email é enviado usando o host e porta SMTP especificados em EMAIL_HOST e EMAIL_PORT. As configurações EMAIL_HOST_USER e EMAIL_HOST_PASSWORD, se definidas, são usadas para autenticar no servidor SMTP, e as configurações EMAIL_USE_TLS e EMAIL_USE_SSL controlam se uma conexão segura deve ou não ser usada.

Nota

O conjunto de caracteres do email enviado com django.core.mail será definido como sendo o valor da configuração DEFAULT_CHARSET.

send_mail()

send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)[código fonte]

A maneira mais simples de enviar um email é usando django.core.mail.send_mail().

Os parâmetros subject, message, from_email e recipient_list devem ser informados.

  • subject: uma string.
  • message: uma string.
  • from_email: uma string.
  • recipient_list: uma lista de strings, onde cada item representa um endereço de email. Cada membro de recipient_list verá os outros recipientes no campo “To:” da mensagem de email.
  • fail_silently: A boolean. When it’s False, send_mail() will raise an smtplib.SMTPException if an error occurs. See the smtplib docs for a list of possible exceptions, all of which are subclasses of SMTPException.
  • auth_user: O username opcional para ser usado na autenticação do servidor SMTP. Se isso não for fornecido, o Django irá usar o valor da configuração EMAIL_HOST_USER.
  • auth_password: A senha opcional para ser usada na autenticação no servidor SMTP. Se isso não for fornecido, o Django irá usar o valor da configuração EMAIL_HOST_PASSWORD.
  • connection: O backend opcional a ser usado para o de email. Se indefinido, uma instância do backend default será usada. Veja a documentação em Email backends para mais detalhes.
  • html_message: Se html_message for fornecido, o email resultante será um multipart/alternative email com message sendo do tipo text/plain e html_message sendo do tipo text/html.

O valor retornado será o número de mensagens entregues com sucesso (o que pode ser 0 ou 1 já que ele só pode enviar uma mensagem).

send_mass_mail()

send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)[código fonte]

django.core.mail.send_mass_mail() foi pensado para manipular envios massivos de email.

datatuple é uma tupla na qual cada elemento possui o seguinte formato:

(subject, message, from_email, recipient_list)

fail_silently, auth_user e auth_password possuem as mesmas funções do método send_mail().

Cada elemento separadamente da datatuple resulta em uma mensagem em separado. Como no método send_mail(), destinatários na mesma lista recipient_list serão capazes de visualizar os outros endereços no campo “To:” das mensagens de email.

Por exemplo, o código abaixo enviaria duas mensagens diferentes para dois conjuntos diferentes de destinatários; porém, somente uma conexão para o servidor de email seria aberta:

message1 = ('Subject here', 'Here is the message', 'from@example.com', ['first@example.com', 'other@example.com'])
message2 = ('Another Subject', 'Here is another message', 'from@example.com', ['second@test.com'])
send_mass_mail((message1, message2), fail_silently=False)

O valor de retorno será o número de mensagens entregues com sucesso.

send_mass_mail() vs. send_mail()

A principal diferença entre os métodos send_mass_mail() e send_mail() é que o método send_mail() abre uma nova conexão para o servidor de email toda vez que é executado, enquanto que o método send_mass_mail() utiliza uma única conexão para todas as suas mensagens. Isso faz com que o método send_mass_mail() seja um pouco mais eficiente.

mail_admins()

mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)[código fonte]

django.core.mail.mail_admins() é um atalho para o envio de email para os admins do site, conforme definido na configuração ADMINS setting.

mail_admins() prefixa o conteúdo com o valor da configuração EMAIL_SUBJECT_PREFIX, que é "[Django] " por padrão.

O cabeçalho “From:” do email será o valor da configuração SERVER_EMAIL.

Esse método existe por conveniência e legibilidade.

Se html_message é fornecido, o email resultante será um email do tipo multipart/alternative com message sendo do tipo text/plain e html_message sendo do tipo text/html.

mail_managers()

mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)[código fonte]

django.core.mail.mail_managers() é parecido com mail_admins(), exceto pelo fato de que ele envia um email para os mantenedores do site, conforme definido na configuração MANAGERS.

Exemplos

Isso envia um único email para john@example.com e jane@example.com, com ambos aparecendo no campo “To:”:

send_mail(
    'Subject',
    'Message.',
    'from@example.com',
    ['john@example.com', 'jane@example.com'],
)

Isso envia uma mensagem para john@example.com e jane@example.com, com ambos recebendo um email em separado:

datatuple = (
    ('Subject', 'Message.', 'from@example.com', ['john@example.com']),
    ('Subject', 'Message.', 'from@example.com', ['jane@example.com']),
)
send_mass_mail(datatuple)

Prevenindo injeção de cabeçalhos

injeção de cabeçalhos é uma vulnerabilidade de segurança onde um atacante insere cabeçalhos extras no email para controlar os campos “To:” e “From:” em mensagens de email geradas pelos seus scripts.

As funções de email do Django descritas acima protegem contra injeção de cabeçalhos proibindo novas linhas nos valores do cabeçalho. Se qualquer campo subject, from_email ou recipient_list contém uma nova linha (seja no estilo Unix, Windows ou Mac), a função de email (por exemplo, o método BadHeaderError`() (uma subclasse de ValueError) e, por tanto, não envia o email. É sua responsabilidade validar todos os dados do email antes de passá-los para as funções de email.

se um campo message contém cabeçalhos no começo da string, os cabeçalhos simplesmente serão exibidos como sendo a primeira parte da mensagem de email.

Segue uma view de exemplo que recebe subject, message e from_email de uma requisição POST, envia esses dados para admin@example.com e redireciona para “/contact/thanks/” no final:

from django.core.mail import BadHeaderError, send_mail
from django.http import HttpResponse, HttpResponseRedirect

def send_email(request):
    subject = request.POST.get('subject', '')
    message = request.POST.get('message', '')
    from_email = request.POST.get('from_email', '')
    if subject and message and from_email:
        try:
            send_mail(subject, message, from_email, ['admin@example.com'])
        except BadHeaderError:
            return HttpResponse('Invalid header found.')
        return HttpResponseRedirect('/contact/thanks/')
    else:
        # In reality we'd use a form class
        # to get proper validation errors.
        return HttpResponse('Make sure all fields are entered and valid.')

A classe EmailMessage

As funções do Django send_mail() e send_mass_mail() são na verdade pequenos wrappers que fazem uso da classe EmailMessage.

Nem todas as funcionalidades da classe EmailMessage estão disponíveis através das funções send_mail() e funções wrapper relacionadas. Se você quiser usar recursos avançados, tais como destinatários BCC’ed, anexar de arquivos, ou email de múltiplas partes, você terá que criar instâncias da classe EmailMessage diretamente.

Nota

Esse é um recurso de design. A função send_mail() e funções relacionadas foram originalmente a única interface que o Django fornecia. Porém, a lista de parâmetros que elas aceitavam foi aos poucos crescendo com o passar do tempo. Fez sentido mudar para um design mais orientado a objetos para mensagens de email e manter as funções originais apenas para manter compatibilidade com versões anteriores.

A classe EmailMessage é responsável por criar a mensagem de email em si. O email backend é depois responsável pelo envio da mensagem.

Por praticidade, a classe EmailMessage fornece um método simples send() para enviar um único email. Se você precisa enviar múltiplas mensagens, a API do email backend fornece uma alternativa.

Objetos EmailMessage

class EmailMessage[código fonte]

A classe EmailMessage é inicializada com os seguintes parâmetros (na seguinte ordem, se argumentos posicionais são usados). Todos os parâmetros são opcionais e podem ser informados a qualquer momento anterior a chamada do método send().

  • subject: A linha de assunto do email.
  • body: O corpo do texto. Deve ser uma mensagem em texto simples.
  • from_email: The sender’s address. Both fred@example.com and "Fred" <fred@example.com> forms are legal. If omitted, the DEFAULT_FROM_EMAIL setting is used.
  • to: Uma lista de tuplas de endereços dos destinatários.
  • bcc: uma lista de tuplas de endereços usados no cabeçalho “Bcc” ao enviar o email.
  • connection: uma instância do email backend. Utilize esse parâmetro se você quiser usar a mesma conexão para múltiplas mensagens. Se omitido, uma nova conexão é criada quando a função send() é chamada.
  • attachments: A list of attachments to put on the message. These can be either MIMEBase instances, or (filename, content, mimetype) triples.
  • headers: um dicionário de cabeçalhos extras para colocar na mensagem. As chaves são os nomes dos cabeçalhos, valores são os valores do cabeçalho. É responsabilidade de quem chama garantir que os nomes e valores dos cabeçalhos estão no formato correto para uma mensagem de email. O atributo correspondente é extra_headers.
  • cc: uma lista ou tupla de endereços de destinatários usados no cabeçalho “Cc” no envio do email.
  • reply_to: uma lista ou tupla de endereços de destinatários usados no cabeçalho “Reply-To” no envio do email.

Por exemplo:

from django.core.mail import EmailMessage

email = EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to1@example.com', 'to2@example.com'],
    ['bcc@example.com'],
    reply_to=['another@example.com'],
    headers={'Message-ID': 'foo'},
)

A classe possui os seguintes métodos:

  • send(fail_silently=False) envia a mensagem. Se uma conexão foi especificada quando o email foi construído, a conexão será usada. Caso contrário, uma instância do backend padrão será instanciada e usada. Se o argumento nomeado fail_silently é True, exceções lançadas durante o envio da mensagem serão invalidadas. Uma lista de destinatários vazia não irá lançar uma exceção.

  • message() constructs a django.core.mail.SafeMIMEText object (a subclass of Python’s MIMEText class) or a django.core.mail.SafeMIMEMultipart object holding the message to be sent. If you ever need to extend the EmailMessage class, you’ll probably want to override this method to put the content you want into the MIME object.

  • recipients() retorna uma lista de todos os destinatários da mensagem, independentemente de eles serem gravados nos atributos to, cc ou bcc. Esse é outro método que você pode precisar sobrescrever ao fazer uma subclasse, porque o servidor SMTP precisa saber toda a lista de destinatários quando a mensagem é enviada. Se você adicionar outra forma de especificar os destinatários em sua classe, eles precisam ser retornados desse método também.

  • attach() cria um novo anexo de arquivo e adiciona ele a mensagem. Existem duas maneiras de chamar attach():

    • You can pass it a single argument that is a MIMEBase instance. This will be inserted directly into the resulting message.

    • Alternativamente, você pode passar ao attach() três argumentos: filename, content e mimetype. filename é o nome do anexo de arquivo que será exibido no email, content é a data que estará contida dentro do anexo e mimetype é o tipo MIME opcional para o anexo. Se você omitir mimetype, o tipo MIME será inferido do nome do anexo de arquivo.

      Por exemplo:

      message.attach('design.png', img_data, 'image/png')
      

      Se você especificar um mimetype do tipo message/rfc822, ele também irá aceitar django.core.mail.EmailMessage e email.message.Message.

      For a mimetype starting with text/, content is expected to be a string. Binary data will be decoded using UTF-8, and if that fails, the MIME type will be changed to application/octet-stream and the data will be attached unchanged.

      Adicionalmente, anexos message/rfc822 não serão mais codificados com base64 por violarem a RFC 2046#section-5.2.1, o que pode causar problemas com a exibição de anexos no Evolution e no Thunderbird.

  • attach_file() cria um novo anexo usando um arquivo do seu sistema de arquivo. Chame-o com o caminho do arquivo a ser anexado e, opcionalmente, o tipo MIME a ser usado no anexo. Se o tipo MIME é omitido, ele será inferido do nome do arquivo. O uso mais simples seria:

    message.attach_file('/images/weather_map.png')
    

    For MIME types starting with text/, binary data is handled as in attach().

Enviando tipos de conteúdo alternativos

Pode ser útil incluir múltiplas versões de conteúdo em um email; o exemplo clássico é enviar tanto a versão em texto simples quanto a versão em HTML de uma mensagem. Com a biblioteca de email do Django, você pode fazer isso usando a classe EmailMultiAlternatives. Essa subclasse de EmailMessage possui um método attach_alternative() para inclusão de versões extras de corpos de mensagens no email. Todos os outros métodos (incluindo a inicialização da classe) são herdados diretamente da classe EmailMessage.

Para enviar uma combinação de texto simples e HTML, você pode escrever:

from django.core.mail import EmailMultiAlternatives

subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()

Por padrão, o tipo MIME de um parâmetro body em uma classe EmailMessage é "text/plain". É uma boa prática deixar assim, porque isso garante que qualquer destinatário esteja apto a ler o email, independentemente do cliente de email. Porém, se você estiver confiante que seus destinatários podem manipular um tipo alternativo de conteúdi, você pode usar o atributo content_subtype da classe EmailMessage para mudar o principal tipo de conteúdo. O principal tipo sempre será “text”``, mas você pode mudar o subtipo. Por exemplo:

msg = EmailMessage(subject, html_content, from_email, [to])
msg.content_subtype = "html"  # Main content is now text/html
msg.send()

Backends de email

O envio de email em si é manipulado pelo email backend.

A classe do email backend tem os métodos a seguir:

  • open() instancia uma conexão de longa duração para envio de email.
  • close() fecha a conexão atual de envio de email.
  • send_messages(email_messages) envia uma lista de objetos da classe EmailMessage. Se a conexão não estiver aberta, essa chamada irá implicitamente abrir a conexão, e fechar a conexão posteriormente. Se a conexão já estiver aberta, ela será mantida aberta depois de o email ser enviado.

Ela também pode ser usada como em um gerenciador de contexto, que irá automaticamente chamar open() e close() conforme necessário:

from django.core import mail

with mail.get_connection() as connection:
    mail.EmailMessage(
        subject1, body1, from1, [to1],
        connection=connection,
    ).send()
    mail.EmailMessage(
        subject2, body2, from2, [to2],
        connection=connection,
    ).send()

Obtendo uma instância de um email backend

A funçção get_connection() em django.core.mail retorna uma instância do email backend que você pode utilizar.

get_connection(backend=None, fail_silently=False, *args, **kwargs)[código fonte]

Por padrão, uma chamada para get_connection() irá retornar uma instância do email backend definido em EMAIL_BACKEND. Se você especificar o argumento backend, uma instância desse backend será criada.

O argumento fail_silently controla como o backend deve manipular erros. Se fail_silently é True, exceções durante o processo de envio de email serão ignoradas silenciosamente.

Todos os outros argumentos são passados diretamente para o construtor do email backend.

O Django é fornecido com vários backends de envio de email. Com exceção do backend SMTP (que é o padrão), esses backends só são úteis durante testes e desenvolvimento. Se você tiver requisitos especiais de envio de email, você pode escrever o seu próprio email backend.

SMTP backend

class backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs)

Este é o backend padrão. Email será enviado através de um servidor SMTP.

O valor de cada argumento é obtido da respectiva configuração se o argumento for None:

O backend SMTP é a configuração padrão herdada do Django. Se você quiser especificá-la explicitamente, adicione o seguinte as suas configurações:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

Se não definido, o timeout padrão será o fornecido pela função socket.getdefaulttimeout(), que por padrão é None (sem limite de tempo).

Console backend

Ao invés de enviar emails reais o console backend escreve esses emails que poderiam ser enviados na saída padrão. Por padrão, o console backend direciona para stdout. Você pode usar diferentes objetos fornecendo o argumento nomeado stream ao construir a conexão.

Para especificar esse backend, coloque o seguinte nas suas configurações:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Esse backend não é intencionado para uso em produção – ele é fornecido como uma conveniência que pode ser usado durante o desenvolvimento.

File backend

O file backend escreve emails em um arquivo. Um novo arquivo é criado para cada nova sessão que é aberto no backend. O diretório para o qual os arquivos são escritos ou é obitdo da configuração EMAIL_FILE_PATH ou do argumento nomeado file_path ao criar uma conexão com o método get_connection().

Para especificar esse backend, coloque o seguinte nas suas configurações:

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location

Esse backend não é intencionado para uso em produção – ele é fornecido como uma conveniência que pode ser usado durante o desenvolvimento.

In-memory backend

O 'locmem' backend armazena mensagens em um atributo especial do módulo django.core.mail. O atributo outbox é criado quando a primeira mensagem é enviada. É uma lista com uma instância da classe EmailMessage para cada mensagem que possa ser enviada.

Para especificar esse backend, coloque o seguinte nas suas configurações:

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Esse backend não é intencionado para uso em produção – ele é fornecido como uma conveniência que pode ser usado durante o desenvolvimento e teste.

Dummy backend

Como o nome sugere o dummy backend não faz nada com suas mensagens. Para definir esse backend, coloque o seguinte em suas configurações:

EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

Esse backend não é intencionado para uso em produção – ele é fornecido como uma conveniência que pode ser usado durante o desenvolvimento.

Definindo um email backend customizado

Se você precisa mudar como os emails são enviados você pode escrever o seu próprio email backend. A configuração EMAIL_BACKEND em seu arquivo de configurações é então o caminho Python do import para a sua classe backend.

Bckends de email customizados devem ser subclasses de BaseEmailBackend que está localizada no módulo django.core.mail.backends.base. Um backend de email customizado deve implementar o método send_messages(email_messages). Esse método recebe uma lista de instâncias da classe EmailMessage e retorna o número de mensagens entregues com sucesso. Se o seu backend possui qualquer conceito de sessão persistente ou conexão, você também deve implementar os métodos open() e close(). Consulte smtp.EmailBackend para uma implementação de referência.

Enviando mútiplos emails

Estabelecer e fechar uma conexão SMTP (ou qualquer outra conexão de rede, nesse caso) é um processo custoso. Se você tem muitos emails para enviar, faz sentido reutilizar uma conexão SMTP, ao invés de criar e destruir uma conexão toda vez que você desejar enviar um email.

Existem duas maneiras de dizer a um email backend para reutilizar uma conexão.

Primeiramente, você pode usar o método send_messages(). send_messages() recebe uma lista de instâncias da classe EmailMessage (ou de subclasses), e envia todas elas usando uma única conexão.

Por exemplo, se você tiver uma função chamada get_notification_email() que retorna uma lista de objetos da classe EmailMessage representando algum email periódico que você gostaria de enviar, voê pode enviá-los usando uma única chamada para send_messages:

from django.core import mail
connection = mail.get_connection()   # Use default email connection
messages = get_notification_email()
connection.send_messages(messages)

Neste exemplo, a chamada send_messages() abre uma conexão no backend, envia a lista de mensagens, e então fecha a conexão novamente.

A segunda abordagem é usar os métodos open() e close() no backend de email para manualmente controlar a conexão. send_messages() não irá abrir ou fechar manualmente a conexão se ela já estiver aberta, se você manualmente abrir a conexão, você pode controlar quando ela será fechada. Por exemplo:

from django.core import mail
connection = mail.get_connection()

# Manually open the connection
connection.open()

# Construct an email message that uses the connection
email1 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to1@example.com'],
    connection=connection,
)
email1.send() # Send the email

# Construct two more messages
email2 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to2@example.com'],
)
email3 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to3@example.com'],
)

# Send the two emails in a single call -
connection.send_messages([email2, email3])
# The connection was already open so send_messages() doesn't close it.
# We need to manually close the connection.
connection.close()

Configurando o email para o desenvolvimento

Existem momentos em que você não quer que o Django envie quaisquer emails. Por exemplo, enquanto estiver desenvolvendo um website, você provavelmente não quer enviar milhares de emails – mas você pode querer validar que esses email serão enviados para as pessoas certas nas condições certas, e que esses emails irão conter o conteúdo correto.

A maneira mais simples de configurar email para desenvolvimento local é usar o console email backend. Esse backend redireciona todos os emails para stdout, permitindo que você inspecione o conteúdo do email.

O file email backend também pode ser útil durante o desenvolvimento – esse backend despeja o conteúdo de todoas as conexões SMTP para um arquivo que pode ser inspecionado quando você desejar.

Outra abordagem é usar um servidor SMTP “burro” que recebe os emails localmente e exibe-os no terminal, mas que não os envia. Python possui uma maneira embutida para fazer isso com um único comando:

python -m smtpd -n -c DebuggingServer localhost:1025

Esse comando irá iniciar um servidor SMTP simples ouvindo a porta 1025 do localhost. Esse servidor simplesmente exibe na saída padrão todos os cabeçalhos e corpo do email. Você só precisa definir a configuração EMAIL_HOST e EMAIL_PORT apropriadamente. Para uma discussão mais detalhada das opções do servidor SMTP, veja o módulo smtpd na documentação do Python.

Para mais informações sobre testes unitários no envio de emails em suas aplicações, veja a seção Serviços de E-mail sobre testes da documentação.

Back to Top