超初心者用: Djangoでウェブアプリを作る ⑤ユーザーによるentryの追加

前回までにユーザーが新しいトピックを追加することができるようになりました。今回は Entry を入力できるようにします。手順はおなじみの、URL の決定、view function と template の記入、ページへのリンクです。しかし、まずは form.py にクラスを追加します。

Entry ModelForm

Entry モデルのためのフォームを作らなければなりません。今回は少し異なります。

# form.py

from django import forms
from .models import Topic, Entry

class TopicForm(forms.ModelForm):
 # --省略--

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = {'text': ''}
        widgets = {'text': forms.Textarea(attrs={'cols': 80})}

まずは、Topic と同様に Entry クラスをインポートします。Metaクラスをネストしてある forms.ModelForm からの EntryForm クラスを作ります。ここでも text フィールドを与えます。

widgets = {'text': forms.Textarea(attrs={'cols': 80})}

widgets attribute を使って、text box の横幅の大きさを設定します。今回は横に80 columns に設定しています。ちなみにデフォルトでは40となっています。

new_entry URL

新しい entry を追加するときには、特定のトピックに結び付けるのでURL に topic_id を含めなければなりません。

# practicing_logs/urls.py

#--省略--
urlpatterns = [
    #--省略--
    # 新しい entry の追加ページ
    url(r'^new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry'),
]

new_entry() view function

この view function は new_topic とほとんど一緒です。

# views.py

from django.shortcuts import render
#--省略--
from .models import Topic
from .forms import TopicForm, EntryForm
#--省略--

def new_entry(request, topic_id):
    """特定のトピックに entry を追加"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 空欄
        form = EntryForm()
    else:
        # POST data が送られた
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()
            return HttpResponseRedirect(reverse('practicing_logs:topic', args=[topic_id]))

    context = {'topic': topic, 'form': form}
    return render(request, 'practicing_logs/new_entry.html', context)

先ほど作成した EntryForm をインポートします。new_entry() はURL から受け取る topic_id parameter を持っています。

topic = Topic.objects.get(id=topic_id)

受け取った id に呼応するトピックを取得します。

if request.method != 'POST':

リクエストが POST か GET なのかを確認します。GET であれば空欄の EntryForm を作成します。POST であれば、入力されたデータで処理します。

if form.is_valid():

入力されたデータが適切なフォーマットであるかを確認し、適切であれば save() を実行します。commit==False はデータベースに保存することなく、entry を作成し、new_entry に保存します。new_entry のトピックを決定したら save() を実行します。これはデータベースに保存されます。

return HttpResponseRedirect(reverse('practicing_logs:topic', args=[topic_id]))

無事に追加できたら、topic ページにリダイレクトします。

new_entry template

このテンプレートも new_topic の時のものと似ています。

# new_entry.html

{% extends "practicing_logs/base.html" %}
{% block content %}
<p><a href="{% url 'practicing_logs:topic' topic.id %}">{{ topic }}</a></p>

<p>Add a new entry:</p>
<form action="{% url 'practicing_logs:new_entry' topic.id %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name='submit'>add entry</button>
</form>

{% endblock content %}

<p><a href="{% url 'practicing_logs:topic' topic.id %}">{{ topic }}</a></p>

一番上にはどのトピックに入力しようとしているのかを分かりやすくするために、トピック名を表示させます。

<form action="{% url 'practicing_logs:new_entry' topic.id %}" method='post'>

action argument は URL の topic_id を含んでいます。

new_entry ページにリンクする

それぞれのトピックページから entry を追加するページに飛べるようにします。

# topic.html

{% extends "practicing_logs/base.html" %}
{% block content %}
<p>Topic: {{ topic }}</p>

<p>Entries:</p>

<p>
<a href="{% url 'practicing_logs:new_entry' topic.id %}">add new entry</a>
</p>

<ul>
 #--省略--
 </ul>
{% endblock content %}

既存の entry を見せる前に 新しい entry を追加するためのリンクを継和得ます。下の画像のような感じになります。

f:id:kendamaaa:20200428063147j:plain