2-8. Djangoでメールを送信する

今回のテーマは「Djangoでメールを送信する」です。ウェブアプリケーションではメールの送信は良くあるアクションの1つですね。今回はDjangoの機能を用いてメール送信する処理を見ていこうと思います。

公式ドキュメントの該当箇所としてはメールを送信するに該当します。

※本ページはサイトマップを作成するまで読まれた方を対象としています。そのためサンプルソースコードが省略されている場合があります。


メールバックエンドについて

メールの送信処理はメールバックエンドによって処理されます。メールバックエンドにはいくつか種類があり、目的に応じて使わけます。まず、開発時に試験的に(実際にメールを送信せず)送信テストをしたい場合はコンソールバックエンドの使用をオススメします。これはメールをコンソールに出力するのみで、送信しません。またファイルバックエンドもファイルに出力するのみで実際には送信しません。メールバックエンドは独自のものを使用することもできますが多くの場合はSMTPバックエンドを使用する機会が多いと思いますので、今回は開発環境ではコンソールバックエンド、送信用にはSMTPバックエンドを使用していきます。

メールバックエンドの設定

まずはmysite/settings.pyにて設定を行います。

mysite/settings.py(一部抜粋)


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

まずは開発用にコンソールバックエンドを指定しました。ではトピックの追加された場合にメールを送信する処理を行いましょう。

thread/views.py(一部抜粋)


+ from django.core.mail import send_mail, EmailMessage

  class TopicCreateView(CreateView):
      template_name = 'thread/create_topic.html'
      form_class = TopicModelForm
      model = Topic
      success_url = reverse_lazy('base:top')
  
      def form_valid(self, form):
          ctx = {'form': form}
          if self.request.POST.get('next', '') == 'confirm':
              ctx['category'] = form.cleaned_data['category']
              return render(self.request, 'thread/confirm_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'back':
              return render(self.request, 'thread/create_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'create':
+             # メール送信処理
+             send_mail(
+                 subject='トピック作成: ' + form.creaned_data['title'],
+                 message='トピックが生成されました。',
+                 from_email='hogehoge@example.com',
+                 recipient_list = [
+                     'admin@example.com',
+                 ]
+             )
              return super().form_valid(form)
          else:
              # 正常動作ではここは通らない。エラーページへの遷移でも良い
              return redirect(reverse_lazy('base:top'))

ここで用いたsend_mail関数はEmailMessageのラッパーで、単順なメール送信では重宝します。タイトル、本文、送信アドレス、受診アドレスを設定します。ではトピック作成してメールがコンソールに出力されるか見てみましょう。

出力例


Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject:
 =?utf-8?b?44OI44OU44OD44Kv5L2c5oiQOiDntKDmlbXjgarjg4vjg6Pjg7PjgrPjga7kuJY=?=
 =?utf-8?b?55WM?=
From: hogehoge@example.com
To: admin@example.com
Date: Wed, 20 Mar 2019 07:58:17 -0000
Message-ID: <155306869746.10086.12768691481838639735@arch.localdomain>

トピックが生成されました。
-------------------------------------------------------------------------------

このように出力されます。

テンプレートを使う
多くのウェブサービスではメール用のテンプレートを用意しておいて変数化された部分のみ変更してメール送信する処理も多いと思います。直接のメール機能というわけではないですが、紹介しておきたいと思います。まずはテンプレートファイルを生成します。

templates/thread/mail/topic_mail.html


以下のトピックが登録されました。

---------------------
タイトル: {{title}}
ユーザー名: {{user_name}}
本文:
{{message}}

thread/views.py(一部抜粋)


+ from django.core.mail import send_mail, EmailMessage
+ from django.template.loader import get_template

  class TopicCreateView(CreateView):
      template_name = 'thread/create_topic.html'
      form_class = TopicModelForm
      model = Topic
      success_url = reverse_lazy('base:top')
  
      def form_valid(self, form):
          ctx = {'form': form}
          if self.request.POST.get('next', '') == 'confirm':
              ctx['category'] = form.cleaned_data['category']
              return render(self.request, 'thread/confirm_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'back':
              return render(self.request, 'thread/create_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'create':
+             # メール送信処理
+             template = get_template('thread/mail/topic_mail.html')
+             mail_ctx={
+                 'title': form.cleaned_data['title'],
+                 'user_name': form.cleaned_data['user_name'],
+                 'message': form.cleaned_data['message'],
+             }
+             send_mail(
+                 subject='トピック作成: ' + form.cleaned_data['title'],
+                 message=template.render(mail_ctx),
+                 from_email='hogehoge@example.com',
+                 recipient_list = [
+                     'admin@example.com',
+                 ]
+             )
              return super().form_valid(form)
          else:
              # 正常動作ではここは通らない。エラーページへの遷移でも良い
              return redirect(reverse_lazy('base:top'))

このようにテンプレートのレンダリングを用いることでコンテキストをテンプレートに渡してメール本文を作成することができます。
[出力例]


Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject:
 =?utf-8?b?44OI44OU44OD44Kv5L2c5oiQOiDntKDmlbXjgarjg6/jg7PjgrPjga7kuJbnlYw=?=
From: hogehoge@example.com
To: admin@example.com
Date: Wed, 20 Mar 2019 08:09:26 -0000
Message-ID: <155306936665.10237.4864318700173116195@arch.localdomain>

以下のトピックが登録されました。

---------------------
タイトル: 素敵なワンコの世界
ユーザー名: 名無し
本文:
ようこそ。ワンコの世界へ
-------------------------------------------------------------------------------

EmeilMessageオブジェクトを使用してメールを送信する

冒頭で書いた通りsend_mail関数はEmailMessageのラッパーです。CCやBCCを使う等の複雑な処理はEmailMessageオブジェクトを使用します。
thread/views.py(一部抜粋)


+ from django.core.mail import send_mail, EmailMessage

  class TopicCreateView(CreateView):
      template_name = 'thread/create_topic.html'
      form_class = TopicModelForm
      model = Topic
      success_url = reverse_lazy('base:top')
  
      def form_valid(self, form):
          ctx = {'form': form}
          if self.request.POST.get('next', '') == 'confirm':
              ctx['category'] = form.cleaned_data['category']
              return render(self.request, 'thread/confirm_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'back':
              return render(self.request, 'thread/create_topic.html', ctx)
          elif self.request.POST.get('next', '') == 'create':
+             # メール送信処理
+             template = get_template('thread/mail/topic_mail.html')
+             mail_ctx={
+                 'title': form.cleaned_data['title'],
+                 'user_name': form.cleaned_data['user_name'],
+                 'message': form.cleaned_data['message'],
+             }
+             EmailMessage(
+                 subject='トピック作成: ' + form.cleaned_data['title'],
+                 body=template.render(mail_ctx),
+                 from_email='hogehoge@example.com',
+                 to=['admin@example.com'],
+                 cc=['admin2@example.com'],
+                 bcc=['admin3@example.com'],
+             ).send()
              return super().form_valid(form)
          else:
              # 正常動作ではここは通らない。エラーページへの遷移でも良い
              return redirect(reverse_lazy('base:top'))

トピックを追加してみましょう。

[出力例]


Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject:
 =?utf-8?b?44OI44OU44OD44Kv5L2c5oiQOiDntKDmmbTjgonjgZfjgY1weXRob27jga7kuJY=?=
 =?utf-8?b?55WM?=
From: hogehoge@example.com
To: admin@example.com
Cc: admin2@example.com
Date: Wed, 20 Mar 2019 08:29:19 -0000
Message-ID: <155307055904.10506.3851232136327226257@arch.localdomain>

以下のトピックが登録されました。

---------------------
タイトル: 素晴らしきpythonの世界
ユーザー名: 名無し
本文:
ようこそ。pythonの世界へ
-------------------------------------------------------------------------------

SMTPバックエンドでメールを送信する

ではSMTPバックエンドによるメール送信を見ていきたいと思います。まず、メールサーバーの設定をmysite/settings.pyに追加します。今回は擬似的にMailCatcherを用いてメールの受信を体験します。MailCatcherの設定についてはDebian 9にMailCatcherを導入するを参考にして下さい。

mysite/settings.py(一部抜粋)


- EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+ 
+ EMAL_USE_TLS = False
+ EMAIL_PORT = 1025
+ EMAIL_HOST = '192.168.0.10'
+ EMAIL_HOST_USER = ''
+ EMAIL_HOST_PASS = ''

これでトピックを追加してみます。MailCatcherにメールが送られました。

最後に

いかがだったでしょうか。SMTPバックエンドを使用すればGmail経由でもメールを送ることができ、個人のウェブサービスでもメールを手軽に使うことが出来ると思います。是非活用してみて下さい。

Sponsored Link