我們要讓使用者可以新增主題開始,建置以表單為基礎的頁面,其方法與前面建置網頁幾乎一樣,要先定義URL,編寫視圖函式和編寫模板,其中不一樣的地方是要匯入含有表單模組的forms.py檔
任何能讓使用者輸入並提交資訊的頁面都是表單,在Django中建置表單最簡單的方式就是使用ModelForm,它會依據一開始定義的模型中的資訊自動建立表單
建立一個forms.py檔存放在models.py所在的資料夾,如下
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic # 依據Topic模型來建立表單
fields = ['text'] # 表單只有text欄位
labels = {'text': ''} # 不要在text欄位加標籤
將new_topic頁面的URL模式新增到learning_logs資料夾中的urls.py中,如下
--省略--
urlpatterns=[
--省略--
path('new_topic/',views.new_topic,name='new_topic'),
]
new_topic()函式需要處理兩種情況,第一種情況是剛進入new_topic網頁這種情況會顯示空表單,第二種情況是提交表單對資料進行處理,並將使用者重新導向到topics網頁中
:arrow_down: 新增一些內容至views.py檔
from django.shortcuts import render
from django.http import HttpResponseRedirect # 新增區塊
from django.urls import reverse # 新增區塊
from .models import Topic
from .forms import TopicForm # 新增區塊
--省略--
# 以下為新增區塊
def new_topic(request):
if request.method != 'POST':
form = TopicForm()
else:
form = TopicForm(request.POST)
if form.is_valid():
new_topic.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
在上面程式碼第二行匯入HttpResponseRedirect類別會讓使用者提交主題後重新導向至topics網頁,第十二行是要確定請求方法是GET還是POST,如果不是POST那就會是玵後返回一個空表單,第十五行建立一個TopicForm實例,把它存到form變數內再將它存到第二十行的context字典,第十六行是確認使用者有沒有對所有欄位填寫資料,如果有就可呼叫第十七行的save(),儲存資料後我們就可以離開頁面,使用reverse()來取得頁面topics的URL,並將它傳送到HttpResponseRedirect(),然後將使用者重新導向topics頁面
建立一個新的new_topic.html模板存放在index.html同個資料夾中
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Add a new topic:</p>
<form action="{% url 'learning_logs:new_topic' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">add topic</button>
</form>
{% endblock content %}
上面程式碼第六行是告訴伺服器要提交的表單資料要傳送到哪裡,第七行是防止駭客用表單來取得對伺服器未經授權的存取,第八行是顯示表單,第九行是為了提交表單建立一個按鈕
開啟topics.html新增一個連到new_topic的連結
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
--省略--
<a href="{% url 'learning_logs:new_topic' %}">Add new topic</a>
{% endblock content %}
:arrow_down:用來新增主題的頁面如下
先按Add new topic
進入新增頁面
建立一個與Entry模型相關聯的表單
新增一些程式碼至forms.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})} # 定義一個文字區塊寬度為80欄
用來新增紀錄項目的頁面URL模式中需要放入topic_id引數,因為紀錄需要和主題相關聯
將new_entry頁面的URL模式新增到learning_logs資料夾中的urls.py中,如下
--省略--
urlpatterns=[
--省略--
path('new_entry/<int:topic_id>/',views.new_entry,name='new_entry'),
]
新增一些內容至views.py檔
--省略--
from .forms import TopicForm,EntryForm # 新增區塊
--省略--
# 以下為新增區塊
def new_entry(request, topic_id):
topic = Topic.objects.get(id=topic_id) # 使用topic_id來取得正確主題物件
if request.method != 'POST':
form = EntryForm()
else:
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('learning_logs:topic',args=[topic_id]))
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
建立一個新的new_entry.html模板存放在index.html同個資料夾中
{% extends "learning_logs/base.html" %}
{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p>
<p>Add a new entry:</p>
<form action="{% url 'learning_logs:new_entry' topic.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name='submit'>add entry</button>
</form>
{% endblock content %}
上面程式碼第三行是讓使用者知道是在哪一個主題中新增項目,第八行可讓視圖函式能把新項目關連到正確的主題
開啟topic.html新增一個連到new_entry的連結
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</p>
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
</p>
<ul>
--省略--
</ul>
{% endblock content %}
用來新增紀錄項目的頁面如下
先按add new entry
進入新增頁面
建立一個頁面可讓使用者編輯現有的紀錄項目
這個頁面的URL需要傳入想編輯的紀錄項目ID模式
開啟learning_logs資料夾中的urls.py中,如下
--省略--
urlpatterns=[
--省略--
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
]
新增一些內容至views.py檔
--省略--
from .models import Topic,Entry # 新增區塊
--省略--
# 以下為新增區塊
def edit_entry(request, entry_id):
"""Edit an existing entry."""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if request.method != 'POST':
form = EntryForm(instance=entry)
else:
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic.id]))
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html',context)
建立一個新的edit_entry.html模板存放在index.html同個資料夾中
{% extends "learning_logs/base.html" %}
{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p>
<p>Edit entry:</p>
<form action="{% url 'learning_logs:edit_entry' entry.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">save changes</button>
</form>
{% endblock content %}
上面程式碼第九行把紀錄項目的id當成引數放在{%url%}標記,讓視圖物件能修改到正確的紀錄項目物件,第十二行是提醒使用者要按按鈕儲存修改的內容
開啟topic.html新增一個連到edit_entry的連結
--省略--
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
<p>
<a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entry</a>
</p>
</li>
{% empty %}
--省略--
{% endblock content %}
每個紀錄項目後面都有一個編輯連結如下
附上排版較精美的
HackMD網址:https://hackmd.io/gEUz1UzfRdG5XuRiP0sN2A
資料來源:<<python程式設計的樂趣>>-Eric Matthes著/H&C譯
資料來源:https://github.com/ehmatthes/pcc/tree/master/chapter_19#p-439-including-the-urls-from-users