19.1 让用户能够输入数据
19.1.1 添加新主题
创建基于表单的页面的方法几乎与前面创建网页一样: 定义URL,编写一个视图函数并编写一个模板。一个主要差别是,需要导入包含表单的模块 forms.py
1. 用于添加主题的表单
让用户输入并提交信息的页面都是表单,表单的很多工作都是由Django自动完成的,比如:
- 用户输入信息时,我们需要进行验证,确认提供的信息是正确的数据类型,且不是恶意的信息。
- 对这些有效信息进行处理,并将其保存到数据库的合适地方
在Django中,创建表单的最简单方式是使用ModelForm, 它根据我们定义的模型中的信息自动创建表单。创建一个名为forms.py文件,并将其存储到models.py所在的目录中, 并在其中编写第一个表单
# forms.py
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
首先导入了模块forms以及要使用的模型Topic,我们定义了一个名为TopicForm的类,它继承了forms.ModelForm。
最简单的ModelForm版本只包含一个内嵌的 Meta 类,它告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段。根据模型Topic创建一个表单,该表单只包含字段text,labels代码让Django不要为字段text生成标签。
2. URL 模式 new_topic
当用户要添加新主题时,我们将切换到http://localhost:8000/new_topic/,添加URL模式到learning_logs/urls.py
# urls.py
from django.conf.urls import url
from . import views
app_name = 'learning_logs'
urlpatterns = [
# index
url(r'^$', views.index, name='index'),
# show all topics
url(r'^topics/$', views.topics, name='topics'),
# show detail info for specific topic
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
# add new topic page
url(r'^new_topic/$', views.new_topic, name='new_topic'),
]
3.视图函数new_topic()
函数new_topic()需要处理两种情况:刚进入new_topic网页(在这种情况下,它应显示一个空表单);对提交的表单数据进行处理,并将用户重定向到网页topics
# 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
# Create your views here.
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
"""show all topics"""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
def new_topic(request):
"""添加新主题"""
if request.method != 'POST':
# 未提交数据: 创建一个新表单
form = TopicForm()
else:
# POST提交的数据, 对数据进行处理
form = TopicForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
如果请求方法是POST, 对提交的表单数据进行处理。我们使用用户输入的数据(它们存储在request.POST中)创建一个TopicForm实例,这样对象form将包含用户提交的信息。
要将提交的信息保存到数据库,必须通过检查确定它们是有效的。函数is_valid()自动验证避免了我们去做大量的工作。如果所有字段都有效,我们就可调用save(),将表单中的数据写入数据库。保存数据后,就可离开这个页面,使用reverse()获取topics的URL。
4. 模板 new_topic
# new_topic.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 %}
实参action告诉服务器将提交的表单数据发送到哪里,这里我们将它发送回视图函数new_topic()。
Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获取对服务器未经授权的访问。 为了显示表单,我们只需要包含模板变量{{ form.as_p }},就可让Django自动创建显示表单所需的全部字段。修饰符as_p让Django以段落格式渲染所有表单元素,这是一种整洁地显示表单的简单方式。
另外,Django 不会为表单创建提交按钮。
5. 链接到页面 new_topic
接下来,我们在页面topics中添加一个到页面new_topic的链接
# topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
<a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a>
{% endblock content %}
用于添加新主题的页面
19.1.2 添加新条目
现在用户可以添加新主题了,但他们还想添加新条目。我们将再次定义URL,编写视图和模板,并了解到添加新条目的网页。但在此之前,我们需要在forms.py中再添加一个类。
** 1. 用于添加新条目的表单**
# forms.py
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols':80})}
我们定义了属性widgets, widget是HTML表单元素,如单行文本框,多行文本趋于或下拉了表。通过设置属性widgets,可覆盖Django选择的默认小部件。通过让Django使用forms.Textarea,我们定制了'text'的输入小部件,将文本区域的宽度设置为80列,而不是默认的40列。
2. URL模式new_entry
在用于添加新条目的页面的URL模式中,需要包含实参topic_id, 因此条目必须与特定的主题相关联。我们将它添加到learning_logs/urls.py中
from django.conf.urls import url
from . import views
app_name = 'learning_logs'
urlpatterns = [
# index
url(r'^$', views.index, name='index'),
# show all topics
url(r'^topics/$', views.topics, name='topics'),
# show detail info for specific topic
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
# add new topic page
url(r'^new_topic/$', views.new_topic, name='new_topic'),
# add new entry page
url(r'new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry'),
]
代码(?P<topic_id>\d+)捕获一个数字值,并将其存储在变量topic_id中。 请求的URL与这个模式匹配时,Django将请求和主题ID发送给函数new_entry()
** 3. 视图函数new_entry()**
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import Topic
from .forms import TopicForm, EntryForm
# Create your views here.
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
"""show all topics"""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
def new_topic(request):
"""添加新主题"""
if request.method != 'POST':
# 未提交数据: 创建一个新表单
form = TopicForm()
else:
# POST提交的数据, 对数据进行处理
form = TopicForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
def new_entry(request, topic_id):
"""在特定的主题中添加新条目"""
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# 未提交数据,创建一个空表单
form = EntryForm()
else:
# POST提交的数据,对数据进行处理
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)
调用save()时,我们传递了实参commit=False,让Django创建一个新的条目对象,并将其存储到ew_entry中,但不将它保存到数据库中。我们将new_entry的属性topic设置为在这个函数开头从数据库中获取的主题,然后调用save()。
4. 模板new_entry
# new_entry.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 %}
5. 链接到页面new_entry
# topic.html
{% 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>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>
There are no entries for this topic yet.
</li>
{% endfor %}
</ul>
{% endblock content %}
添加新条目new_entry页面
19.1.3 编辑条目
下面创建一个页面,让用户能够编辑既有条目
1. URL模式edit_entry
修改后的learning_logs/urls.py
from django.conf.urls import url
from . import views
app_name = 'learning_logs'
urlpatterns = [
# index
url(r'^$', views.index, name='index'),
# show all topics
url(r'^topics/$', views.topics, name='topics'),
# show detail info for specific topic
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
# add new topic page
url(r'^new_topic/$', views.new_topic, name='new_topic'),
# add new entry page
url(r'new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry'),
# edit entry page
url(r'edit_entry/(?P<entry_id>\d+)/$', views.edit_entry, name='edit_entry')
]
2.视图函数edit_entry()
页面edit_entry收到GET请求时,edit_entry()将返回一个表单,让用户能够对条目进行编辑。该页面收到POST请求时,它将修改后的文本保存到数据库中
# views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
# Create your views here.
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
"""show all topics"""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
def new_topic(request):
"""添加新主题"""
if request.method != 'POST':
# 未提交数据: 创建一个新表单
form = TopicForm()
else:
# POST提交的数据, 对数据进行处理
form = TopicForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
def new_entry(request, topic_id):
"""在特定的主题中添加新条目"""
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# 未提交数据,创建一个空表单
form = EntryForm()
else:
# POST提交的数据,对数据进行处理
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)
def edit_entry(request, entry_id):
"""编辑既有条目"""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if request.method != 'POST':
# 初次请求,使用当前条目填充表单
form = EntryForm(instance=entry)
else:
# POST提交表单,对数据进行处理
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)
在请求方法为GET时将执行的if代码块中,我们使用实参instance=entry创建一个EntryForm实例。这个实参让Django创建一个表达,并使用既有条目对象中的信息填充它。处理POST请求时,我们传递实参instance=entry和data=request.POST,让Django根据既有条目对象创建一个表单实例,并根据request.POST中的相关数据对其进行修改。
3. 模板 edit_entry.html
# edit_entry.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 %}
4.链接到页面edit_entry
# topic.html
{% 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>
{% 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 %}
<li>
There are no entries for this topic yet.
</li>
{% endfor %}
</ul>
{% endblock content %}
每个条目都有一个用于对其进行编辑的链接
网友评论