视图简介
视图在Django中的作用是用于处理Web请求信息以及返回响应信息的函数。
当我们访问http://127.0.0.1:8000/polls
时,Django会解析出'polls'
这个路径,然后拿着这个路径去URLconfs
里配置相对应的视图函数,视图函数接收请求,然后处理请求,最后返回响应信息,也许这一段不是很理解,但是当做完之后再回头看,就很清楚了。
投票应用,需要列以下几个视图:
- 问题索引页——展示最近的几个投票问题
- 问题详情页——展示某个投票的问题和不带结果的选项列表
- 问题结果页——展示某个投票的结果
- 投票处理器——用于响应用户为某个问题的特定选项投票的操作
创建视图
增加视图
我们通过编辑polls/views.py
来增加三个视图
# 创建问题详情页视图
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
# 创建问题结果页视图
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
# 创建处理器视图
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
其中request
属性,当一个页面被请求时,Django就会创建一个包含本次请求信息的HttpRequest对象,Django会将这个对象自动传递给视图函数,一般约定使用request
参数来接收这个对象,在视图函数中,可以通过访问该对象的属性来提取http
协议的的请求数据,以下是常用的属性,也可以查看【官网】中详细说明
request.GET:url上携带的参数,包含所有HTTP GET参数的类字典对象QueryDict
request.POST:post请求携带的数据,包含所有HTTP POST参数的类字典对象QueryDict
request.method:请求方法,请求中使用的HTTP方法的字符串表示,全大写(POST/GET)
request.path:完整路径
request.path_info:URL的路径 不包含ip和端口 不包含参数
request.body:请求体
配置URLconfs
我们通过上面增加了polls/views.py
的内容,但是我们还不能马上使用,因为当我们去访问的时候,Django并不能通过URL找到对应的视图函数,所以,我要在URLconfs里增加新增视图函数的路由,那来编辑一下polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
保存,重新运行Django。当然,我们可以根据上面路径上的注释来测试访问一下,即http://127.0.0.1:8000/polls/34
、http://127.0.0.1:8000/polls/34/results
、http://127.0.0.1:8000/polls/34/vote
,其实很简单的,是视图中question_id
在接收path
中的<int:question_id>
,如果你不能清楚也没关系,后续将内容添加后,可以帮助更容易的理解。
使用视图
结合polls
这个项目,我们给polls/views.py
中的index
视图函数进行修改一下,利用数据库API来查询和展示前五个投票问题
from django.http import HttpResponse
from .models import Question
def index(request):
# 利用数据库API来获取前五个问题
latest_question_list = Question.objects.order_by('-pub_date')[:5]
# 将内容提取出来,使用','符号拼接起来
output = ', '.join([q.question_text for q in latest_question_list])
# 使用HttpResponse返回拼接后的内容
return HttpResponse(output)
创建静态文件
上面的代码可以展示前五个问题,但是这时的展示是没有经过渲染的,所以我们给它们加上模板文件进行渲染,Django中提供了对应的寻找目录,即在polls
下创建一个template
文件夹,再在这个文件夹下创建一个polls
的文件夹,最后在这个文件里创建index.html
文件,完整的路径就是polls/template/polls/index.html
,这个是在setting.py
文件中TEMPLATES
选项设置的。
然后,我们可以将以下内容,填充到刚创建的模板文件(polls/template/polls/index.html
)中
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
最后,我们通过修改polls/views.py
来使用模板渲染
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
# 利用数据库API来获取前五个问题
latest_question_list = Question.objects.order_by('-pub_date')[:5]
# 指定渲染使用的模板
template = loader.get_template('polls/index.html')
# 指定问题列表
context = {
'latest_question_list': latest_question_list,
}
# 将问题列表传到模板中,然后通过HttpResponse返回响应
return HttpResponse(template.render(context, request))
快捷函数:render()
以上代码中,载入模板-填充模板上下文-生成HttpResponse对象,这一套流程使用太过于频繁,所以Django提供了一个render()
函数来减少冗余,我们把上面的代码通过render()
来重写
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
抛出404
访问404,也是很常用的一个操作,当我们访问不存在的一个问题ID时,就抛出404异常
from django.http import Http404
from django.shortcuts import render
from .model import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Qestion.DoesNotExist:
raise Http404("Question does not exist.")
return render(request, 'polls/detail.html', {'question': question})
# ...
此时,这个detail
视图使用到了一个模板文件,但是我们没有创建,所以现在无法正常运行,我们先创建一个,进行占位,即polls/template/polls/detail.html
{{ question }}
快捷函数:get_object_or_404()
尝试使用get()
函数获取一个对象,如果不存在就抛出Http404异常也是一个普遍的流程,对此,Django也提供了一个快捷函数,所以我们利用这个快捷函数来修改一个detail
视图
from django.shortcuts import get_object_or_404, render
from .model import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
# ...
优化模板
我们来补全polls/detail.html
模板文件
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
模板中通过点来访问变量的属性,即{{ question.question_text }}
在{ % for % }
循环中发生的函数调用:question.choice_set.all
被解释为Python代码中的question.choice_set.all()
,将会返回一个可迭代的Choice对象,这个对象被for循环输出
去除硬编码
向上翻,可以看到polls/index.html
中的投票链接是硬链接
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
这个问题在于,硬链接和强耦合相链接,对于一个包含多个应用的项目,修改起来比较复杂,所以我们可以使用在polls/urls.py
中为URL定义的name参数来替代原有的硬链接,使用{ % url %}
来使用
<li><a href="{% url 'detail' question_id %}">{{ question.question_text }}</a></li>
URL命名空间
命名空间的作用是,区分多个应用时,{% url %}
中name参数对应哪条URL,修改比较简单,就是在polls/urls.py
中添加app_name参数
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
所以还需要修改一下polls/index.html
文件
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>