13.Djangoで新規投稿機能を作ってみよう

データを入力

Djangoで1行掲示板にユーザーが投稿するための新規投稿機能を作ってみましょう。新規投稿機能を作成するには投稿フォームとそれを表示するWebページ、投稿内容をデータベースに保存するなど複数の機能が必要になります。

目次

必要な機能のリストアップ

まずはじめに必要になる機能とページを確認しておきましょう。

掲示板の新規投稿には次の各機能が必要となります。

掲示板の新規投稿機能のフロー図

  • 投稿用Webページ、投稿保存機能へのルーティング(urls.pyに記述)
  • 新規投稿ページへ遷移するためのボタン(index.htmlに配置)
  • 新規投稿用のWebページ(new.htmlを作成)
  • 投稿内容をデータベースに保存する関数(views.pyに記述)
  • 投稿完了ページ(posted.html)を作成

これらを1つずつ作っていきましょう。

ルーティング

まずはユーザーが各URLにアクセスした場合のルーティング処理を作成します。

/bbs/urls.py に次のように記述します。

from django.urls import path
from . import views

app_name = 'bbs'

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:id>', views.detail, name='detail'),
    path('new', views.new, name='new'), # 投稿用WebページのURL
    path('create', views.create, name='create'), # 新規投稿をデータベースに保存するためのURL
]

ユーザーが /bbs/new/ にアクセスすると views.py の new関数を、/bbs/create/ にアクセスすると views.py の create関数を呼び出します。

また、それぞれname属性を付けておいたので、newやnameといった名前でこのURLをリンクで呼び出せます。

新規投稿ページへ遷移するためのボタンをindex.htmlに配置

投稿一覧ページに新規投稿ページへの遷移ボタンを配置しておきましょう。

{% extends "./base.html" %}

{% block content %}
  <h1>Djangoで作る1行掲示板</h1>

  {% if searchForm %}
    <form action='{% url "bbs:index" %}' method="get">
      <div>
        {{ searchForm }}
        <input type="submit" value="検索">
        <a href='{% url "bbs:index" %}'>クリア</a>
      </div>
    </form>
  {% endif %}

  {% for article in articles %}
    <p>
      <a href='{% url "bbs:detail" article.id %}'>{{ article.content }}</a>
      {{ article.user_name }}
    </p>
  {% endfor %}

  <!-- 新規投稿ページへの遷移ボタン -->
  <p>
    <button onclick='location.href="{% url "bbs:new" %}"'>新規投稿</button>
  </p>

{% endblock %}

先ほど urls.py で /new/ のURLを new という名前で呼び出せるようにしておいたので、25行目のリンクで /bbs/new/ へ遷移することができます。

新規投稿フォームを定義

次に新規投稿フォームを定義しましょう。

forms.py に次のように記述します。

from django import forms
from .models import Article # models.pyからArticleクラスをインポート

# SearchFormクラスを定義
class SearchForm(forms.Form):
        keyword = forms.CharField(label='', max_length=50)

# 新規投稿フォームを定義
class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ('content', 'user_name')

データベースに書き込むためのフォームなので、データ形式を指定する時にモデルを参照するだけで記述できます。

新規投稿ページを呼び出すための関数を作成

作成したフォームを利用して新規投稿用のWebページを呼び出すための関数を作成します。

views.py に次のように記述します。

from django.shortcuts import render,get_object_or_404
from django.http import HttpResponse
from .models import Article
from .forms import SearchForm
from .forms import ArticleForm # forms.pyからArticleFormクラスをインポート

def index(request):
    searchForm = SearchForm(request.GET)
    if searchForm.is_valid():
        keyword = searchForm.cleaned_data['keyword']
        articles = Article.objects.filter(content__contains=keyword)
    else:
        searchForm = SearchForm()
        articles = Article.objects.all()

    context = {
    'articles': articles,
    'searchForm': searchForm,
    }
    return render(request, 'bbs/index.html', context)

def detail(request, id):
    article = get_object_or_404(Article, pk=id)
    context = {
        'article': article,
    }
    return render(request, 'bbs/detail.html', context)

# 新規投稿画面のWebページを返すnew関数
def new(request):
    articleForm = ArticleForm() # ArticleFormオブジェクトを生成
    # テンプレート側でarticleFormのデータを取り出せるようにcontextに渡す
    context = {
        'articleForm': articleForm,
    }
    return render(request, 'bbs/new.html', context) # new.htmlというテンプレートを返す

新規投稿フォームを呼び出すだけなので

  • forms.pyからArticleFormクラスをインポート
  • articleFormオブジェクトを用意してcontextに追加
  • テンプレート(new.html)を呼び出す

これだけです。

投稿用Webページ(new.html)の作成

views.py で呼び出すように記述した new.html というテンプレートファイルを作成します。

/templates/bbs/ に new.html という名前でファイルを作成し、次のように記述します。

{% extends "./base.html" %}

{% block content %}
  <h1>Djangoで作る新規投稿フォーム</h1>

  <form action='{% url "bbs:create" %}' method="post">
    {% csrf_token %}<!-- セキュリティのための記述 -->
    {{ articleForm }}<!-- views.pyから受け取ったarticleFormを表示 -->
    <button type='submit'>投稿</button>
    <a href='{% url "bbs:index" %}'>一覧ページへ戻る</a>
  </form>
{% endblock %}

forms.py で定義した ArticleForm クラスを元に、views.py の new関数の中でオブジェクトを作成してテンプレートに渡しているので、テンプレート側では articleForm という変数名を指定するだけで、必要な項目(今回はcontentとUser name)が用意されたフォームを作成してくれます。

また、6行目では「投稿」ボタンをクリックすると create という名前のルートをPOSTメソッドで呼び出すよう記述しています。

※csrf_tokenはDjangoが用意してくれているセキュリティ機能です。フォームがデータを送信する時にトークン情報を一緒に送信し、リクエストを受け取ったViewが、トークンが正しいものがどうか検証し、正常なリクエストだと認められれば処理を実行してくれます。

投稿内容をデータベースに保存する関数を作成

次に、投稿用Webページで「投稿」ボタンを押した時に呼び出される create関数を作成しましょう。

views.py に次のように記述します。

from django.shortcuts import render,get_object_or_404
from django.http import HttpResponse
from .models import Article
from .forms import SearchForm
from .forms import ArticleForm # forms.pyからArticleFormクラスをインポート

def index(request):
    searchForm = SearchForm(request.GET)
    if searchForm.is_valid():
        keyword = searchForm.cleaned_data['keyword']
        articles = Article.objects.filter(content__contains=keyword)
    else:
        searchForm = SearchForm()
        articles = Article.objects.all()

    context = {
    'articles': articles,
    'searchForm': searchForm,
    }
    return render(request, 'bbs/index.html', context)

def detail(request, id):
    article = get_object_or_404(Article, pk=id)
    context = {
        'article': article,
    }
    return render(request, 'bbs/detail.html', context)

# 新規投稿画面のWebページを返すnew関数
def new(request):
    articleForm = ArticleForm()
    context = {
        'articleForm': articleForm,
    }
    return render(request, 'bbs/new.html', context)

#新規投稿データを保存するcreate関数
def create(request):
    # リクエストのメソッドがPOSTなら
    if request.method == 'POST':
        articleForm = ArticleForm(request.POST) # リクエストから取り出したデータを代入
        # 受け取ったデータが正常なら
        if articleForm.is_valid():
            article = articleForm.save() # データを保存
    context = {
        'article': article,
    }
    return render(request, 'bbs/posted.html', context) # detail.htmlを返す

この関数は新規投稿フォームからデータを受け取った時、POSTメソッドかどうか、受け取ったデータが正常かどうかをチェックし、問題なければデータを保存します。

その後、保存したデータを context に追加して、テンプレートファイル(posted.html)を呼び出します。

投稿完了ページ(posted.html)を作成

最後に、ユーザーに投稿完了を知らせる投稿完了ページのテンプレートを作成しましょう。

/templates/bbs/ に posted.html という名前でファイルを作成し、次のように記述します。

{% extends "./base.html" %}

{% block content %}
    <h1>{{ article.id }}の投稿が完了しました!</h1>
    <p>{{ article.content }}, {{article.user_name }}</p>
    <p><a href='{% url "bbs:index" %}'>一覧ページへ戻る</a></p>
{% endblock %}

投稿した記事のidと投稿内容、投稿者名、そして一覧ページに戻るためのリンクを配置しています。

動作確認

それでは動作確認です。

Djangoコマンドを使ってサーバーを起動し、ブラウザで確認してみましょう。

http://127.0.0.1:8000/bbs にアクセスします。

トップページ

新規投稿ボタンをクリックしてみましょう。

新規投稿ページ

このように新規投稿フォームが表示されます。

※一切ページデザインをしていないのでガタガタですが、見た目は後からいくらでも整えることができます。

試しにデータを入力して「投稿」してみましょう。

データを入力

「投稿」ボタンをクリック

投稿完了ページ

投稿完了ページへと遷移しました。

※何度かテストしたのでidがずれてしまっています。気にしないで下さい。

「一覧ページへ戻る」をクリックしてみましょう。

投稿の追加を確認

先ほど投稿した内容が一覧ページにも反映されています。

新規投稿機能の完成です!

このエントリーをはてなブックマークに追加

コメントを残す

頂いたコメントは一読した後表示させて頂いております。
反映まで数日かかる場合もございますがご了承下さい。