ホーム >  Python > Django >  Djangoのお問い合わせフォームを作る。contextによるデータの受け渡し


Posted:2018/05/07 9:00:31 AM|Category : Django

Djangoのお問い合わせフォームを作る。contextによるデータの受け渡し

よくホームページなどで見かけるお問い合わせフォームをDjangoサイトに作成します。「お問い合わせフォーム」→「お問い合わせ内容確認」→「お問い合わせ内容をメール送信」の流れを作ります。

お問い合わせフォーム

前回、パスワードリマインダ機能を作成しました。今回は、ユーザがサイト管理者にお問い合わせしたい場合に備え、お問い合わせページを作成します。また、送信前に一度入力内容を確認するためにお問い合わせ内容確認ページを作ります。このとき、contextによるページ間のデータ受け渡し処理を行います。これらを実現するため、セッション汎用ビューを利用します。

手順

  • お問い合わせページにメッセージを入力して確認を押します。
  • お問い合わせ内容確認ページに移動します。
  • お問い合わせ内容確認ページで送信をクリックすると、メッセージが送信されてトップページへ戻ります。
  • お問い合わせ内容確認ページで戻るをクリックすると、お問い合わせページへ戻ります。このとき、入力メッセージは保持します。

URL設計

トップページ(/index) ドメイン直下のページです。ログイン前はページタイトルのみですが、ログインすると、ヘッダーナビゲーションにログアウトのリンクを表示します。
お問い合わせフォーム(/contact) お問い合わせフォームを表示します。
お問い合わせフォーム確認(/contact_confirm) ユーザがお問い合わせフォームに入力した内容を確認するためのページです。戻るボタンを押すと、入力内容を保持します。
お問い合わせ送信(/contact_send) お問い合わせフォームの入力内容をメールとして送信します。ページは表示しませんが、送信が完了するとトップページにリダイレクトします。

前提

  • 既にDjangoのプロジェクトを作成済み。
  • gmailを利用済み。(GmailのSMTPでも可。)

仕様

①トップページです。「ドメイン/」にアクセスします。画面右上にお問い合わせフォーム(contact)へのリンクがあります。

お問い合わせフォーム画面です。「ドメイン/contact」にアクセスします。

お問い合わせフォーム確認画面です。「ドメイン/contact_confirm」にアクセスします。

お問い合わせフォーム確認画面で送信を押すと、サイト管理者宛にメッセージが送信されます。

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject: =?utf-8?b?44GC772U44KJ772U772H44KJ?=
From: お問い合わせ利用者のメールアドレス
To: サイト管理者
Date: Mon, 30 Apr 2018 02:39:21 -0000
Message-ID: <xxxxxxxxx.xxxxxxxxxxxxxxxxxxx@localhost.localdomain>

メッセージ送信テストを実行します。

トップページへリダイレクトします。

準備

Vagrantでゲスト環境(仮想環境)を作ります。

①Userモデルをカスタマイズしています。

DjangoのUserモデルカスタマイズ(UUIDを使う)

②ユーザ登録機能を作ります。

Djangoのログイン・ユーザ登録(カスタムユーザモデル)

③仮登録した後、メールから本登録する機能を作ります。

仮登録した後、メールから本登録させる(Django)

④パスワードリマインダ機能を作ります。

パスワードリマインダ機能を作る(Django)

環境

OS CentOS 7.1.1503
pyenv 1.1.3-5-g7dae197
Anaconda 3-4.3.0
MariaDB 5.5.52-1.el7
Apache 2.4.6
mod_wsgi 4.5.14
Django 1.11.3

お問い合わせフォーム概要

Userモデルの継承してカスタマイズします。usernameの代わりにemailを使うようにしています。

①以下のようなDjangoアプリ構成を作ります。プロジェクト名が「pj1」で、アプリケーション名が「users」です。プロジェクトの設定は済ませたものとします。赤字のファイルは、今回変更する部分です。

  • pj1/
  • pj1/
  • __init__.py
  • settings.py
  • urls.py
  • wsgi.py
  • users/
  • __init__.py
  • admin.py
  • apps.py
  • urls.py
  • views.py
  • models.py
  • token_manager.py
  • templates/
  • base.html
  • contact.html
  • contact_confirm.html
  • index.html
  • login.html
  • regist.html
  • mailtemplate/password_reset/
  • message.txt
  • subject.txt
  • manage.py

settings.py

pj1ディレクトリ下のsettings.pyを変更します。ログイン関連のURLや、Gmailで送信する設定をしています。

LOGIN_URL = "users:login"  # ログインするページ。デフォルトにするなら"/admin/login/"等も
LOGIN_REDIRECT_URL = 'users:index'  # ログインページに直接飛んだとき、ログイン完了後のリダイレクト先

# メールを実際に送らず、コンソール画面へ表示する
#EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'gmailアカウント名'
EMAIL_HOST_PASSWORD = 'gmailパスワード'
EMAIL_USE_TLS = True

models.py

①特に変更はありません。内容は前回と同じです。

DjangoのUserモデルカスタマイズ(UUIDを使う)

urls.py

①pj1ディレクトリのurls.pyを以下のように編集します。

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url('admin/', admin.site.urls),
    url(r'^', include('users.urls', namespace = 'users'))
]

②usersディレクトリのurls.pyを以下のように編集します。

from django.conf.urls import include, url
from django.contrib.auth import views as auth_views
from . import views
 
app_name = 'users'
 
urlpatterns = [
    url(r'^$', views.index, name='index'),

    # ログイン、ログアウト
    url(r'^login/$', views.login, name='login'),
    url(r'^logout/$', views.logout, name='logout'),
    url(r'^regist/$', views.regist, name='regist'),
    url(r'^regist_save/$', views.regist_save, name='regist_save'),

〜省略〜

    # お問い合わせ
    url(r'^contact/$', views.contact.as_view(), name='contact'),
    url(r'^contact_confirm/$', views.contact_confirm.as_view(), name='contact_confirm'),
    url(r'^contact_send/$', views.contact_send.as_view(), name='contact_send'),

]

forms.py

①usersディレクトリ配下のforms.pyを編集します。以下の内容を追記します。

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.forms import AuthenticationForm
from users.models import  User
from django.contrib.auth.forms import PasswordChangeForm, PasswordResetForm, SetPasswordForm
from django.core.exceptions import ValidationError
from django.core.validators import validate_email

〜省略〜

class ContactForm(forms.Form):
    name = forms.CharField(max_length=20) # 名前
    email = forms.CharField(max_length=40)
    message = forms.CharField(widget=forms.Textarea, max_length=100) #問い合わせ内容

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs['class'] = 'form-control'
        self.fields['name'].widget.attrs['placeholder'] = 'お名前'
        self.fields['name'].widget.attrs['maxlength'] = '20'
        self.fields['email'].widget.attrs['class'] = 'form-control'
        self.fields['email'].widget.attrs['placeholder'] = 'メールアドレス'
        self.fields['name'].widget.attrs['maxlength'] = '40'
        self.fields['message'].widget.attrs['class'] = 'form-control'
        self.fields['message'].widget.attrs['placeholder'] = 'メッセージ'
        self.fields['message'].widget.attrs['maxlength'] = '200'
        self.fields['message'].widget.attrs['rows'] = '6'

    def clean_email(self):
        email = self.cleaned_data["email"]
        try:
            validate_email(email)
        except ValidationError:
            raise ValidationError("正しいメールアドレスを指定して下さい。")

解説

    name = forms.CharField(max_length=20) # 名前
    email = forms.CharField(max_length=40) # 名前
    message = forms.CharField(widget=forms.Textarea, max_length=100) #問い合わせ内容

入力フォームを定義します。「widget=forms.Textarea」は、inputタグを複数行入力に設定します。

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs['class'] = 'form-control'
        self.fields['name'].widget.attrs['placeholder'] = 'お名前'
        self.fields['name'].widget.attrs['maxlength'] = '20'
        self.fields['email'].widget.attrs['class'] = 'form-control'
        self.fields['email'].widget.attrs['placeholder'] = 'メールアドレス'
        self.fields['name'].widget.attrs['maxlength'] = '40'
        self.fields['message'].widget.attrs['class'] = 'form-control'
        self.fields['message'].widget.attrs['placeholder'] = 'メッセージ'
        self.fields['message'].widget.attrs['maxlength'] = '200'
        self.fields['message'].widget.attrs['rows'] = '6'

入力フォームの初期値を設定します。「class」はinputタグのclassを、「placeholder」は入力フォーム内に表示する文字を、「maxlength」は入力可能な文字数を設定します。「rows」は、「Textarea」の行数です。

    def clean_email(self):
        email = self.cleaned_data["email"]
        try:
            validate_email(email)
        except ValidationError:
            raise ValidationError("正しいメールアドレスを指定して下さい。")

フォームに入力されたEmailのバリデーション(入力確認)を行います。のフォームから送信(POST)したデータは、ContactFormのcleaned_dataに格納されます。その情報をもとにvalidate_emailでメールアドレスのフォーマットとして正しいか確認します。

views.py

①usersディレクトリのviews.pyを以下のように編集します。

from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from .forms import  ContactForm
from django.contrib.auth.models import User
from django.contrib.auth import views as auth_views
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.contrib.auth import login as auth_login
from users.backends import EmailModelBackend

User = get_user_model()


def index(request):
  context = {
    'users':request.user,
  }
  return render(request, 'index.html', context)


〜省略〜


class contact(generic.FormView):
    """お問い合わせフォームページ"""

    template_name = 'contact.html'
    form_class = ContactForm
    success_url = reverse_lazy('users:contact_confirm')

    def get_form(self, form_class=None):
        # contact.hmltで、データを送信した場合
        if 'name' in self.request.POST:
            form_data = self.request.POST
 
        # お問い合わせフォーム確認画面から「戻る」リンクを押した場合や
        # 初回の入力欄表示は以下の表示。
        # セッションにユーザーデータがあれば、それをフォームに束縛
        else:
            form_data = self.request.session.get('form_data', None)
 
        return self.form_class(form_data)
 
    def form_valid(self, form):
        # 入力した値を、セッションに保存
        self.request.session['form_data'] = self.request.POST
        return super().form_valid(form)


class contact_confirm(generic.TemplateView):
    """お問い合わせフォーム確認ページ"""

    template_name = 'contact_confirm.html'
 
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        form_data = self.request.session.get('form_data', None)
        context['form'] = ContactForm(form_data)
        return context


class contact_send(generic.FormView):
    """お問い合わせ送信"""

    form_class = ContactForm
    success_url = reverse_lazy('users:index')
 
    def get_form(self, form_class=None):
        # popで、セッションに入れたユーザーデータ自体取り出し
        form_data = self.request.session.pop('form_data', None)

        #メール送信
        subject = form_data['name']
        message = form_data['message']
        from_email = form_data['email']
        to = [settings.EMAIL_HOST_USER]
        send_mail(subject, message, from_email, to)

        return self.form_class(form_data)

解説

class contact(generic.FormView):
    """お問い合わせフォームページ"""

    template_name = 'contact.html'

汎用ビューの「FormView」を使用します。お問い合わせフォームのhtmlテンプレートを指定します。

    form_class = ContactForm

お問い合わせフォームクラスを指定します。

    success_url = reverse_lazy('users:contact_confirm')

post後のリダイレクト先を指定します。

    def get_form(self, form_class=None):
        # contact.hmltで、データを送信した場合
        if 'name' in self.request.POST:
            form_data = self.request.POST
  
        # お問い合わせフォーム確認画面から「戻る」リンクを押した場合や
        # 初回の入力欄表示は以下の表示。
        # セッションにユーザーデータがあれば、それをフォームに束縛
        else:
            form_data = self.request.session.get('form_data', None)

        return self.form_class(form_data)

「確認画面へ」ボタンを押したら、form_data変数にPOST内容を格納します。初めて表示・お問い合わせ確認画面から「戻る」リンクでリダイレクトしてきた場合は、セッションの内容をform_dataに代入します。

    def form_valid(self, form):
        # 入力した値を、セッションに保存
        self.request.session['form_data'] = self.request.POST
        return super().form_valid(form)

ユーザが入力したデータの検証に成功したら、セッションにその内容を格納します。

class contact_confirm(generic.TemplateView):
    """お問い合わせフォーム確認ページ"""
 
    template_name = 'contact_confirm.html'
  
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        form_data = self.request.session.get('form_data', None)
        context['form'] = ContactForm(form_data)
        return context

お問い合わせフォーム確認ページを表示します。「self.request.session.get()」でセッションを取得します。

class contact_send(generic.FormView):
    """お問い合わせ送信"""
 
    form_class = ContactForm
    success_url = reverse_lazy('users:index')

お問い合わせフォームの内容を送信し、トップページ(index)へリダイレクトするだけのクラスです。URLは存在しますがページは表示しません。

    def get_form(self, form_class=None):
        # popで、セッションに入れたユーザーデータ自体取り出し
        form_data = self.request.session.pop('form_data', None)

        #メール送信
        subject = form_data['name']
        message = form_data['message']
        from_email = form_data['email']
        to = [settings.EMAIL_HOST_USER]
        send_mail(subject, message, from_email, to)

        return self.form_class(form_data)

セッションをpopで取り出し、その情報を元にメール文を作成します。send_mail()でメールを送信します。

テンプレート

「Base.html」、「index.html」、は、前回の通りです。

{% extends 'base.html' %}
{% block content %}
<div class="container-fluid" style="margin-top: 30px;">
  <div class="row">
    <div class="col-md-6 offset-md-3">
      <div class="card">
        <div class="card-header">お問い合わせ</div>
        <div class="card-body">
          <div class="card-block"> 
            <form class="my-form" action="" method="POST">
              <div class="row">
                <div class="form-group col-md-11">
                  <label for="id_email">お名前</label>
                  {{ form.name }}
                </div>
                <div class="form-group col-md-11 pb-0">
                  <label for="id_email">メールアドレス</label>
                  {{ form.email }}
                </div>
                <div class="form-group col-md-11" style="height: 1em;">
                  <ul class="errorlist m-0 p-0">
                  <li>
                  {% if form.email.errors %}
                      {% for error in form.email.errors %}
                          {{ error|escape }}
                      {% endfor %}
                  {% endif %}
                  </li>
                  </ul>
                </div>
                <div class="form-group col-md-11">
                  <label for="id_email">メッセージ</label>
                  {{ form.message }}
                </div>
                <div class="form-group col-md-11">
                  {% csrf_token %}
                  <button type="submit" class="btn btn-block btn-outline-primary">送信</button>
                </div>
              </div>
            </form>
          </div>
        </div><!-- end card-body-->
      </div><!-- end card-->
    </div>
  </div><!-- end row -->
</div><!-- end container-fluid-->
{% endblock %}

「contact.html」は、お問い合わせフォーム画面を定義します。

{% extends 'base.html' %}
{% block content %}
<div class="container-fluid" style="margin-top: 30px;">
  <div class="row">
    <div class="col-md-6 offset-md-3">
      <div class="card">
        <div class="card-header">お問い合わせ内容確認</div>
        <div class="card-body">
          <div class="card-block">
            <h6 class="card-title">お名前</h6>
            <p class="card-text" style="word-wrap: break-word;">{{ form.name.value }}</p>
            <h6 class="card-title">メールアドレス</h6>
            <p class="card-text" style="word-wrap: break-word;">{{ form.email.value }}</p>
            <h6 class="card-title">メッセージ</h6>
            <p class="card-text pb-3" style="word-wrap: break-word;">{{ form.message.value }}</p>
            <form class="my-form" action="{% url 'users:contact_send' %}" method="POST">
              <div class="row">
                <div class="form-group col-md-11">
                  <div style="display:none">
                    {{ form }}
                    {% csrf_token %}
                  </div>
                  <button type="submit" class="btn btn-block btn-outline-primary">送信</button>
                </div>
              </div>
              <div class="row">
                <div class="form-group col-md-11">
                  <div style="display:none">
                    {{ form }}
                    {% csrf_token %}
                  </div>
                  <a href="{% url 'users:contact' %}" class="btn btn-block btn-outline-primary">戻る</a>
                </div>
              </div>
            </form>
          </div>
        </div><!-- end card-body-->
      </div><!-- end card-->
    </div>
  </div><!-- end row -->
</div><!-- end container-fluid-->
{% endblock %}

「contact_confirm.html」は、お問い合わせフォーム確認画面を定義します。


トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告