续上一篇,这一篇 着重于创建公共接口------"视图"
第三部分------3、视图和模板
- 1、概述
- 2、编写更多视图
- 3、写一个真正有用的视图
-
- [一个快捷函数 render() render------渲染](#一个快捷函数 render() render——渲染)
- 4、抛出404错误
-
- [一个快捷函数 get_object_or_404()](#一个快捷函数 get_object_or_404())
- 5、使用模板系统
- 6、去除模板中的硬编码URL
- 7、为URL名称添加命名空间
1、概述
- Django中视图------一类具有相同功能和模板 的网页的集合
- 网页和其他内容由视图派生而来。
- 视图是一个python函数or方法
- 根据用户请求的URL(域名之后的部分)来选择使用哪个视图
- 将URL与视图关联起来------URLconfs(将URL映射到视图)来配置
2、编写更多视图
在 polls/views 增加
python
def detail(request,question_id):
return HttpResponse("You're loooking 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)
将视图添加进 polls/urls 模块中
python
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/34/"它将运行detail()函数,显示为:
同理访问"/polls/2/results/",或者访问"/polls/2/vote/":将看到:
原理------django依次访问了什么文件
- 访问根urls.py文件,即ROOT_URLCONF,按顺序遍历这些模式,匹配path("polls/",include("polls.urls")),并将剩余的文本发送给polls.urls
- Django 会匹配 path('int:question_id/', views.detail, name='detail'),其中 'int:question_id/' 表示将URL路径的这一部分解析为整数,并作为 question_id 参数传递给 detail 视图函数。
- 在polls/views,detail 函数接收两个参数:request 和 question_id。当你访问 /polls/34/ 时,question_id 的值为 34,因此视图函数会返回包含 "You're looking at question 34." 的响应。
总结:
1、Django加载项目的URL配置:mysite/urls.py。
2、匹配到包含的应用URL配置:polls/urls.py。
3、解析URL路径参数并调用相应视图函数:将 question_id 解析为 34,并调用 detail 视图函数。
4、视图函数处理请求并返回响应:detail 函数生成并返回响应。
3、写一个真正有用的视图
视图必须做的只有两件事:返回一个包含被请求页面的HttpResponse对象。或者抛出一个异常。
视图还可以做什么?视图可以从数据库读记录,可以使用一个模板引擎,,可以生成一个pdf文件,可以输出一个xml,可以创建一个zip文件,可以做任何你想做的事情。
接下来修改polls/views/index()函数,显示最近的五个问题
python
def index(request):
latest_question_list=Question.objects.order_by("-pub_date")[:5]
output=",".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
- 这里有一个问题,页面的代码写死在了视图函数里,这里解释一下什么叫"写死",以及它的弊端
当我们说页面的设计"写死"在视图函数里时,意思是视图函数中包含了生成页面内容的具体代码 。如果你想修改页面的外观或布局,就必须直接编辑这些视图函数。这种做法的问题在于:
1、难以维护和更新:每次需要修改页面的外观或布局时,都必须直接更改视图函数中的代码,可能导致代码变得复杂和难以维护。
2、代码与内容耦合:视图函数不仅负责处理请求逻辑,还负责生成HTML内容,使得业务逻辑和页面设计耦合在一起 。
3、复用性差:如果不同的视图函数需要共享相同的HTML结构或样式,每个视图函数都必须重复相同的HTML代码,导致代码重复和不易复用。
所以要使用Django的模板系统,只需要创建一个视图,就可以将页面的设计从代码里分离出来
首先,创建polls/templates目录。Django会在这个目录里查找模板文件
接着创建templates/polls/index.html
- 避免模板文件名称冲突,我们应该将模板文件放在与应用同名的子文件夹中。这样做的好处是,如果你有多个应用程序,它们可以使用相同名称的模板文件而不会互相冲突
- 命名空间:为了帮助Django区分不同应用的模板,我们将模板文件放在与应用同名的子文件夹中。这就是所谓的命名空间。例如,对于 polls 应用,我们应该在 polls/templates 目录下再创建一个 polls 子目录。
在index.html文件中:
python
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Polls Index</title>
</head>
<body>
{% 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 %}
</body>
</html>
python
<ul>:表示无序列表(unordered list)。通常用于列出项目,每个项目由 <li>(列表项)标签表示。
<li>:表示列表中的项目(list item)。在无序列表 <ul> 或有序列表 <ol> 中使用。
<a>:表示超链接(anchor)。用于创建链接,可以导航到其他页面或资源。href 属性指定链接目标。
<p>:表示段落(paragraph)。用于定义文档中的段落,浏览器会自动在段落前后添加一些空白,以便段落与其他内容分开。
{{ }}:Django模板语法,用于输出变量的值。例如,{{ question.id }} 输出问题的ID,{{ question.question_text }} 输出问题的文本。
{% %}:Django模板标签,用于执行逻辑语句或控制流。例如,{% if latest_question_list %} 检查 latest_question_list 是否有数据,{% for question in latest_question_list %} 循环遍历 latest_question_list 中的每个问题,{% endif %} 结束 if 语句,{% endfor %} 结束 for 循环。
接下来更新polls/views.py中的Index视图
python
def index(request):
latest_question_list=Question.objects.order_by("-pub_date")[:5]
template= loader.get_template("polls/index.html")
context={
"latest_question_list": latest_question_list,
}
return HttpResponse(template.render(context,request))#模板渲染
一个快捷函数 render() render------渲染
载入模板,填充上下文,再返回由它生成的HttpResponse对象
这是一个非常常见的操作流程,于是Django提供了一个快捷函数,我们用它来重写index()视图
python
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)
此时,就不再需要导入loader和HttpResponse啦
4、抛出404错误
接下来我们来处理 投票详情页面------它会显示指定投票的问题标题
python
from django.http import Http404
def detail(request,question_id):
try:
question=Question.objects.get(pk=question_id)
except Question.DoesNotExist:#当指定问题ID所对应的问题不存在,这个视图就会抛出一个Http404错误
raise Http404("Question does not exist")
return render(request,"polls/detail.html",{"question":question})
一个快捷函数 get_object_or_404()
尝试用get函数来获取一个对象,如果不存在就抛出一个404错误
这是一个普遍的流程,Django也提供了一个快捷的函数,下面是修改的detail代码
python
from django.shortcuts import get_object_or_404
def detail(request,question_id):
question=get_object_or_404(Question,pk=question_id)
return render(request,"polls/detail.html",{"question":question})
使用辅助函数 get_object_or_404,而不是自己捕获ObjectDoesNotExist异常?为什么模型API不直接抛出ObjectDoesNotExist,而是抛出Http404呢?
这样会增加 模型层 和 视图层 的 耦合性。但是指导Django设计的最重要的思想之一就是要 保证松散耦合。yi==一些受控的耦合会被包含在django.shortcuts模块中。
5、使用模板系统
在 polls/detail.html 中:
python
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Polls Details</title>
</head>
<body>
<h1>{{question.question_text}}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
</body>
</html>
6、去除模板中的硬编码URL
在polls/index.html 中编写投票链接时,链接是硬编码
python
<li><a href="/polls/{{question.id}}/">{{question.question_text}}</a></li>
这种 硬编码、强耦合的方法的问题在于,在具有大量模板的项目中更改url变得更有挑战性。还记得在polls/urls模块中,path()函数中定义了name参数,你可以通过使用
{% url %} 模板标签来消除对url配置中定义的特定的URL路径的依赖
将其修改为:
python
<li><a href="{% url"detail"question.id %}">{{question.question_text}}</a></li>
回忆一下在polls.urls中:
如果你想改变投票详情页面的url 比如想改成polls/specifics/12/,你不需要在模板里(包括其他模板),只要在polls/urls.py里稍微修改一下就行:
python
path("specifics/<int:question_id>/",views.detail,name="detail"),
此时访问http://127.0.0.1:8000/polls/specifics/2/将看到
7、为URL名称添加命名空间
本教程中只有一个应用:polls,在实际中可能会有好几十个应用。那么如果两个应用出现重名的url时,Django怎么分辨呢?比如,polls应用有detail视图,另一个博客应用也有detail视图,Django如何知道 {% url %} 到底对应的是哪个应用呢,需要在两个地方做出声明:
1、在polls.urls中设置命名空间:
2、在模板标签处(index.html/a href)添加声明