1、POST和GET是HTTP协议定义的与服务器交互的方法。GET一般用于获取/查询 资源信息,而POST一般用于更新 资源信息。还有另两种方法是PUT和DELETE
2、POST和GET都可以与服务器交互完成查、改、增、删的操作。 注意都是大写的 而且都是客户端对服务端的请求
<form> 标签的 action 属性
form表单是用于提交数据的, action就是将数据提交到哪儿
如 <form action="/index/" method="post"> 这个是将数据提交到 当前主网页的 /index/ 中去 方法是post
Web框架本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。
import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen(5) while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"OK") conn.close()
可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。
HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?
让我们首先看下我们在服务端接收到的消息是什么。
然后再看下我们浏览器收到的响应信息是什么。
响应头在浏览器的network窗口可以看到,我们看到的HTML页面内容就是响应体。本质上还是字符串,因为浏览器认识HTML,所以才会渲染出页面。
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type
表明响应的内容格式。如 text/html
表示HTML网页。
HTTP GET请求的格式:
GET /path HTTP/1.1 注意这里有一个空格
header1:v1
header2:v2
使用
分隔多个header
HTTP POST请求格式:
POST /path HTTP/1.1
header1:v1
header2:v2
请求体...
当遇到连续两个
时,表示Header部分结束了,后面的数据是Body。
HTTP响应的格式:
200 OK
Header1:v1
Header2:v2
响应体...
让我们的Web框架在给客户端回复响应的时候按照HTTP协议的规则加上响应头,这样我们就实现了一个正经的Web框架了。
import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8000)) sock.listen(5) while True: conn, addr = sock.accept() data = conn.recv(8096) conn.send(b"HTTP/1.1 200 OK ") 头部 conn.send(b"OK") 打印的信息 conn.close()
上述通过socket来实现了其本质。
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
wsgiref模块示例
from wsgiref.simple_server import make_server def index(): return "这是index页面" def home(): return "这是home页面" def login(): with open("login.html", encoding="utf8") as f: data = f.read() return data URL_FUNC = [ ("/index/", index), ("/home/", home), ("/login/", login), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')]) # 设置HTTP响应的状态码和头信息 url = environ['PATH_INFO'] # 取到用户输入的url print("--->url:", url) # 根据url的不同,返回不同的内容 func_name = None for i in URL_FUNC: if url == i[0]: # 如果能找到对应关系,就把函数名拿到 func_name = i[1] break # 拿到可以执行的函数,执行函数拿到结果 if func_name: body = func_name() else: body = "404找不到这个页面" return [bytes("<h1>{}</h1>".format(body), encoding="utf8"),] if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()
解决了不同URL返回不同内容的需求。 但是问题又来了,如果有很多很多页面怎么办?难道要挨个判断? 当然不用,我们有更聪明的办法。
from wsgiref.simple_server import make_server def index(): return [bytes("<h1>这是index页面</h1>", encoding="utf8"), ] #这里注意返回值的格式一定要设置成bytes格式,因为信息传送是要bytes格式 def home(): return [bytes("<h1>这是home页面</h1>", encoding="utf8"), ] # 定义一个url和函数的对应关系 URL_LIST = [ ("/index/", index), ("/home/", home), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息 返回的格式要是utf8
与返回的信息格式保持一致,才能翻译好 url = environ['PATH_INFO'] # 取到用户输入的url func = None # 将要执行的函数 for i in URL_LIST: if i[0] == url: func = i[1] # 去之前定义好的url列表里找url应该执行的函数 break if func: # 如果能找到要执行的函数 return func() # 返回函数的执行结果 else: return [bytes("404没有该页面", encoding="utf8"), ] #格式保持一值 utf8 if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()
依据客户端的位置选则性返回相应的网站内容
这里返回的是一个HTML文件组成的字符串,浏览器解析渲染出来网页
from wsgiref.simple_server import make_server def index(): with open("index.html", encoding="utf8") as f: data = f.read() 将写好的HTML文件读取出来写在变量里,用于稍后的返回给客户端的浏览器 # 连接数据库 import pymysql # 连接 conn = pymysql.connect(host="localhost", user="root", password="root1234", database="s8", charset="utf8") # 没有- # 获取光标 cursor = conn.cursor() # 写sql语句 sql = "select name, pwd from userinfo where id =1;" cursor.execute(sql) ret = cursor.fetchone() print(ret) # 将数据库中的数据填充到HTML页面 new1_data = data.replace("@@2@@", ret[0]) new2_data = new1_data.replace("##3##", ret[1]) return new2_data 吧信息填好,返回给浏览器的请求端,然后就能将从数据库中取出的内容都出来 def home(): return "这是home页面" def login(): with open("login.html", encoding="utf8") as f: data = f.read() import time time_str = str(time.time()) new_data = data.replace("@@2@@", time_str) return new_data URL_FUNC = [ ("/index/", index), ("/home/", home), ("/login/", login), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')]) # 设置HTTP响应的状态码和头信息 url = environ['PATH_INFO'] # 取到用户输入的url print("--->url:", url) # 根据url的不同,返回不同的内容 func_name = None for i in URL_FUNC: if url == i[0]: # 如果能找到对应关系,就把函数名拿到 func_name = i[1] break # 拿到可以执行的函数,执行函数拿到结果 if func_name: body = func_name() else: body = "404找不到这个页面" return [bytes("<h1>{}</h1>".format(body), encoding="utf8"),] if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()
其实模板渲染有个现成的工具: jinja2
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <h1>姓名:{{name}}</h1> <h1>爱好:</h1> <ul> {% for hobby in hobby_list %} 有几个爱好就打印几个Li标签 jinjia2主要是对这里进行渲染,让一些内容可以导入并进行替换中括号中的内容 <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
使用jinja2渲染index2.html文件: 这里用到了render
from wsgiref.simple_server import make_server from jinja2 import Template def index(): with open("index2.html", "r") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) # 把数据填充到模板里面 return [bytes(ret, encoding="utf8"), ] def home(): with open("home.html", "rb") as f: data = f.read() return [data, ] # 定义一个url和函数的对应关系 URL_LIST = [ ("/index/", index), ("/home/", home), ] def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息 url = environ['PATH_INFO'] # 取到用户输入的url func = None # 将要执行的函数 for i in URL_LIST: if i[0] == url: func = i[1] # 去之前定义好的url列表里找url应该执行的函数 break if func: # 如果能找到要执行的函数 return func() # 返回函数的执行结果 else: return [bytes("404没有该页面", encoding="utf8"), ] if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()
模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
Django
安装(安装最新LTS版):
pip3 install django==1.11.9 注意正常是pip install 但是有的时候给程序 即在cmd 中启动Python 时 用的名字是 Python3 所以给他装也加上那个序号
创建一个django项目:
新建Django项目: 命令行方式: > 先进入到你新建的项目要存放的目录 cd /d D:py上课内容day 61s8 > django-admin startproject s8 > cd s8 进入刚建成的项目中去 > python manage.py runserver PyCharm方式: File -> new project -> 选Django -> 起名字 -> 点右下角create
目录介绍:
mysite/ ├── manage.py # 管理文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
运行Django项目:
python manage.py runserver 127.0.0.1:8000
settings.py中的配置
模板文件配置: 这个在settings.py文件中
templates这个文件夹中只放 html 文件
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")], # template文件夹位置 当前学的是 'APP_DIRS': True, DIRS指的是目录 在setting中的有对它进行设置,
os.path.join 拼接路径, BADE_DIR当前文件的符路径的父路径 然后拼接上templates ,就是他的路径 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
静态文件配置:
static 也是新建一个文件夹,这里通常存放 css js 文件
这个在settings.py文件中
STATIC_URL = '/static/' # HTML中使用的静态文件夹前缀 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 静态文件存放位置 static是文件夹的名字 base_dir是 ]
看不明白?有图有真相:
刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试。
以免影响自己在本地试验
Django基础必备三件套:
from django.shortcuts import HttpResponse, render, redirect
HttpResponse
内部传入一个字符串参数,返回给浏览器。 也可以是标签对象
例如:
def index(request): # 业务逻辑代码 return HttpResponse("OK")
render
除request参数外还接受一个待渲染的模板文件(就是html文件)和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)
例如:
def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
redirect
接受一个URL参数,表示跳转到指定的URL。
例如:
def index(request): # 业务逻辑代码 return redirect("/home/")
跟request相关的方法:
request.method
request.POST
request.POST.get("key") --> 获取对应的值
新建Django项目三件事:
1.注释csrf那一行
2. 配置静态文件
3. 检查templates配置项
课后练习:
实现登录验证功能
from django.shortcuts import HttpResponse,render,redirect def index(request): #表示所有跟请求相关的数据都放在request中 # return HttpResponse(b'<h1>hello</h1>') error_msg='' print(request.method)#获得并打印当前请求的方法,在未点提交前方法是 GET 所以if 条件不成立, # 先执行最后一 渲染网页,当点击提交的时候 此时 request.method 的是POST所以执行判断登录信息 if request.method=="POST": #获取用户输入的用户名和密码,因为用户输入的信息在后端是以字典的形式保存的,所以直接get 键 获得值 name=request.POST.get('username') pwd=request.POST.get('password') print(name,pwd) #去数据库中去对比输入信息是否正确 import pymysql conn=pymysql.connect(host='localhost',user='root',password='123456',database='db2',charset='utf8') cursor=conn.cursor() sql='select name,psw from s8 where name=%s' ret=cursor.execute(sql,name) if ret==0: error_msg='无此用户' if ret==1: name1,psw=cursor.fetchone() cursor.close() conn.close() print(name1,psw,name,pwd) if ret==1: if psw==pwd: # redirect立即跳转到相应网站 return redirect('https://www.baidu.com/') # 否则给 error 赋值 密码错误, else: error_msg = '密码错误' return render(request,'denglu.html',{"error":error_msg}) #给客户端返回这个网站 urlpatterns = [ url(r'index/',index),#url中有 index/ 就执行 index ]
for 循环 和 if语句的使用
for循环的写法
开头: {% for class in calss_list %}
中间可以使用相应的值 如 <th scope="row">{{ class.id }}</th>
结尾: {% endfor %}
for循环详见
http://www.cnblogs.com/wangkun122/p/8278045.html
if语句的写法 如下
if 判断:
{% if student.cid == class.id %}
<option selected value="{{ class.id }}">{{ class.cname }}</option>
{% else %}
<option value="{{ class.id }}">{{ class.cname }}</option>
{% endif %}
编辑页函数代码
def edit(request): if request.method=='GET': name='' ret=request.GET.get('id') print(ret) conn = pymysql.connect(host='localhost', user='root', password='123456', database='db1', charset='utf8') cursor = conn.cursor(pymysql.cursors.DictCursor) sql = 'select id,cname from class where id=%s' cursor.execute(sql, ret) ret2 = cursor.fetchone() print(ret2) conn.commit() cursor.close() conn.close() name=ret2 return render(request,'eidt.html',{"name":name}) if request.method == 'POST': retid = request.POST.get('id') retname = request.POST.get('name') print(retid,retname) conn = pymysql.connect(host='localhost', user='root', password='123456', database='db1', charset='utf8') cursor = conn.cursor(pymysql.cursors.DictCursor) sql = 'update class set cname=%s where id=%s' cursor.execute(sql,(retname,retid)) # ret2 = cursor.fetchone() conn.commit() cursor.close() conn.close() return redirect('/classlist/')
编辑学生菜单操作 html端的写法 if else 语句
<select name="cid" id=""> #先用for循环取出每一个值 {% for class in class_list %} #然后再用if else 语句判断是否符合条件 {% if class.id == student_list.cid %} #符合条件的即编辑之前的id 和 班级名称,设为默认选中值 <option value="{{ class.id }}" selected>{{ class.cname }}</option> {% else %} <option value="{{ class.id }}">{{ class.cname }}</option> #以 endif 结尾 {% endif %} #结束for循环 {% endfor %} </select>
总结: 常与后端传过来的含有多个字典的列表配合使用
后端的 cursor = conn.cursor(pymysql.cursors.DictCursor) 这样是穿过去的数据是字典