自学python 进击之路 - Django-请求和响应

1,013 阅读11分钟

src=http___5b0988e595225.cdn.sohucs.com_images_20190916_32ce26032b124c3cb145a4231627373d.jpeg&refer=http___5b0988e595225.cdn.sohucs.jpg

请求和响应

定义

  • 请求是指浏览器端通过HTTPa协议发送给服务端的数据
  • 响应是指服务端接收到钱请求后做相应的处理后再回复浏览器端的数据

Snipaste_2021-07-10_13-39-07.png

请求-请求样例

POST /v1/tokens HTTP/1.1    -> 起始行

HOST: 127.0.0.1:8000    --> headers
User-AgentMozilla/5.0 (x11; ubuntu; Linux x86_64;rv74.0) Gecko/20100101 Firefox/74.0
Accept:* /*
Accept-Language: zh-CN , zh;q=0.8, zh-TW;q=0.7, zh-HK; q=0.5, en-US;q=0.3, en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type : application/json
Content_Length:58
Origin:http://127.0.0.1:7000
Connection:keep-alive
Referer: http://127.0.0.1:7000/dadashop/templates/logoin.html


{'username':'guoxiaonao'}   --> body

请求中的方法

  • 根据HTTP标准,HTTP请求可以使用多种请求方法
  • HTTP1.0定义为了三种请求方法:GET,POST 和HEAD方法(最常用)
  • HTTP1.1 新增了五种请求方法:OPTIONS,PUT, DELETE,TRACE和CONNECT方法。

Snipaste_2021-07-15_13-14-28.png

Django中的请求

  • 请求在Django中实测就是视图函数的第一个参数,即HttpRequest对象
  • Django接收到http协议的请求后,会根据请求数据报文创建HttpRequset对象
  • HttpRequest对象通过属性描述了请求的所有相关信息
  • path_info:URL字符串
  • method:字符串,表示HTTP请求方法,常用值:'GET'、'POST'
    • GET:QueryDict查询字典的对象,包含1get请求方式的所有数据
    • POST: QueryDict查询字典的对象,包含post请求方式的所有数据
# 我们先看看path 与 method 是什么,我们去打印看看
# views.py
def test_request(request):
    print('path info is', request.path_info)
    print('method is', request.method)

    return HttpResponse('test request ok')
    
# urls.py
 path('test_request/', views.test_request)
  • 终端的显示

Snipaste_2021-07-10_18-53-48.png

  • 打印一下我们这个GET
# views.py
    print('querystring is', request.GET)

Snipaste_2021-07-10_19-15-49.png

  • 给它添加一点参数看看打印出什么

Snipaste_2021-07-10_19-33-55.png

  • FILES:类型于字典的对象,包含所有的上传文件信息
  • COOKIES:Python字典,包含所有的cookie, 键和值都为字符串
  • session:似于字典的对象,表示当前的会话
  • body:字符串,请求体的内容(POST或PUT)
  • scheme:请求协议('http'/ 'https')
  • request.get_full_path():请求的完整路径
    • 让我们打印看看这个方法
# views.py
 print('full path is', request.get_full_path())
  • 刷新浏览器观察终端

Snipaste_2021-07-10_20-07-26.png

  • request.META:请求中的元数据(消息头)
    • request.META['REMOTE_ADDR']:客户端IP地址

响应-响应状态

  • HTTP状态码的英文为HTTP Status Code
    • 下面是常见的HTTP状态码:
    1. 200 - 请求成功
    2. 301-永久重定向-资源(网页等)被永久转移到其他URL
    3. 302-临时重定向
    4. 404-请求的资源(网页等)不存在
    5. 500- 内部服务器错误

响应-Django中的响应对象

  • 构造函数格式:

    • HttpResponse(content= 响应体, content_type=响应体数据类型, status=状态码)
    • 作用:向客户端浏览器返回响应,同时携带响应体内容
  • 常用的Content-Type如下

    • 'text/html'(默认的, html文件)
    • 'text/plain' (纯文本)
    • 'text/css' (css文件)
    • 'text/javascript' (js文件)
    • 'multipart/form-data' (文件提交)
    • 'application/json' (json传输)
    • 'application/xml' (xml文件)
  • HttpResponse子类

Snipaste_2021-07-15_13-29-28.png

GET请求和POST请求

定义

  • 无论是GET还还是POST, 统一都由视图函数接收请求,通过判断request.method 区别具体的请求动作
  • 样例
if request.method == 'GET':
   处理GET 请求时的业务逻辑
elif request.method == 'POST':
   处理POST请求的业务逻辑
else:
    其他请求业务业务逻辑

GET处理

  • GET请求动作, 一般用于向服务器获取数据
  • 能够产生GET请求的场景:
    • 浏览器地址栏中输入URL,回车后
    • < a href='地址?参数=值&参数=值'>
    • form表单中的method为get
  • GET请求方式中。如果有数据需要传递给服务器,通常会用查询字符串(Query String)传递 【注意:不要传递敏感信息】
  • URL 格式:xxx?参数名1= 值1&参数2=值2...
例如:http://127.0.0.1:8000/page1?a=100&b=200
  • 服务器端接收参数
    • 获取客户端请求GET请求提交的数
  • 方法示例:
request.GET['参数名']    #QueryDict

request.GET.get('参数名','默认值')

request.GET.GET.getlist('参数')

POST处理

  • POST请求动作,一般用于向服务器提交大量、隐私数据
  • 客户端通过表单等POST请求将数据传递给服务器端如:

Snipaste_2021-07-12_18-54-14.png

  • 服务器端接收参数
# 通过request.method 来判断是否为POST请求:如
# POST 请求
if request.method == 'POST':
    处理POST请求的数据并响应
else:
    处理非POST请求的响应 
  • 使用post方式接收客户端数据
request.POST['参数名']    #QueryDict

request.POST.get('参数名''')

request.POST.getlist('参数名')


注意点:在settings.py 里面取消csrf验证,否则Django将会拒绝客户端发来的POST请求,报403响应

  1. 我们先去简单粗暴的去settings.py注释代码

Snipaste_2021-07-12_20-35-23.png 2. 然后定义全局变量

# views.py
POST_FORM = '''
<form method='post' action='/test_get_post'>
      用户名:<input type='text' name='uname'>
      <input type='submit'  value='提交'>
</form>
'''
  1. 使用post方式

Snipaste_2021-07-12_20-41-04.png 4.测试

Snipaste_2021-07-12_20-52-27.png

Django的设计模式及模板层

MVC和MTV

传统的MVC
  • MVC代表Model-view-Controller(模型-视图-控制器)模式
    • M模型层(Model), 主要用于对数据库层的封装
    • V视图层(view), 用于向用户展示效果(WHAT+ HOW)
    • C控制(Controller), 用于处理请求、 获取数据、 返回结果(重要)
  • 作用:降低模块间的耦合度(解耦)

Snipaste_2021-07-12_22-00-45.png

Django的MTV模型

  • MTV代表Model-Template-View(模型-模板-视图) 模式
    • M模型层(model), 负责与数据库交互
    • T模型层(Template)负责呈现内容到浏览器(How)
    • V视图层(View)是核心,负责接收请求,获取数据、返回结果(WHAT)
模板配置
  • 创建模板文件夹<项目名>/templates
  • 在setting.py中TEMPLATES配置项
    • BACKEND:指定模板的引擎
    • DIRS:模板的搜索目录(可以是一个或多个)
    • APP_DIRS:是否要在应用中的templates文件夹中搜索模板文件
    • OPTIONS:有关模板的选项
  • 配置项中 需要修改部分
    • 设置DIRS-'DIRS':[os.path.join(BASE_DIR,'templates')
什么是模板
  • 模板是可以根据字典数据动态变化的html网页
  • 模板可以根据视图中传统传递的字典数据动态生成相应的HTML网页
模板的加载方式
  • 方案一-通过loader获取模板,通过HttpResponse进行响应在视图函数中
from django.template import loader
# 1.通过loade加载模板
t = load.get_template('模板文件名字')
# 2.将t转换成HTML字符串
html = t.render(字典数据)
# 3.用响应对象转换的字符串内容返回浏览器
return HttpResponse(html)
案例演示
  • 1.在项目中创建一个文件夹

Snipaste_2021-07-13_15-08-31.png

  • 2. 定义视图
# views.py
def test_html(request):
    from django.template import loader
    t = loader.get_template('test_html.html')
    html = t.render()
    return HttpResponse(html)

Snipaste_2021-07-13_15-02-42.png

  • 3. 创建HTML文件

Snipaste_2021-07-13_15-02-32.png

  • 4. 定义url
 # urls.py
 path('test_html', views.test_html)
    1. 运行
python manage.py runserver 8888

Snipaste_2021-07-13_15-02-12.png

加载方式:方案二
  • 使用render()直接加载并响应模板在视图函数中:
form django.shortcuts import render
return render(request, '模板文件名', 字典数据)

Snipaste_2021-07-13_15-40-32.png

视图层与模板层之间的交互
  • 1.视图函数中可以将Python变量封装到字典中传递到模板
def xxx_view(requset)
  dic = {
     "变量1""值1",
     "变量2":"值2"
   }
   return render (request, 'xxx.html', dic)

Snipaste_2021-07-13_15-58-41.png

  • 2. 模板中,我们可以用{{变量名}}的语法调用视图传进来的变量

Snipaste_2021-07-13_15-59-22.png

模板层-变量和标签

模板层-变量

def xxx_view(requset)
  dic = {
     "变量1""值1",
     "变量2":"值2"
   }
   return render (request, 'xxx.html', dic)
  • 能传递到模板中的数据类型
    • str - 字符串
    • list - 数组
    • int - 整型
    • tuple - 元祖
    • dict - 字典
    • func - 方法
    • obj - 类实例化的对象
  • 在模板中使用变量语法
    • {{变量名}}
    • {{变量名.index}}
    • {{变量名.key}}
    • {{对象.方法}}
    • {{函数名}}
  • 样例
    • 定义视图
# views.py
def test_html_param(request):
    dic = {}
    dic['int'] = 88
    dic['str'] = 'mydjango'
    dic['list'] = ['tom', 'jack', 'lily']
    dic['dict'] = {'a': 9, 'b': 8}
    dic['func'] = say_hi
    dic['class_obj'] = Dog()
    return render(request, 'test_html_param.html', dic)

# 定义了一个方法
def say_hi():
    return 'hahaah'

# 定义了一个类
class Dog:
    def say(self):
        return 'wangwang'
  • 创建html文件test_html_param
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>int 是 {{int}}</h3>
<h3>str 是 {{str}}</h3>
<h3>list 是 {{list}}</h3>
<h3>list 是 {{list.0}}</h3>
<h3>dict 是 {{dict}}</h3>
<h3>dict['a'] 是 {{dict.a}}</h3>
<h3>function 是 {{func}}</h3>
<h3>class_obj 是 {{class_obj.say}}</h3>

</body>
</html>
  • 定义路由
# urls.py
  path('test_html_param', views.test_html_param)
  • 页面显示效果

Snipaste_2021-07-13_18-51-56.png

模板层-标签

  • 作用:将一些服务器端的功能嵌入到模板中。例如流程控制等
  • 标签语法
{% 标签 %}
.....
{% 结束标签 %}
  • if标签
    • 语法
{% if 条件表达式1  %}
...
{% elif 条件表达式2  %}
....
{% elif 条件表达式3  %}
...
{% else %}
....
# 要注意这个endif的结束语
{% endif %}

注意

  1. if条件表达式里可以用的运算符 == , !=, <, >, <=,>=,in,notin, is , is not, not ,and ,or
  2. 在if标记中使用实际括号是无效的语法。如果你需要他们指示优先级,则应使用嵌套的if标记
  • 样列
    • 创建视图
# views.py
def test_if_for(request):
    dic = {'x': 20}
    return render(request, 'test_if_for.html', dic)
  • 创建模板
# test_if_for.html
# 在templates文件里面创建
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% if x > 10 %}
今天好热呀
{% else %}
今天真的好热
{% endif %}

</body>
</html>
  • 路由
path('test_if_for', views.test_if_for),

模板标签-if标签-练习

  • 写一个简单的计算器页面,能够在服务端进行简单加减乘除计算

Snipaste_2021-07-13_20-10-58.png

  • 创建模板
# 创建mycal.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/mycal" method="post">
    <input type="text" name="x" value="{{ x }}">
    <select name="op">
            <!-- 这里算法是处理点击开始计算的时候显示所点算法-->
        <option value="add" {% if op == "add" %} selected{% endif %}> + 加 </option>
        <option value="sub"   {% if op == "sub" %} selected{% endif %}>- 减</option>
        <option value="mul"  {% if op == "mul" %} selected{% endif %}> * 乘</option>
        <option value="div"  {% if op == "div" %} selected{% endif %}> / 除</option>
    </select>
    <input type="text" name="y" value="{{ y }}">= <span>{{ result }}</span>
    <div>
        <input type="submit" value="开始计算">
    </div>
</form>
</body>
</html>
  • 创建视图
# views.py
def test_macal(request):
    if request.method == 'GET':
        return render(request, 'mycal.html')
    elif request.method == 'POST':
        # 处理计算
        x = int(request.POST['x'])
        y = int(request.POST['y'])
        op = request.POST['op']

        result = 0
        if op == 'add':
            result = x + y
        elif op == 'sub':
            result = x - y
        elif op == 'mul':
            result = x * y
        elif op == 'div':
            result = x / y

        return render(request, 'mycal.html', locals())
  • 配置路由
 path('mycal', views.test_macal),

模板标签- for标签

  • 语法
{% for 变量 in 可迭代对象 %}
... 循环语句
{% empty %}
... 可迭代对象无数据时填充的语句
{% endfor %}
  • 内置变量- forloop

Snipaste_2021-07-13_21-43-40.png

  • 例子
# views.py
def test_if_for(request):
    dic = {'x': 20, 'list': ['Tom', 'Jack', 'lily']}
    return render(request, 'test_if_for.html', dic)
# test_if_for
{% for name in list%}
<p>{{forloop.counter}}.{{name}}</p>
{% empty %}
 无数据传递
{% endfor %}

Snipaste_2021-07-13_22-03-21.png

模板层-过滤器和继承

模板过滤器

  • 定义:在变量输出时对变量的值进行处理
  • 作用:可以通过使用过滤器来改变变量的输出显示
  • 语法:{{变量 | 过滤器1:'参数值1' | 过滤器2:'参数值2'.....}}

常用过滤器

Snipaste_2021-07-14_14-22-28.png

  • 例子

Snipaste_2021-07-14_14-41-05.png

Snipaste_2021-07-14_14-50-01.png

模板的继承

  • 模板继承可以使父模板的内容内容重用,子模板直接继承父模板的全部并可以覆盖父模板中的相应的块
  • 语法-父模板中:
    • 定义父模板中的块block标签
    • 标识出那些在子模块是允许被修改的
    • block标签:在父模板中定义,可以在子模板中覆盖
  • 语法-子模板中:
    • 继承模板extends 标签(写在模板文件的第一行) 例如{% extends 'base.html' %}
    • 子模板 重写父模板中的内容块
{% block block_name%}
子模板块用来覆盖父模板中 block_name 块的内容
{% endblock block_name%}

案例

  • 创建视图
#viewsl.py

def base_view(request):
    return render(request, 'base.html')


def music_view(request):
    return render(request, 'music.html')


def sport_view(request):
    return render(request, 'sport.html')
  • 创建所需要的HTML页面

Snipaste_2021-07-14_16-23-28.png

  • 创建父模板
# 这里把base.html 作为父模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
  {% block mytitle %}
    <title>主页</title>
  {% endblock %}
</head>
<body>
 <a href="/music_index">音乐频道</a>
 <a href="/sport_index">体育频道</a>
<br>
{%  block  info %}
这是主页
{% endblock %}

<br>
<h3>有任何问题请联系xxxxxx</h3>
</body>
</html>
  • 子模板
# music.html && sport.html

{% extends 'base.html' %}


{% block mytitle %}
<title>音乐频道</title>
{% endblock%}


{%  block info%}
欢迎来到音乐频道
{% endblock %}
  • 配置路由

Snipaste_2021-07-14_16-45-35.png

  • 重写的覆盖规则
    • 不重写,将按照模板的效果显示
    • 重写。则按照重写效果显示
  • 注意
    • 模板继承是,服务器端的动态内容无法继承

url反向解析

url-代码中url出现位置

    1. 在模板(html)中
    • < a href='url'>超链接点击后页面跳转至url
    • from 表单的数据用post方法添加至url
    1. 中-302跳转 HttpRequestRedirec('url')将用户地址栏中的地址跳转到url

代码中url书写规范

  1. 绝对地址
http://127.0.0.1:8888/page/1
  1. 相对地址
  • '/page/1' - '/'开头的相对地址,浏览器会把当前地址栏里的协议,ip和端口加上这个地址,作为最终访问地址,即如果当前页面地址为http://127.0.0.1:8888/page/3; 当前相对地址最终结果为http://127.0.0.1:8888+/page/1
  • 'page/1' - 没有'/'开头的相对地址,浏览器会根据当前url的最后一个/之前的内容加上改相对地址作为最终访问地址,例如当前地址栏地址为http://127.0.0.1:8888/topic/detail; 则该相对地址最终结果为http://http://127.0.0.1:8888/topic/+ page/1
  • 案例
  • 定义视图
def test_url(request):
    return render(request, 'test_url.html')


def test_url_result(request):
    return HttpResponse('---test url res is ok--')
  • 创建test_url.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="http://127.0.0.1:8888/test_url_result">绝对地址</a>
<a href="/test_url_result">带'/'的相对地址</a>
<a href="test_url_result">不带'/'的相对地址</a>
</body>
</html>
  • 配置路由
path('test/url', views.test_url),
  path('test_url_result', views.test_url_result),
  • 点击第一个时

Snipaste_2021-07-14_20-40-10.png

Snipaste_2021-07-14_20-42-04.png

  • 点击第二个时

Snipaste_2021-07-14_20-43-50.png

Snipaste_2021-07-14_20-42-04.png

  • 点击第三个时

Snipaste_2021-07-14_20-49-22.png

Snipaste_2021-07-14_20-51-30.png

url反向解析

  • url反向解析是指在视图或模板中,用path定义的名称来动态查找或计算出相应的路由
  • path 函数的语法
    • path(route, views, name='别名')
    • path('page', views,page_view, name='page_url')
  • 根据path中的‘name = ’ 关键字传参给url 确定了个唯一确定的名字,在模板或视图中,可以通过这个名字反向推断出此url信息
  • 模板中-通过url标签实现地址的反向解析
{% url '别名' %}
{% url '别名' '参数值1' '参数值2' %}
ex:
{% url 'pagen' '400'%}
{% url 'pagen' age='18' name='gxn'%}
  • 例子
# urls.py
# 定义一个参数(正则) 给一个别名
 path(r'test_url_result/<int:age>', views.test_url_result, name='tr'),


# test_url.html 
<a href='{% url 'tr' '100' %}'> url反向解析</a>

  • 实现的效果

Snipaste_2021-07-14_21-42-19.png

  • 在视图函数中-> 可调用django中的reversea方法进行反向解析
from django.urls import reverse
reverse('别名', args=[], kwargs={}) 
ex:
print(reverse('pagen', args=[300]))
print(reverse('person', kwargs={'name': 'xixi', 'age': 18}))
  • 例子
# url.py
# 给别名
  path('base_index', views.base_view, name='base_index'),
  
  
# views.py
def test_url_result(request, age):
    # 302 跳转
    from django.urls import reverse
    url = reverse('base_index')
    return HttpResponseRedirect(url)
    # return HttpResponse('---test url res is ok--')
  • 运行的效果

Snipaste_2021-07-14_22-10-08.png