Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板
Posted kaoa000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板相关的知识,希望对你有一定的参考价值。
HTTP请求中产生两个核心的对象:
http请求:HttpRequest对象
http响应:HttpResponse对象
所在位置django.http,前边用的reques就是HttpRequest对象。
HttpRequest对象的属性:
path:请求页面的全路径,不包括域名和端口
method:请求中使用的HTTP方法的字符串表示,全大写表示,如POST、GET等
GET:包含所有HTTP GET参数的类字典对象
POST:包含所有HTTP POST参数的类字典对象。服务器收到空的POST请求的情况也是可能发生的,即表单form通过HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用
if req.POST来判断是否使用了HTTP POST方法;应该使用 if req.method == 'POST'
COOKIES:包含所有cookies的标准python字典对象;keys和values都是字符串。
FILES:包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name=""/>标签中name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
filename:上传文件名,用字符串表示
content_type:上传文件的Content Type
content:上传文件的原始内容
user:是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前没有登陆,user将被初始化为django.contrib.auth.models.AnonymouseUser的实例。可以通过user的is_authenticated()方法来辨别用户是否登陆:if req.user.is_authenticaed();只有激活Django中的AuthenticationMiddleware时该属性才能用
session: 唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
HttpRequest对象的方法:get_full_path(),比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123
req.path得到的是:/index33
HttpResponse对象:
对于HttpRequest对象,是Django自动创建的,但是HttpResponse对象就必须自己创建。每个views请求处理方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse
在HttpResponse对象上扩展的常用方法有:
页面渲染:render(),render_to_response()
页面跳转:redirect()
locals():可以直接将函数中所有的变量传给模板
render()与redirect()的区别,render是直接渲染一个页面文件,而redirect是重新提交一个请求路径。
测试:
登录页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎登陆!!</h1>
msg
<form action="/app01/login/" method="post">
<input type="submit" value="submit">
</form>
</body>
</html>
路由:
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
]
视图函数:
from django.shortcuts import render,HttpResponse,redirect,reverse
from pathlib import Path
import os
import datetime
from app01 import models
def login(req):
msg = ""
if req.method == "GET":
msg = "经过了if逻辑判断!"
return render(req,"login.html","msg":msg)
if req.method == "POST":
login_fail = 1
if login_fail:
return render(req,"login.html","msg":msg)
# return redirect("/app01/login/")
第一次进入:
视图函数使用render,点击submit按钮,这时只是渲染了login.html页面,这时的结果 :
没有经过if逻辑判断,直接渲染的login.html, 所以,msg是空,注意,这里的空,不是msg=“”赋值的,而是根本没有定义,渲染就没有经过msg=""这里。
视图函数使用redirect,点击submit,使用redirect是提交的路径,相当于又走了一遍请求,经过了if
模板:Template和Context
不使用模板直接将HTML硬编码到视图里,也可以实现内容反馈前端,但是却并不是一个好主意。 主要原因:
1、对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
2、Python 代码编写和 HTML 设计是两项不同的工作,由不同的人员(或部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
3、程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
将页面的设计和Python的代码分离开会更干净简洁更容易维护,于是出现了模板
模板就是HTML+逻辑控制语句
对于前面views中的函数,最后的render(req,“index.html”,“abc”:times),第二个参数叫做模板,第三个参数,即字典叫做Context上下文。
在Django中,模板和上下文都是一个对象,在Python交互窗口中演示:
视图文件中函数最后返回的: render(req,“index.html”,“abc”:times),实际就是先读取index.html模板文件,生成一个魔板对象,像上图中的t,然后将第三个参数“abc”:times封装成上下文对象,然后魔板对象调用render,使用上下文对象渲染模板。
一个模板可以反复使用多次,被不同的Context上下文渲染:
在Django中视图函数的各种写法:
def current_time(req):
# 原始的视图函数,视图函数的作用就是最后反馈内容给前端浏览器,实际就是反馈HttpResponse对象
now=datetime.datetime.now()
html="<html><body>现在时刻:<h1>%s.</h1></body></html>" %now
# 使用字符串格式化命令,替换处理后反馈给浏览器的内容
return HttpResponse(html)
def current_time(req):
# django模板修改的视图函数
now=datetime.datetime.now()
t=Template('<html><body>现在时刻是:<h1 style="color:red">current_date</h1></body></html>')
# t=get_template('current_datetime.html') 上面的写法可以将参数写在html文件中,然后以加载文件的方式生成模板,效率高,又进行了分离
c=Context('current_date':now)
html=t.render(c)
return HttpResponse(html)
#另一种写法(推荐)
def current_time(req):
now=datetime.datetime.now()
return render(req, 'current_datetime.html', 'current_date':now)
前面的例子 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
模板中万能的句点号:替换变量 变量名
列表,views中最后render的Context参数:“list”:[111,222,333],在模板中 list.2 返回列表的第三项
字典,views中最后render的Context参数:“dict”:“a”:111,“b”:222,在模板中 list.a 返回字典的a键的值
对象,views中最后render的Context参数:“obj”:times,times = datetime.datetime.now(),在模板中可以使用句点号返回时间的属性,如 obj.year 返回年
# app01项目下的urls.py
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
path('index/',views_app01.index),
]
# app01项目下的views_app01.py视图
from django.shortcuts import render
from pathlib import Path
import datetime
def index(req):
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
p1 = Person('张三',32)
times = datetime.datetime.now()
return render(req,"index.html","obj1":times,"obj2":p1)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> obj1 ------ obj1.year </h1>
<hr>
<h1> obj2.name ==> obj2.age </h1>
<br>
obj2
</body>
</html>
模板中的语句:
if语句:
% if %
% elif %
% else %
% endif %
for语句:
% for item in obj %
obj是列表,item是每项的值,想要取得索引,使用 forloop.counter ,默认counter是从1开始的
obj3=["a","b","c"]
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
% for item in obj3 %
forloop.counter ===> item <br>
% endfor %
<hr>
% for item in obj3 %
forloop.counter0 ===> item <br>
% endfor %
<hr>
% for item in obj3 %
forloop.revcounter ===> item <br>
% endfor %
<hr>
% for item in obj3 %
forloop.revcounter0 ===> item <br>
% endfor %
</body>
</html>
如果obj3是字典,遍历的是Key,想要值需要使用句点号。
模板中的过滤器filter:使用管道符|
后台obj=“hello”
模板 obj | upper 即可将小写变大写。lower、first 、capfirst、default等等。
# 1 add : 给变量加上相应的值
# 2 addslashes : 给变量中的引号前加上斜线
# 3 capfirst : 首字母大写
# 4 cut : 从字符串中移除指定的字符
# 5 date : 格式化日期字符串
# 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
# 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
#value1="abcDe"
value1|upper <br> #全部转换为大写ABCDE
#value2=5
value2|add:3 <br> #5+3=8
#value3='he llo wo r ld'
value3|cut:' ' <br> #去掉空格helloworld
#import datetime
#value4=datetime.datetime.now()
value4|date:'Y-m-d' <br> #格式化日期
#value5=[]
value5|default:'空的' <br> #如果为空,就输出default后的值
#value6='<a href="#">跳转</a>'
value6 #整体作为字符串<a href="#">跳转</a>
% autoescape off %
value6 #字符串作为html代码,即作为了标签<a>
% endautoescape %
value6|safe <br> #等同于上面,作为标签<a>
value6|striptags
#value7='1234'
value7|filesizeformat <br> #将1234作为数字换算为KB或MB等表示
value7|first <br> #第一个字符
value7|length <br> #长度
value7|slice:":-1" <br> #切片
#value8='http://www.baidu.com/?a=1&b=3' #转换为urlencode编码,http%3A//www.baidu.com/%3Fa%3D1%26b%3D3
value8|urlencode <br>
# value9='hello I am yuan'
value9|truncatechars:'6' <br> #按字符进行截取
value9|truncatewords:'2' #按字(单词)进行截取
% csrf_token %:
当form表单提交的时候,如果中间件,即在settings.py中的MIDDLEWARE中,'django.middleware.csrf.CsrfViewMiddleware',没有被禁用,form提交后页面会出现Forbidden错误
在form表单中增加% csrf_token %:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎登陆!!</h1>
<form action="/app01/login/" method="post">
<input type="text" name="username">
<input type="text" name="pass">
<input type="submit" value="submit">
% csrf_token %
</form>
</body>
</html>
渲染模板时,会自动生成一个input标签,type为hidden,value是一个随机的长字符串,这个键值对会被后台保存,在前端submit提交时会一同提交,后台(实际上应该是中间件django.middleware.csrf.CsrfViewMiddleware)获取并进行比较,一致就认为是安全的。生成的csrf_token标签,用于防治跨站攻击验证。注意如果在view的index里用的是render_to_response方法,不会生效。
% url %: 引用路由配置的地址,% url "name的值" %,这里name的值就是urls中每个路由的name参数的值,如path(“login/”,views.login,name="abc"),这时,模板中% url "abc"%
% with %:用更简单的变量名替代复杂的变量名
% with total=fhjsaldfhjsdfhlasdfhljsdal % total % endwith %
% verbatim %: 禁止render,即禁止渲染% verbatim %
hello
% endverbatim %
在渲染后的结果是显示 hello 本身。
% load %: 加载标签库
学习这个之前,要了解自定义标签和过滤器
自定义filter和simple_tag:
1)、在app中创建templatetags模块(必须的)
2)、创建任意 .py 文件,如:my_tags.py
需要注意的是自定义标签不能使用在if语句的条件中,而过滤器可以。使用自定义标签和过滤器,必须在文件第一行使用% load mytag%引入相应模块,即相应的py文件。
extend模板继承:
问题的提出:多个页面,大部分内容相同,如下,只有中间右半部分内容有差异
<!--caidan1.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.page-top
height: 50px;
background-color: rosybrown;
.page-body .menu
height: 300px;
background-color: green;
float: left;
width: 20%;
.page-body .content
height: 300px;
background-color: palegoldenrod;
float: left;
width: 80%;
.page-footer
height: 30px;
background-color: yellow;
clear: both;
</style>
</head>
<body>
<div class="page-top">页头部分</div>
<div class="page-body">
<div class="menu">
<a href="/app01/caidan1/">菜单一</a><br>
<a href="/app01/caidan2/">菜单二</a>
</div>
<div class="content">
菜单一内容部分
</div>
</div>
<div class="page-footer">页脚部分</div>
</body>
</html>
<!--caidan2.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.page-top
height: 50px;
background-color: rosybrown;
.page-body .menu
height: 300px;
background-color: green;
float: left;
width: 20%;
.page-body .content
height: 300px;
background-color: palegoldenrod;
float: left;
width: 80%;
.page-footer
height: 30px;
background-color: yellow;
clear: both;
</style>
</head>
<body>
<div class="page-top">页头部分</div>
<div class="page-body">
<div class="menu">
<a href="/app01/caidan1/">菜单一</a><br>
<a href="/app01/caidan2/">菜单二</a>
</div>
<div class="content">
菜单二内容部分
</div>
</div>
<div class="page-footer">页脚部分</div>
</body>
</html>
# urls.py
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views,views_app01
urlpatterns = [
path('new/story/',views.app_story),
path('login/',views_app01.login),
path('index/',views_app01.index),
path('caidan1/',views_app01.caidan1),
path('caidan2/',views_app01.caidan2),
]
# views_app01.py
from django.shortcuts import render,HttpResponse,redirect,reverse
from pathlib import Path
def caidan1(req):
return render(req,"caidan1.html")
def caidan2(req):
return render(req,"caidan2.html")
caidan1.html和caidan2.html代码有大量的重复,解决的方法是将重复部分抽取,写在一个公共的模板上,不同的部分使用% block 名称%取代:
<!-- base.html 公共的模板-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.page-top
height: 50px;
background-color: rosybrown;
.page-body .menu
height: 300px;
background-color: green;
float: left;
width: 20%;
.page-body .content
height: 300px;
background-color: palegoldenrod;
float: left;
width: 80%;
.page-footer
height: 30px;
background-color: yellow;
clear: both;
</style>
</head>
<body>
<div class="page-top">页头部分</div>
<div class="page-body">
<div class="menu">
<a href="/app01/caidan1/">菜单一</a><br>
<a href="/app01/caidan2/">菜单二</a>
</div>
% block content %
% endblock %
</div>
<div class="page-footer">页脚部分</div>
</body>
</html>
<!-- caidan1.html -->
% extends "base.html" %
% block content %
<div class="content">
菜单一内容部分
</div>
% endblock %
<!-- caidan2.html -->
% extends "base.html" %
% block content %
<div class="content">
菜单二内容部分
</div>
% endblock %
公共部分的内容只写一份放在base.html中,在caidan1.html和caidan2.html中通过% extend %引入母版,将不同的部分用% block % % endblock%包围起来,以进行替换。
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过 % include % 支持了这种方法。 但是 Django 解决此类问题的首选方法是使用更加优雅的策略,即上面说的—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。要比include方法灵活。
以上是关于Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板的主要内容,如果未能解决你的问题,请参考以下文章
Python入门自学进阶-Web框架——20Django其他相关知识2
Python入门自学进阶-Web框架——3Django的URL配置
Python入门自学进阶-Web框架——21DjangoAdmin项目应用