第19章 用户账户
19.1让用户输入数据
19.1.1 添加新主题
1.用于添加主题的表单
forms.py
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):#模块TopicForm继承forms.ModelForm
class Meta:
model =Topic #根据模型Topic创建表单
fields = ['text']
labels = ['text':'']#对字段text不生成标签
2.URL模式new_topic
#learning_logs/urls.py
from django.urls import path#导入path用于映射url到视图
from . import views#.:从当前文件夹导入views.py
app_name = 'learning_logs'
urlpatterns = [
#主页
path('',views.index,name='index'),#指定django要调用index函数
#显示所有主题
path('topics/',views.topics, name='topics'),
#特定主题的详细页面
path('topics/<int:topic_id>/',views.topic,name='topic'),
#用于添加新主题的页面
path('new_topic/',views.new_topic/,name='new_topic'),
]
app_name = 'learning_logs'
3.视图函数new_topic()
views.py
from django.shortcuts import render,redirect
from .models import Topic
from .forms import TopicForm
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
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(data=request.POST)
if form.is_valid():
form.save()
return redirect(request,'learning_logs/new_topic.html',context)
4.GET请求和POST请求
创建Web应用程序时,将用到的两种主要请求类型是GET请求
和POST请求。对于只是从服务器读取数据的页面,使用GET
请求;在用户需要通过表单提交信息时,通常使用POST 请
求。处理所有表单时,都将指定使用POST方法。还有一些其
他类型的请求,但本项目没有使用。函数new_topic() 将请求对象作为参数。用户初次请求该页
面时,其浏览器将发送GET请求;用户填写并提交表单时,其
浏览器将发送POST请求。根据请求的类型,可确定用户请求
的是空表单(GET请求)还是要求对填写好的表单进行处理
(POST请求)。
❶处的测试确定请求方法是GET还是POST。如果请求方法不
是POST,请求就可能是GET,因此需要返回一个空表单。
(即便请求是其他类型的,返回空表单也不会有任何问题。)
❷处创建一个TopicForm 实例,将其赋给变量form ,再通过
上下文字典将这个表单发送给模板(见❼)。由于实例
化TopicForm 时没有指定任何实参,Django将创建一个空表
单,供用户填写。
如果请求方法为POST,将执行else 代码块,对提交的表单数
据进行处理。我们使用用户输入的数据(存储
在request.POST 中)创建一个TopicForm 实例(见❸),这
样对象form 将包含用户提交的信息。
要将提交的信息保存到数据库,必须先通过检查确定它们是有
效的(见❹)。方法is_valid() 核实用户填写了所有必不可
少的字段(表单字段默认都是必不可少的),且输入的数据与
要求的字段类型一致(例如,字段text 少于200字符,这是第
18章在models.py中指定的)。这种自动验证避免了我们去做大
量的工作。如果所有字段都有效,就可调用save() (见
❺),将表单中的数据写入数据库。
保存数据后,就可离开这个页面了。为此,使用redirect()
将用户的浏览器重定向到页面topics (见❻)。在页
面topics 中,用户将在主题列表中看到他刚输入的主题。
我们在这个视图函数的末尾定义了变量context ,并使用稍后
将创建的模板new_topic.html来渲染页面。这些代码不在if 代
码块内,因此无论是用户刚进入new_topic 页面还是提交的表
单数据无效,这些代码都将执行。用户提交的表单数据无效
时,将显示一些默认的错误消息,帮助用户提供有效的数据。
5.模板 new_topic
{% 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 %}
这个模板继承了base.html,因此其基本结构与项目“学习笔
记”的其他页面相同。在❶处,定义了一个HTML表单。实参
action 告诉服务器将提交的表单数据发送到哪里。这里将它
发回给视图函数new_topic() 。实参method 让浏览器以
POST请求的方式提交数据。
Django使用模板标签{% csrf_token %} (见❷)来防止攻击
者利用表单来获得对服务器未经授权的访问(这种攻击称为跨
站请求伪造 )。❸处显示表单,从中可知Django使得完成显
示表单等任务有多简单:只需包含模板变量{{ form.as_p
}} ,就可让Django自动创建显示表单所需的全部字段。修饰
符as_p 让Django以段落格式渲染所有表单元素,这是一种整
洁地显示表单的简单方式。
Django不会为表单创建提交按钮,因此我们在❹处定义了一
个。
6.链接到页面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 添加新条目
1.用于添加新条目的表单
forms.py
from django import forms
from .models import Topic,Entry
class TopicForm(forms.ModelForm):#模块TopicForm继承forms.ModelForm
class Meta:
model = Topic #根据模型Topic创建表单
fields = ['text']
labels = {'text':''}#对字段text不生成标签
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text':' '}
widgets = {'text':forms.Textarea(attrs={'cols'/:80})}
2.URL模式new_entry
urls.py
#learning_logs/urls.py
from django.urls import path#导入path用于映射url到视图
from . import views#.:从当前文件夹导入views.py
app_name = 'learning_logs'
urlpatterns = [
#主页
path('',views.index,name='index'),#指定django要调用index函数
#显示所有主题
path('topics/',views.topics, name='topics'),
#特定主题的详细页面
path('topics/<int:topic_id>/',views.topic,name='topic'),
#用于添加新主题的页面
path('new_topic/',views.new_topic,name='new_topic'),
#用于添加新条目的标签
path('new_entry/<int:topic_id>/',views.new_entry,name='new_entry'),
]
app_name = 'learning_logs'
3.视图函数new_entry()
from django.shortcuts import render,redirect
from .models import Topic
from .forms import TopicForm,EntryForm
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
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':#请求方法为POST
#未提交数据:创建一个新表单实例
form = TopicForm()
else:
#POST提交的数据,对数据进行处理
form = TopicForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('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 redirect('learning_logs:topic',topic_id=topic_id)
#显示空表单或指出表单数据无效
context ={'topic':topic,'form':form}
return render(request,'learning_logs/new_entry.html',context)
4.模板new-entry
{% extends "learnings_logs/base.html" %}
{% block content %}
<p><a href="{% url 'learnings_logs:topic' topic.id %}">{{ topic }}</a></p>
<p>Add a new entry:</p>
<form action="{% url 'learnings_logs:new_entry' topic.id %}"method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name='submit'>Add entry</button>
</form>
{% endlock content %}
5.链接到页面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>
{% 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 %}