【Django】お問い合わせフォームを作成する|簡単な掲示板アプリの作成

Djangoでお問い合わせフォームを作成する方法を解説します。フォームで受け取ったデータをデータベースに保存し、その内容を自動でメール送信する機能も実装します。モデル、フォーム、ビュー、URL設定、テンプレート作成までの一連の流れを実践的に学べます。

作成日: 更新日:

開発環境

  • OS: Windows10
  • Visual Studio Code: 1.73.0
  • Python: 3.10.11
  • Django: 5.0.3

サンプルコード

/app/forms.py

/app/forms.py
1 # app/forms.py
2 from django import forms
3-from .models import Board, Comment, Favorite
4+from .models import Board, Comment, Favorite, Contact
5 from django.contrib.auth.forms import UserCreationForm
6 from django.contrib.auth.models import User
7 
8 class FavoriteForm(forms.ModelForm):
9     class Meta:
10         model = Favorite
11-        fields = ['board']
12+        fields = ['board']
13+
14+class ContactForm(forms.ModelForm):
15+    class Meta:
16+        model = Contact
17+        fields = ['title', 'message', 'email']
18

/app/models.py

/app/models.py
1     updated_at = models.DateTimeField(auto_now=True)
2 
3     class Meta:
4-        unique_together = ('user', 'board')
5+        unique_together = ('user', 'board')
6+
7+class Contact(models.Model):
8+    title = models.CharField(max_length=100)
9+    message = models.TextField()
10+    email = models.EmailField()
11+    created_at = models.DateTimeField(auto_now_add=True)
12+
13+    def __str__(self):
14+        return self.title
15

/app/urls.py

/app/urls.py
1     path('sort/', views.board_sort, name='sort'),
2     path('add_favorite/', views.add_favorite, name='add_favorite'),
3     path('remove_favorite/', views.remove_favorite, name='remove_favorite'),
4+    path('contact/', views.contact, name='contact'),
5+    path('contact/success/', views.contact_success, name='contact_success'),
6 ]
7

/app/views.py

/app/views.py
1 # app/views.py
2 from django.shortcuts import render, redirect, get_object_or_404
3 from .models import Board, Comment, Favorite
4-from .forms import BoardForm, SignUpForm, CommentForm, FavoriteForm
5+from .forms import BoardForm, SignUpForm, CommentForm, FavoriteForm, ContactForm
6 from django.contrib.auth import logout
7 from django.contrib.auth.views import LoginView
8 from django.contrib.auth.decorators import login_required
9 from django.http import JsonResponse
10 from django.db.models import Count
11 from django.db import models
12+from django.core.mail import send_mail
13+from django.conf import settings
14 
15 def user_owns_board(view_func):
16     @wraps(view_func)
17         return redirect('index')
18     return redirect('index')
19 
20+def contact(request):
21+    if request.method == 'POST':
22+        form = ContactForm(request.POST)
23+        if form.is_valid():
24+            contact = form.save()
25+
26+            # ユーザーへのメール
27+            user_subject = 'お問い合わせを受け付けました'
28+            user_message = 'お問い合わせ内容:\n\n{}'.format(contact.message)
29+            send_mail(user_subject, user_message, settings.EMAIL_HOST_USER, [contact.email])
30+
31+            # 運営者へのメール
32+            admin_subject = '新しいお問い合わせがありました'
33+            admin_message = 'お問い合わせ内容:\n\n{}'.format(contact.message)
34+            send_mail(admin_subject, admin_message, settings.EMAIL_HOST_USER, [settings.EMAIL_HOST_USER])
35+
36+            return redirect('contact_success')
37+    else:
38+        form = ContactForm()
39+    return render(request, 'contact.html', {'form': form})
40+
41+def contact_success(request):
42+    return render(request, 'contact_success.html')
43+
44 # ログインページのビュー
45 class CustomLoginView(LoginView):
46     template_name = 'registration/login.html'
47

/config/settings.py

/config/settings.py
1 # SECURITY WARNING: don't run with debug turned on in production!
2 DEBUG = True
3 
4-ALLOWED_HOSTS = []
5+ALLOWED_HOSTS = ['localhost', '127.0.0.1']
6 
7 
8 # Application definition
9 # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
10 
11 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
12+
13+# Emailの設定
14+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
15+EMAIL_HOST = 'smtp.gmail.com'
16+EMAIL_PORT = 587
17+EMAIL_USE_TLS = True
18+EMAIL_HOST_USER = 'engr.sng@gmail.com'
19+EMAIL_HOST_PASSWORD = 'vjostbnxevshsajl'
20

/templates/base.html

/templates/base.html
1                             {% csrf_token %}
2                             <button type="submit" class="btn btn-light">ログアウト</button>
3                         </form>
4+                        <a href="{% url 'contact' %}" class="btn btn-light">お問い合わせ</a>
5                     </div>
6-                    {% else %}
7+                {% else %}
8                     <!-- ログインしていない場合 -->
9                     <div>
10                         <a href="{% url 'signup' %}" class="btn btn-light">サインアップ</a>
11                         <a href="{% url 'login' %}" class="btn btn-light">ログイン</a>
12+                        <a href="{% url 'contact' %}" class="btn btn-light">お問い合わせ</a>
13                     </div>
14                 {% endif %}
15                 </div>
16

/templates/contact.html

/templates/contact.html
1+{% extends 'base.html' %}
2+
3+{% block title %}お問い合わせ{% endblock %}
4+
5+{% block content %}
6+    <section class="container">
7+        <div class="row justify-content-center">
8+            <div class="col-md-6">
9+                <h2 class="mb-4">お問い合わせ</h2>
10+                <form method="POST">
11+                    {% csrf_token %}
12+                    <div class="mb-3 row">
13+                        <label for="id_email">メールアドレス</label>
14+                        <div class="col-sm-8">
15+                            {{ form.email }}
16+                        </div>
17+                    </div>
18+                    <div class="mb-3 row">
19+                        <label for="id_title">件名</label>
20+                        <div class="col-sm-8">
21+                            {{ form.title }}
22+                        </div>
23+                    </div>
24+                    <div class="mb-3 row">
25+                        <label for="id_message">内容</label>
26+                        <div class="col-sm-8">
27+                            {{ form.message }}
28+                        </div>
29+                    </div>
30+                    <div class="mb-3 row">
31+                        <div class="col-sm-8 offset-sm-4">
32+                            <button type="submit" class="btn btn-primary">送信</button>
33+                        </div>
34+                    </div>
35+                </form>
36+            </div>
37+        </div>
38+    </section>
39+{% endblock %}
40

/templates/contact_success.html

/templates/contact_success.html
1+{% extends 'base.html' %}
2+
3+{% block title %}お問い合わせ{% endblock %}
4+
5+{% block content %}
6+<section class="container">
7+    <p>お問い合わせありがとうございます。</p>
8+    <p>後日、担当者よりご連絡させていただきますので、しばらくお待ちください。</p>
9+    <a href="{% url 'index' %}" class="btn btn-primary">ホームに戻る</a>
10+</section>
11+{% endblock %}
12

コード解説

変更点: お問い合わせ内容を保存するモデルの追加

/app/models.py
1+class Contact(models.Model):
2+    title = models.CharField(max_length=100)
3+    message = models.TextField()
4+    email = models.EmailField()
5+    created_at = models.DateTimeField(auto_now_add=True)
6+
7+    def __str__(self):
8+        return self.title

お問い合わせフォームから送信されたデータをデータベースに保存するための設計図として、Contactモデルを新しく作成しました。モデルは、データベースのテーブル(表)に相当します。

  • title = models.CharField(max_length=100): お問い合わせの「件名」を保存するフィールドです。最大100文字の短いテキストを保存できます。
  • message = models.TextField(): お問い合わせの「内容」を保存するフィールドです。文字数制限のない長いテキストを保存できます。
  • email = models.EmailField(): 「メールアドレス」を保存するフィールドです。メールアドレス形式の文字列が入力されているか自動でチェックしてくれます。
  • created_at = models.DateTimeField(auto_now_add=True): データが作成された日時を自動で記録するフィールドです。auto_now_add=True を指定することで、データが最初に保存される時に現在の日時が自動的に設定されます。
  • def __str__(self):: このモデルのデータを管理画面などで表示する際に、どのように表示するかを決めるためのものです。ここでは、お問い合わせの件名(title)が表示されるようになります。

変更点: お問い合わせフォームの定義を追加

/app/forms.py
1 # app/forms.py
2 from django import forms
3-from .models import Board, Comment, Favorite
4+from .models import Board, Comment, Favorite, Contact
5 
6+class ContactForm(forms.ModelForm):
7+    class Meta:
8+        model = Contact
9+        fields = ['title', 'message', 'email']

ユーザーがブラウザで入力するお問い合わせフォームを定義するために、ContactFormを新しく作成しました。

forms.ModelFormを継承することで、先ほど作成したContactモデルと連携したフォームを簡単に作ることができます。

  • class Meta:: この中にフォームの設計情報を記述します。
  • model = Contact: このフォームが、Contactモデルと連携することを示します。
  • fields = ['title', 'message', 'email']: Contactモデルの中から、フォームとして画面に表示したいフィールドを指定します。これにより、「件名」「内容」「メールアドレス」の3つの入力欄が自動的に生成されます。

変更点: お問い合わせページのURL設定を追加

/app/urls.py
1+    path('contact/', views.contact, name='contact'),
2+    path('contact/success/', views.contact_success, name='contact_success'),

ユーザーがウェブサイトの特定のURLにアクセスしたときに、どのプログラム(ビュー関数)を呼び出すかを設定するファイルに、お問い合わせ関連のURLを追加しました。

  • path('contact/', views.contact, name='contact'):
    • ユーザーが /contact/ というURLにアクセスすると、views.pyに定義されているcontactという名前の関数が実行されるようになります。
    • name='contact'は、このURL設定に「contact」という名前を付けています。これにより、HTMLテンプレート側でURLを直接書かずに、この名前を使って簡単にリンクを生成できます。
  • path('contact/success/', views.contact_success, name='contact_success'):
    • ユーザーが /contact/success/ というURLにアクセスすると、views.pycontact_success関数が実行されます。これは、お問い合わせ送信後の完了ページを表示するための設定です。

変更点: お問い合わせ処理を行うビューを追加

/app/views.py
1+from .forms import BoardForm, SignUpForm, CommentForm, FavoriteForm, ContactForm
2+from django.core.mail import send_mail
3+from django.conf import settings
4+
5+def contact(request):
6+    if request.method == 'POST':
7+        form = ContactForm(request.POST)
8+        if form.is_valid():
9+            contact = form.save()
10+
11+            # ユーザーへのメール
12+            user_subject = 'お問い合わせを受け付けました'
13+            user_message = 'お問い合わせ内容:\n\n{}'.format(contact.message)
14+            send_mail(user_subject, user_message, settings.EMAIL_HOST_USER, [contact.email])
15+
16+            # 運営者へのメール
17+            admin_subject = '新しいお問い合わせがありました'
18+            admin_message = 'お問い合わせ内容:\n\n{}'.format(contact.message)
19+            send_mail(admin_subject, admin_message, settings.EMAIL_HOST_USER, [settings.EMAIL_HOST_USER])
20+
21+            return redirect('contact_success')
22+    else:
23+        form = ContactForm()
24+    return render(request, 'contact.html', {'form': form})
25+
26+def contact_success(request):
27+    return render(request, 'contact_success.html')

お問い合わせフォームの表示、データ受信、保存、メール送信といった一連の処理を行うためのプログラム(ビュー関数)を追加しました。

contact関数

この関数はお問い合わせページのメイン処理を担当します。

  • if request.method == 'POST':: ユーザーがフォームの「送信」ボタンを押した(POSTリクエスト)かどうかを判断しています。
    • 送信された場合 (ifブロックの中):
      1. form = ContactForm(request.POST): 送信されたデータを使ってContactFormを初期化します。
      2. if form.is_valid(): 入力内容が正しいか(例えばメールアドレスの形式が正しいかなど)を検証します。
      3. contact = form.save(): 検証が通ったら、入力されたデータをContactモデルを通じてデータベースに保存します。
      4. send_mail(...): Djangoに標準で備わっているメール送信機能です。これを2回呼び出しています。
        • 1回目: 入力されたメールアドレス(contact.email)宛に、受付完了の自動返信メールを送信します。
        • 2回目: サイト運営者(settings.EMAIL_HOST_USER)宛に、新しいお問い合わせがあったことを知らせる通知メールを送信します。
      5. return redirect('contact_success'): 全ての処理が成功したら、お問い合わせ完了ページ(/contact/success/)へ利用者を移動(リダイレクト)させます。
    • ページを最初に表示した場合 (elseブロックの中):
      • form = ContactForm(): 空のフォームを作成します。
  • return render(request, 'contact.html', {'form': form}): 作成したフォーム(中身あり、または空)をHTMLテンプレート contact.html に渡して、最終的なWebページを生成してユーザーに返します。

contact_success関数

この関数は、お問い合わせ完了ページを表示するだけのシンプルな役割です。

  • return render(request, 'contact_success.html'): contact_success.htmlというHTMLテンプレートを画面に表示します。

変更点: Djangoプロジェクト全体の設定変更

/config/settings.py
1-ALLOWED_HOSTS = []
2+ALLOWED_HOSTS = ['localhost', '127.0.0.1']
3
4+
5+# Emailの設定
6+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
7+EMAIL_HOST = 'smtp.gmail.com'
8+EMAIL_PORT = 587
9+EMAIL_USE_TLS = True
10+EMAIL_HOST_USER = 'engr.sng@gmail.com'
11+EMAIL_HOST_PASSWORD = 'vjostbnxevshsajl'

プロジェクト全体に関わる設定ファイルsettings.pyに2つの重要な変更を加えました。

  1. ALLOWED_HOSTSの更新 ALLOWED_HOSTSは、このDjangoアプリケーションがどのドメイン名(ホスト名)からのアクセスを許可するかを指定するセキュリティ設定です。開発環境で動作確認するために、ローカル環境を示す'localhost''127.0.0.1'を追加しました。
  2. Emailの設定 Djangoからメールを送信するために必要な設定を追加しました。
    • EMAIL_BACKEND: どのようにメールを送信するかを指定します。ここでは標準的なsmtpプロトコルを使う設定です。
    • EMAIL_HOST: 利用するSMTPサーバーのアドレスです。今回はGmailのサーバーを指定しています。
    • EMAIL_PORT: SMTPサーバーのポート番号です。
    • EMAIL_USE_TLS: 通信を暗号化するための設定です。
    • EMAIL_HOST_USER: SMTPサーバーにログインするためのメールアドレス(Gmailアドレス)です。
    • EMAIL_HOST_PASSWORD: SMTPサーバーにログインするためのパスワードです。Gmailの場合は、通常のログインパスワードではなく、「アプリパスワード」を生成して設定する必要があります。

変更点: 全ページ共通のヘッダーにお問い合わせリンクを追加

/templates/base.html
1+                        <a href="{% url 'contact' %}" class="btn btn-light">お問い合わせ</a>
2...
3+                        <a href="{% url 'contact' %}" class="btn btn-light">お問い合わせ</a>

サイトの全てのページで共通して使われるヘッダー部分(base.html)に、「お問い合わせ」ページへのリンクを追加しました。

  • {% url 'contact' %}: これはDjangoのテンプレートタグという機能です。urls.pyで設定したname='contact'を使い、/contact/というURLを自動的に生成してくれます。URLを直接HTMLに書き込むよりも、後からURLを変更したくなった場合に修正箇所が少なくなるため、非常に便利です。
  • ログインしている場合と、していない場合の両方のヘッダー表示部分にリンクを追加したため、ユーザーの状態に関わらず常にお問い合わせページへアクセスできるようになりました。

変更点: お問い合わせフォームページのHTMLを新規作成

/templates/contact.html
1+{% extends 'base.html' %}
2+
3+{% block title %}お問い合わせ{% endblock %}
4+
5+{% block content %}
6+    <section class="container">
7+        <h2 class="mb-4">お問い合わせ</h2>
8+        <form method="POST">
9+            {% csrf_token %}
10+            <div class="mb-3 row">
11+                <label for="id_email">メールアドレス</label>
12+                {{ form.email }}
13+            </div>
14+            ...
15+            <button type="submit" class="btn btn-primary">送信</button>
16+        </form>
17+    </section>
18+{% endblock %}

お問い合わせフォームを表示するためのHTMLファイル contact.html を新しく作成しました。

  • {% extends 'base.html' %}: このテンプレートが base.html を土台(継承)にしていることを示します。これにより、サイト共通のヘッダーやフッターが自動的に表示されます。
  • {% block content %} ... {% endblock %}: このブロックの中に、このページ固有のコンテンツ(お問い合わせフォーム)を記述します。
  • <form method="POST">: フォームのデータをサーバーに送信する方法としてPOSTメソッドを指定しています。
  • {% csrf_token %}: Djangoでフォームを使う際に必須のセキュリティ対策タグです。これがないと、悪意のあるサイトからの不正な送信を防げず、エラーになります。
  • {{ form.email }}, {{ form.title }}, {{ form.message }}: ビューから渡されたformオブジェクトの各フィールドを、HTMLの入力欄(<input><textarea>タグ)として自動的に描画しています。

変更点: お問い合わせ送信完了ページのHTMLを新規作成

/templates/contact_success.html
1+{% extends 'base.html' %}
2+
3+{% block title %}お問い合わせ{% endblock %}
4+
5+{% block content %}
6+<section class="container">
7+    <p>お問い合わせありがとうございます。</p>
8+    <p>後日、担当者よりご連絡させていただきますので、しばらくお待ちください。</p>
9+    <a href="{% url 'index' %}" class="btn btn-primary">ホームに戻る</a>
10+</section>
11+{% endblock %}

お問い合わせフォームの送信が正常に完了した後に表示されるページ contact_success.html を新しく作成しました。

  • contact.htmlと同様に、{% extends 'base.html' %}で共通レイアウトを継承しています。
  • {% block content %}の中には、ユーザーへの感謝のメッセージと、トップページへ戻るためのリンクを設置しています。
  • <a href="{% url 'index' %}" ...>: urls.pyname='index'と名付けられたトップページのURLを自動生成し、リンクを作成しています。

おわりに

今回はお問い合わせフォームの実装を通して、Djangoでの機能追加における一連の流れを学びました。モデルでデータベースの設計図を作り、フォームで入力欄を定義し、ビューでデータを受け取って保存するという、それぞれのファイルの役割を体験できたはずです。さらに、settings.pyに必要な設定を追加することで、send_mail関数を使ったメール自動送信機能も実現しました。ここで学んだモデル、ビュー、テンプレート、URL設定の連携は、あらゆるWebアプリケーション開発の基礎となる重要な知識です。

関連コンテンツ