Nerds N' Computers

ソフトウェア技術や思ったことについて書きます.

ソフトウェア技術や思ったことについていろいろと書きます.

Ajax+Flaskを用いたコンタクトフォーム作成

経緯

ウェブ制作の一環として、コンタクトフォームを作ることになったのですが、既にある物を使うくらいなら勉強にもなるし、1から作ろうと考え他のがきっかけです。 実際、phpなどを使う方法もあるのですが、pythonに慣れていたため、flaskを使おうと思いました。 

アーキテクチャ

Untitled Diagram.png

参考にしたサイト

CSSフレームワーク: - Bulma

Flaskでは: - Jinja2の使い方がわかるとFlaskを用いた開発がよりスマートになる - Flask-Mail documentation - Attaching content and links to messages (Slack)

HTMLの中身

HTMLで必要になってくるのはformタグ内のmethodactionの指定であり、コンタクトフォームであるので下記のように記しておくべきである。

<form id="contact-form" method="POST" action="/127.0.0.1:5000">

今回はFlaskを使用しているため、actionの指定先はFlaskを立ち上げるサーバになる。 したがって、htmlの中身は下記のようになる

<form id="contact-form" method="POST" action="/127.0.0.1:5000">
    <div>
        <div class="field">
            <label class="label">Name</label>
            <div class="control">
                <input name="name" class="input" type="text" placeholder="Name" required>
            </div>
        </div>
        <div class="field">
            <label class="label">Email</label>
            <div class="control">
            <input id="email" name="email" class="input" type="email" placeholder="Email" required>
            </p>
            <p id="email-error" class="help is-danger email-success">
                This email is invalid
            </p>
        </div>
        <div class="field">
            <label class="label">Message</label>
            <div class="control">
                <textarea name="message" class="textarea" placeholder="Textarea" required></textarea>
            </div>
        </div>
        <div>
            <div class="control">
                <button id="submit" class="button is-link">Submit</button>
            </div>
            <div class="control">
                <button id="cancel" class="button is-text">Cancel</button>
            </div>
        </div>
    </div>
</form>
<div id="result"></div>

簡略化のために氏名・メールアドレス・メッセージだけにした。ここで重要になるのが、inputtextareabuttonであるので、他の要素はとりあえずは無視して良い。

jsの中身

まず、フォームの中身をオブジェクト型で取ってくる

var form = document.forms.contactForm;

これを使用して、もしsubmitボタンが押された時、それらをajaxで処理する。

ajaxの部分について

初めに、デフォルトではsubmitをした時にデフォルトでPOSTするようになっているのでこれをしないように明言する必要がある。

$(form).submit(function(e) {
   // prevent it from POSTing
   e.preventDefault();
   
   //ここに必要なコードを記述していく
}

では実際に内容を書いていきます。 ajaxdataは正規化されていないといけないので、下記のように正規化する。

var formData = $(form).serialize();

そしてこれを用いて、ajaxに渡す内容は下記のようになる

$.ajax({
  type: 'POST',
  url: 'http://127.0.0.1:5000',
  data: formData
})

urlはflaskで指定する番号になるため、とりあえずはこのままにしておく。実際にサーバがあるのならそれをここに記述する。

成功した時

.done(function(response) {
    // ここに成功時の動作を記述する
})

失敗時

.fail(function(response) {
    // ここに失敗時の動作を記述する
});

jsのまとめ

したがって、以上のコードをまとめると下記のようになる

// Object of the form
var form = document.forms.contactForm;

/*
 * This function receives the form and sends it
 * using ajax.
 */
function sendForm(e) {
  // When 'submit' was clicked
  $(form).submit(function(e) {
    // prevent it from POSTing
    e.preventDefault();

    var formData = $(form).serialize();

    $.ajax({
      type: 'POST',
      url: 'http://127.0.0.1:5000',
      data: formData
    })
    // If succeeded, show a popup message
    .done(function(response) {
      // reset form inputs
      $('form :input').val('');
    })
    .fail(function(response) {
      // 失敗時の記述
    });
  });
};

form.name.addEventListener('change', sendForm, false);

となる。私は成功時は更にポップアップメッセージを追加し、失敗時はそのエラーを吐くようにしました。 色々工夫の方法はあると思うので、自由に書いてください。

Flaskの中身

pythonコードのでは実際にslackやメールを送信するという動作を行います。
メールは flask_mail を使用すると実装ができ、slackは slackweb というパッケージを使用すると実装ができます。

flask_mail

上記でも参考サイトであげさせていただいたFlask-Mail documentationを参考に送信するメールのコードを書く。 単純にメールを送信するコードは下記のようになる:

from flask_mail import Message

@app.route("/")
def send():

    msg = Message('Hello',
                  sender='from@example.com',
                  recipients=['to_1@example.com', 'to_2@example.com'])

しかし、これでは同期処理となり、時間がかかるため、非同期処理にメールを送信する方法がある。 これを行うには、 threadingパッケージの Threadメソッドを用いる。

from threading import Thread

from flask_mail import Message, Mail


def send_async_mail(app, msg):
    """
    Send mail asynchronously
    """
    mail = Mail(app)
    with app.app_context():
        mail.send(msg)

def send_mail(email, app, html, sender, mail_title):
    """
    Send mail to receiver
    """
    msg = Message(
            mail_title,
            sender=('name', 'sender@example.com'),
            recipients=[email],
            bcc=[sender],
            )
    msg.html = html
    thr = Thread(target=self.send_async_mail, args=[app, msg])
    thr.start()

    return True

とすれば、メールを非同期処理で送信することができます!

ここで、メールをhtmlでフォーマットしたい場合、Flaskはjinja2との相性が良いのでFlask-Mail documentationを参考にすると綺麗に作成できると思います。

Slack

今回、Slackには簡単にコンタクトフォームの内容を送信してみることにした。こちらはSlackが提供しているAPIですぐ実装ができます。 その前に、Slackにメッセージが送れるようにWebhook URLの発行が必要で、下記のサイトを参考にすると簡単にできます。 SlackのWebhook URL取得手順

URLを取得できればあとは簡単です。 Attaching content and links to messages (Slack)を参考に、好きな形でメッセージを送信します。 まず、メッセージの作成です。

def create_message():
        attachments = []
        attachment = {
                'fallbacks': 'New Contact Has Arrived!',
                'title': "New Contact Has Arrived!",
                'color': '#anycolor',
                'fields': [
                    {
                        'title': 'Date',
                        'value': date,
                        'short': True
                        },
                    {
                        'title': 'Name',
                        'value': name,
                        'short': True
                        },
                    {
                       # Any other fields
                        },
                    ]
                }
        attachments.append(attachment)

        return attachments

これでメッセージの作成ができました。あとは先ほど取得したwebhook urlを用いてメッセージの送信を行います。

import slackweb

slack = slackweb.Slack(url=WEBHOOK_URL)


def send_to_slack(slack):
        slack.notify(attachments=format_attachment())


if __name__ == '__main__':
        send_to_slack(slack)

以上で、メールとslackにメッセージを送信することができました!

最後に

コンタクトフォームを作る方法は色々あると思いますが、flaskを用いると結構簡単に作れてしまうことがわかりました。実際、色々な機能を追加するとまた違いますが、、、