【初めてのDjango】(入門編 #04)お問い合わせフォームの設置、メール送信処理まで

本記事では「初めてのDjango」(入門編)として、初めてDjangoというフレームワークを使用してWebアプリケーションを作ってみたい!という方向けに、簡単なアプリケーションを作成しながら基本的な知識をご紹介していきます。

こんな人におすすめ!

・とりあえず手を動かしてWebアプリケーションを作成してみたい!
・pythonはある程度知識ある!
・お問い合わせフォームを追加したい!

本記事は連作になっております。

もし本記事のみご覧になる方は、こちらからファイル群をダウンロードすることで、実際に手を動かしながら学ぶことができるかと思います。

目次

お問い合わせページの作成

本記事ではお客様が入力・送信を行うお問い合わせフォームを実装します。データベースは使用しませんので、モデルとは連携いたしません。

Djangoにはフォームを実装するための仕組みが備わっており、簡単に実装することができます。

ルーティングの設定

まずはルーティングの設定です。

おさらい
ルーティングとは…

どのURLにアクセスしたらどのページを表示させるかの設定

今回は https://<ホスト名>/contact にアクセスしたらお問い合わせ画面が表示されるようにします。アプリケーション内の urls.py を下記のように編集します。

from django.urls import path
from . import views

app_name = 'cafe_app'
urlpatterns = [
    path('', views.IndexView.as_view(), name="index"), 
    path('menu', views.MenuView.as_view(), name="menu"), 
    path('contact', views.ContactView.as_view(), name="contact"),   # 追加
]

リンクの追加

ルーティングの次はリンクの設定の設定を行いましょう。ここは前回記事のおさらいなのでさらっといきます笑

index.htmlのナビゲーション部分を下記のように編集してください。

<nav>
    <ul>
        <li><a href="#">HOME</a></li>
        <li><a href="{% url 'cafe_app:menu' %}">MENU</a></li>
        <li><a href="{% url 'cafe_app:contact' %}">CONTACT</a></li>
    </ul>
</nav>

ビューの追加

続いて、お問い合わせ用のビューを追加していきます。views.pyに次のクラスを追加しましょう!

from .forms import ContactForm

# ... 略 ...

# 追加
class ContactView(generic.FormView):
    template_name = "contact.html"
    form_class = ContactForm

今回のフォームはデータベースを作成しないため、汎用的に使用できるFormViewクラスを継承しています(7行目)。

Djangoにおけるフォームの作成方法

ログイン画面やお問い合わせフォームなど、Djangoでフォームを作成する方法は下記のような方法があります。

  • Djangoのフォームクラスを使用(データベース不使用)
  • Djangoのフォームクラスを使用(データベース使用)
  • テンプレート(HTML)に直接<input>タグを書き込んで作る

フォームクラスを使用してフォームを作成する場合、その項目を forms.py(まだ作成していません)に定義し、それを利用して作成するのが一般的です。

なので忘れずにインポートするようにしましょう!(1行目)

さらにそのフォーム項目を使用するために form_class変数に、インポートしたクラスを格納してあげます(8行目)。

新米エンジニア

今までのgeneric.TemplateViewを継承していた時とは少し違うね!

フォームフィールドを定義

ここまででちょろっと触れましたが、お問い合わせ画面の項目をフォーム用ファイル forms.py に定義します。なので「cafe_app」ディレクトリ直下に forms.py を新規作成しましょう。

作成した forms.py を次のように編集しましょう。

from django import forms

class ContactForm(forms.Form):
    # フォーム項目のフィールドを作成
    name = forms.CharField(label='お名前', max_length=30, error_messages={'required': '必須項目です'})
    email = forms.EmailField(label='メールアドレス', error_messages={'required': '必須項目です'})
    tel = forms.CharField(label='電話番号', required=False)
    title = forms.CharField(label='タイトル', max_length=50, required=False)
    message = forms.CharField(label='メッセージ', widget=forms.Textarea, required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 属性値の追加
        self.fields['name'].widget.attrs['placeholder'] = '例)じゃんご太郎'
        self.fields['email'].widget.attrs['placeholder'] = '例)django@django.co.jp'
        self.fields['tel'].widget.attrs['placeholder'] = '例)08012345678'
        self.fields['title'].widget.attrs['placeholder'] = '例)撮影許可のお願い'
        self.fields['message'].widget.attrs['placeholder'] = 'ここに本文を入力してください'

ここで各フォーム項目の定義を行います。例えばそのフォームに入力される項目は文字列なのか、メールアドレスなのか、数字なのか、、、といったデータの種類と、その文字数の最大値は30文字で、初期値はいくつで、、、などといったオプションを定義することができます。

それぞれこのように定義できます。

データやオプションの種類などはこちらに詳しくまとめてくださっているので、余裕のある方は一読をおすすめします。

また属性値の追加も行なっております。

# 属性値の追加
self.fields['name'].widget.attrs['placeholder'] = '例)じゃんご太郎'

このように記述することで、指定したキー(上記だと「name」)の<input>タグに対する属性値を指定することができます。

例えばclass名も追加したい場合は、

# class名の追加
self.fields['name'].widget.attrs['class'] = 'inputName'

# htmlではこうなる
# <input class="inputName">

とすれば良いのです。

今回は

  • お名前
  • メールアドレス
  • 電話番号
  • タイトル
  • メッセージ内容

のフィールドを定義しました。

テンプレートを作成

お問い合わせ用のテンプレートを作成します。「cafe_app/templates」ディレクトリ直下に contact.html を新規作成しましょう。

作成した contact.html を下記のように編集します。

{% extends 'base.html' %}

{% load static %}

{% block title %}お問い合わせ{% endblock title %}

{% block contents %}
<section class="sectionContact">
    <div class="wrapperHeader">
        <h2>CONTACT</h2>
    </div>
    <div class="wrapperForm">
        <form method="post">
            <!-- CSRF対策用タグ -->
            {% csrf_token %}

            {{ form.non_field_errors }}

            <!-- formフィールドを一つずつ取り出す -->
            {% for field in form %}
            <div class="formContent">
                <p>{{ field.label_tag }}</p>
                {{ field }}
                {{ field.errors }}
            </div>
            {% endfor %}
            <button type="submit">送 信</button>
        </form>
    </div>
    <a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}

ここで重要なのは、テンプレートタグで formフィールドを一つずつ取り出している点です(20~26行目)。フィールドが取り出される順番は、forms.pyに記述したフィールドの順番に依存します。

また field.label_tag とすることで、forms.pyで定義した各フィールドのlabel引数に指定した値を取り出すことができます。

またエラーの取得について、

{{ form.non_field_errors }}

cleanメソッドなどで発生する、フィールドにまたがるエラーを取得できる。

{{ field.errors }}

フィールド単位のエラーを取得できる。

のような違いがあります。

{% csrf_token %}について

サイバー攻撃の一種であるCSRF(Cross Site Request Forgeries)対策用タグです。DjangoでPOST通信を行う際にこのタグをつけていないと403エラーが返されますので注意しましょう。Djangoの設定でこのエラーが発生しないようにすることもできますが、特に理由がない限りはそのままにしておきましょう。

お問合せページ用のCSSも追加しましょう。

mystyle.css
/* 略 */

/* お問い合わせ */

.sectionContact {
    background-color: var(--base-color);
    top: 0;
    color: var(--dark-color);
    min-height: calc(100vh - var(--footer-height));
}

.wrapperForm {
    width: fit-content;
    margin: 0 auto;
}

.formContent {
    position: relative;
    display: flex;
    flex-direction: row;
    justify-content: right;
    align-items: center;
    margin-top: 1rem;
}

.formContent p {
    position: relative;
    display: block;
    margin-right: 10px;
    padding: 5px 5px;
}

.formContent input {
    background-color: inherit;
    width: 450px;
    height: 24px;
    font-size: 16px;
    border: none;
    border-bottom: solid var(--dark-color) 1px;
}

.formContent input:focus {
    outline: none;
    border-bottom: solid var(--accent-color) 2px;
}

.formContent textarea {
    width: 450px;
    height: 280px;
    resize: none;
    background-color: inherit;
    font-size: 16px;
}

.wrapperForm button {
    background-color: var(--accent-color);
    display: block;
    padding: 10px 70px;
    border: solid var(--semi-dark-color) 1px;
    font-size: 20px;
    margin: 3rem auto 0 auto;
    box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
    transition-duration: .2s;
}

.wrapperForm button:hover {
    transform: translateY(-2px);
    opacity: .9;
    cursor: pointer;
}

編集後のお問い合わせページはこのようになります。

CSSがうまく適用されないときは、ブラウザのキャッシュを削除してから再度更新してみると直る場合があります。

この時点でのコード全体をGithubに載せております。確認用としてお使いいただければと思います。

メール送信機能の実装

現段階だと、フォームに入力して「送信」ボタンを押してもエラーが吐かれるだけです。ここからいよいよメールを実際に送信する機能を実装していきます。

今回は皆さん持っているであろうgmailを採用して送信させます。

Googleアプリパスワードの発行

送信機能実装!と息巻いていたところですが、その前にGoogleのアプリパスワードというのが必要になります。まずはこちらを各自取得しましょう(数分で取得できます)。

「Googleアプリパスワード 取得」などと検索すればたくさんヒットすると思いますが、こちらなんかが分かりやすそうなので引用させていただきます。感謝です。

settings.json

プロジェクトのルートディレクトリにある settings.json に、先ほど取得したGoogleアプリパスワードなどを追加していきます。

※【初めてのDjango】シリーズを本記事からご覧になった方は新規作成してください。

{
    "SECRET_KEY":"<シークレットキー>",
    "EMAIL_HOST":"<Googleアカウントのメールアドレス>",
    "EMAIL_HOST_PASS":"<取得したアプリパスワード>"
}

メール送信設定

続いて、プロジェクトの設定ファイル(settings.py)にてメール送信のための設定を追加します。「cafe/」ディレクトリ直下にある settings.py を下記のように編集しましょう!

# ... 略 ...

# Read settings.json
try:
    config_path = os.path.join(BASE_DIR, 'settings.json')
    config = json.load(open(config_path, "r", encoding="utf-8"))
except FileNotFoundError as e:
    print("[ERROR] Config file is not found.")
    raise e
except ValueError as e:
    print("[ERROR] Json file is invalid.")
    raise e

secret_key = config['SECRET_KEY']
email_host = config['EMAIL_HOST']
email_host_pass = config['EMAIL_HOST_PASS']

# ... 略 ...

# メールの配信先の設定
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'    # [開発時用]コンソール上に内容を表示させる
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'       # [運用時用]

# メールサーバー設定
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = email_host
EMAIL_HOST_PASSWORD = email_host_pass
EMAIL_USE_TLS = True

ハイライトしてある部分が新規追加された箇所です。

まず先ほど settings.json に追加したアプリパスワードなどを読み取っています。

secret_key = config['SECRET_KEY']
email_host = config['EMAIL_HOST']
email_host_pass = config['EMAIL_HOST_PASS']

機密情報なので一度 settings.json を経由しています。

さらにメールの配信先の設定です。

# メールの配信先の設定
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'    # [開発時用]コンソール上に内容を表示させる
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'       # [運用時用]

メール送信処理の開発時にいちいち実際に送信していては確認が面倒です。Djangoには、コンソール上でメール内容が確認できる設定が備わっております。

本番運用時にはコメントアウトを逆にしましょう!

メール送信メソッドの追加

メール送信メソッドを forms.py に追加します。forms.py を下記のように編集しましょう。

from django import forms
from django.core.mail import EmailMessage

# ... 略 ...

class ContactForm(forms.Form):
    # フォーム項目のフィールドを作成
    name = forms.CharField(label='お名前', max_length=30, error_messages={'required': '必須項目です'})
    email = forms.EmailField(label='メールアドレス', error_messages={'required': '必須項目です'})
    tel = forms.CharField(label='電話番号', required=False)
    title = forms.CharField(label='タイトル', max_length=50, required=False)
    message = forms.CharField(label='メッセージ', widget=forms.Textarea, required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 属性値の追加
        self.fields['name'].widget.attrs['placeholder'] = '例)じゃんご太郎'
        self.fields['email'].widget.attrs['placeholder'] = '例)django@django.co.jp'
        self.fields['tel'].widget.attrs['placeholder'] = '例)08012345678'
        self.fields['title'].widget.attrs['placeholder'] = '例)撮影許可のお願い'
        self.fields['message'].widget.attrs['placeholder'] = 'ここに本文を入力してください'
    
    def send_email(self):
        # ユーザー入力値の取得
        name = self.cleaned_data['name']
        email = self.cleaned_data['email']
        tel = self.cleaned_data['tel']
        title = self.cleaned_data['title']
        message = self.cleaned_data['message']

        # --- メール内容の設定 ---

        # 件名
        subject = f'[お問い合わせ]{title}'

        # 本文
        body = (
            'cefe_appにてお問い合わせがありました。\n\n' + 
            f'送信者: {name}\n' + 
            'メールアドレス:\n' + 
            f'{email}\n' + 
            f'電話番号: {tel}\n' + 
            'メッセージ\n' + 
            f'{message}'
        )

        # 送信元メールアドレス
        from_email = '<任意の送信元メールアドレス>'

        # 送信先メールアドレス
        to_list = [<任意の送信先のメールアドレスのリスト>]

        # CCリスト
        cc_list = [<CCに含めたいメールアドレスのリスト>]

        # メール送信処理
        msg = EmailMessage(subject=subject, body=body, from_email=from_email, to=to_list, cc=cc_list)
        msg.send()

メール送信クラス

今回は django.core.mail.EmailMessageクラスを使用してメール送信を行います。このクラスはBCCや添付も行うことができ、簡単に実装することができます。

from django.core.mail import EmailMessage

send_email()メソッド

具体的な送信処理は send_email()関数内に記述しています。

まずユーザーの入力値を取得しています。

name = self.cleaned_data['name']

このように self.cleaned_data[‘<フィールド名>’] とすることで、フォームバリデーションを通過したユーザーの入力値を取得することができます。

フォームバリデーションとは

フォームに入力された値が正しい書式であるか・要件を満たしているか、などのチェックのことです。

例)タイトルが50文字以内か、など

さらに、取得した入力値を使用してメールに必要な件名・本文・送信元メールアドレスなどを定義しています。開発環境では、送信元と送信先が空欄でも動作します。

最後に、ほんの2行でメール送信処理を行なっています。

msg = EmailMessage(subject=subject, body=body, from_email=from_email, to=to_list, cc=cc_list)
msg.send()

フォーム動作時の処理をビューに追加する

お問い合わせ画面用に作成したビュー(ContactView)に、メール送信に関する処理とロギングを追加します。

views.pyを下記のように編集しましょう!

import logging
from django.urls import reverse_lazy
from django.views import generic

from .forms import ContactForm

# ロガーのインスタンス化
logger = logging.getLogger(__name__)

# ... 略 ...

class ContactView(generic.FormView):
    template_name = "contact.html"
    form_class = ContactForm
    success_url = reverse_lazy('cafe_app:contact')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        return context
    
    def form_valid(self, form):
        form.send_email()
        logger.info('Contact sent by {}'.format(form.cleaned_data['name']))
        return super().form_valid(form)

ロガーのセッティング

8行目にてロガーのセッティングを行なっています。

logger = logging.getLogger(__name__)

logger.<ログレベル>(<出力内容>) と記述することで任意のログを吐き出すことができます。

リダイレクトURL

FormViewでは、クラス変数 success_url にURLを設定することで、問題なく処理が完了した時のリダイレクト先を設定することができます。これがないとエラーが発生します。

success_url = reverse_lazy('cafe_app:contact')

またURLの設定には reverse_lazy()関数を使用しています。これを用いることで、URL全体をごりごりに記述することを避けることができます。

reverse_lazy(‘<urls.pyのapp_name>:<ルーティングに付けたname>’)

リンクを貼る時の書式と似ていますね。

form_validメソッド

form_validメソッドは親クラスに定義されているメソッドで、フォームバリデーションに問題がなかった時に実行されます。これを改めて定義し処理を記述(オーバーライド)することで、独自の処理を追加することができます。

def form_valid(self, form):
    form.send_email()
    logger.info('Contact sent by {}'.format(form.cleaned_data['name']))
    return super().form_valid(form)

form.send_email() によって、先ほど forms.py に追加したsend_email()メソッドを実行しています。

メール送信してみる

ここまでお疲れ様でした!では実際にメールを送信して中身を確認してみましょう。

こんな感じでフォームに入力します。内容はなんでも良いのですが、名前とメールアドレスは必ず入力しないとエラーが発生します。

この状態で「送信」ボタンを押すと、ターミナル上にこのようなメールの内容が表示されます。

おめでとうございます!

無事にメール送信処理を実装することができました。文面は好き勝手いじっていただいて構いません。

ターミナル上でなく、実際にメールを送信したい場合は settings.py のメールバックエンド設定を変更しましょう。

# 開発時の設定
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'     # [開発時用]コンソール上に内容を表示させる
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'      # [運用時用]

# ↓↓↓こうする
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'   # [開発時用]コンソール上に内容を表示させる
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'        # [運用時用]

※2行記述してコメントアウトで対応する必要もないんですが、比較して記述した方が分かりやすいのでこのようにしています。

このように修正した後、再度フォーム入力して「送信」ボタンを押してみます。

このように実際に送信することができます。

この時点でのコード全体をGithubに載せております。確認用としてお使いいただければと思います。

まとめ

本セクションでは、お問い合わせページを新規作成し、実際にメールを送信する処理の実装まで行いました。

最初はどういう流れで処理が遷移するのかが分かりにくいかと思いますが、時間をかければ必ず理解できるようになるので根気良く頑張りましょう!

次回は、お問い合わせ送信後にメッセージを表示させる方法をご紹介します!

案件、ありますか?

「メインの仕事があるけれど、週1、2日だけできる仕事ないかな、、、」

「ある程度スキルが身に付いてはきたけど、そのスキルを活用できる場が欲しい」

なんて悩みが以前はありました。

自分で仕事を探しに行くのも大事ですが、蛇の道は蛇。その道の人に頼むことで、自分だけでは見つからないような案件に携わることができます。

IT PRO パートナーズでは、簡単に無料でアカウントを登録でき、さらにはエージェントさんに希望の働き方・案件の種類を提示することでお仕事を紹介してくれます!

登録自体も非常に簡単で、「エージェントさんとの面談を希望する」という欄にチェックをするだけで、エージェントさんから直接連絡をいただくことができます。

驚くほど簡単で正直拍子抜けしてしまいました笑

もしお仕事探しに困っておりましたら、一度登録し案件を眺めてみることをおすすめします!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

勤めていた設計会社を退社し、フリーランスとして活動しています
また、趣味で主にpyhonを用いて機械学習を行なっています!
現在競艇の予測モデルの開発中です。

コメント

コメント一覧 (11件)

コメントする

目次