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框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板