投稿日: | 最終更新日:
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のログイン・ユーザ登録(カスタムユーザモデル)
③仮登録した後、メールから本登録する機能を作ります。
④パスワードリマインダ機能を作ります。
環境
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」は、お問い合わせフォーム確認画面を定義します。
- Python 114
- 制作 54
- RaspberryPi 41
- Django 40
- WordPress 40
- Linux 27
- VPS 22
- JavaScript 21
- PHP 20
- HTML・CSS 19
- AWS 16
- 仮想環境 15
- レスポンシブデザイン 13
- マイコン 11
- WEB全般 11
- 動画製作 9
- Webサービス 8
- 統合開発環境 8
- 機械学習 8
- PyCharm 7
- jQuery 7
- AfterEffects 7
- 起業・設立 7
- Django REST framework 6
- C# 6
- デザイン 6
- SEO 6
- pydata 6
- Visual Studio 5
- 数学 5
- 携帯サイト 5
- heroku 5
- Mac 5
- illustrator 5
- node.js 5
- Anaconda 5
- Nginx 4
- Jupyter Notebook 4
- インフラ 4
- Google Colaboratory 4
- symfony 4
- Webスクレイピング 3
- photoshop 3
- Go言語 3
- PC 3
- ツール 3
- Docker 3
- facebook 3
- 作業効率化 3
- データベース 3
- Cloud9 3
- コマンド 2
- micro:bit 2
- Kali Linux 2
- Webサーバー 2
- MariaDB 2
- ドローン 2
- コンテナ 2
- DaVinci Resolve 2
- ネットワーク 2
- Java 2
- movie 2
- PCDJ 2
- 音楽 2
- XSERVER 2
- Ansible 1
- Vue.js 1
- JSON 1
- Bootstrap 1
- バージョン管理システム 1
- SSL 1
- S3 1
- ムームードメイン 1
- ネットワーク 1
- アニメーション 1
- D3.js 1
- Rhino 1
- アニメ 1
- git 1
- windows 1
- アクセス解析 1
- スマートフォン 1
- アフィリエイトノウハウ 1
- 知識 1
- TypeScript 1
- 役立つ本・書籍 1
- データサイエンス 1
- ESP32 1
- AI 1
- ownCloud 1
- API 1