iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
0
自我挑戰組

初學者Python的應用實作系列 第 26

DAY 26 Django允許使用者輸入資料

我們要讓使用者可以新增主題開始,建置以表單為基礎的頁面,其方法與前面建置網頁幾乎一樣,要先定義URL,編寫視圖函式和編寫模板,其中不一樣的地方是要匯入含有表單模組的forms.py檔

新增主題

主題的ModelForm

任何能讓使用者輸入並提交資訊的頁面都是表單,在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模式

將new_topic頁面的URL模式新增到learning_logs資料夾中的urls.py中,如下

--省略--
urlpatterns=[
--省略--
path('new_topic/',views.new_topic,name='new_topic'),
]

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模板

建立一個新的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 %}

上面程式碼第六行是告訴伺服器要提交的表單資料要傳送到哪裡,第七行是防止駭客用表單來取得對伺服器未經授權的存取,第八行是顯示表單,第九行是為了提交表單建立一個按鈕

連結到new_topic頁面

開啟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

進入新增頁面


新增紀錄項目

紀錄項目的ModelForm

建立一個與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欄

new_entry URL模式

用來新增紀錄項目的頁面URL模式中需要放入topic_id引數,因為紀錄需要和主題相關聯

將new_entry頁面的URL模式新增到learning_logs資料夾中的urls.py中,如下

--省略--
urlpatterns=[
--省略--
path('new_entry/<int:topic_id>/',views.new_entry,name='new_entry'),
]

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模板

建立一個新的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 %}

上面程式碼第三行是讓使用者知道是在哪一個主題中新增項目,第八行可讓視圖函式能把新項目關連到正確的主題

連結到new_entry頁面

開啟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

進入新增頁面

編輯紀錄項目

建立一個頁面可讓使用者編輯現有的紀錄項目

edit_entry URL模式

這個頁面的URL需要傳入想編輯的紀錄項目ID模式

開啟learning_logs資料夾中的urls.py中,如下

--省略--
urlpatterns=[
--省略--
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
]

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模板

建立一個新的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%}標記,讓視圖物件能修改到正確的紀錄項目物件,第十二行是提醒使用者要按按鈕儲存修改的內容

連結到edut_entry頁面

開啟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


上一篇
DAY 25 Django建置其他網頁
下一篇
DAY 27 Django建立使用者帳號
系列文
初學者Python的應用實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
skywalker66
iT邦新手 5 級 ‧ 2020-06-27 16:50:50

想請問有辦法讓 {{ form.as_p }} 在送出資料(post)後不要消失嗎?感謝!

我要留言

立即登入留言