转载自:http://www.cnblogs.com/liwenzhou/p/8258992.html

一、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请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type表明响应的内容格式。如 text/html表示HTML网页。

HTTP GET请求的格式:

GET /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n

使用 \r\n分隔多个header

HTTP POST请求格式:

POST /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n
\r\n\r\n
请求体...

当遇到 \r\n\r\n时,表示Header部分结束了,后面的数据是Body。

HTTP响应的格式:

200 OK
Header1:v1\r\n
Header2:v2\r\n
\r\n\r\n
响应体...

让我们的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\r\n\r\n")
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开发环境用的就是这个模块来做服务器。

from wsgiref.simple_server import make_server

def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')]) # 设置HTTP响应的状态码和头信息
return [bytes("<h1>Hello world!</h1>", encoding="utf8"),] if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print("Serving HTTP on port 8000...")
httpd.serve_forever()

这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?

小事一桩,我们可以从请求相关数据里面拿到请求的URL,然后做一个判断。

from wsgiref.simple_server import make_server

def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
if url == "/index/":
return [bytes("<h1>这是index页面</h1>", encoding="utf8"), ]
elif url == "/home/":
return [bytes("<h1>这是home页面</h1>", encoding="utf8"), ]
else:
return [bytes("404没有该页面", 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"), ] 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响应的状态码和头信息
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()

完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?

没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我可以打开HTML文件,读取出它内部的二进制数据,然后发送给浏览器。

from wsgiref.simple_server import make_server

def index():
with open("index.html", "rb") as f:
data = f.read()
return [data, ] 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()

这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。

from wsgiref.simple_server import make_server

def index():
with open("index.html", "rb") as f:
data = f.read()
import time
time_str = str(time.time())
data_str = str(data, encoding="utf8")
data_str = data_str.replace("@@a@@", time_str)
return [bytes(data_str, 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中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

下载jinja2:

pip install 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>{{hobby}}</li>
{% endfor %}
</ul>
</body>
</html>

index2.html文件

使用jinja2渲染index2.html文件:

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()

现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?

使用pymysql连接数据库:

conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select name, age, department_id from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close()

创建一个测试的user表:

CREATE TABLE user(
id int auto_increment PRIMARY KEY,
name CHAR(10) NOT NULL,
hobby CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

二、Django

Django官网下载页面

1.安装(安装最新LTS版):

pip3 install django==1.11.9

2.创建一个django项目:

下面的命令创建了一个名为"mysite"的Django 项目:

django-admin startproject mysite

3.目录介绍

mysite/
├── manage.py # 管理文件
└── mysite # 项目目录
├── __init__.py
├── settings.py # 配置
├── urls.py # 路由 --> URL和函数的对应关系
└── wsgi.py # runserver命令就使用wsgiref模块做简单的web server

4.运行Django项目:

python manage.py runserver 127.0.0.1:8000

5.模板文件配置:

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "template")], # template文件夹位置
'APP_DIRS': True,
'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',
],
},
},
]

6.静态文件配置:

STATIC_URL = '/static/'  # HTML中使用的静态文件夹前缀
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), # 静态文件存放位置
]

看不明白?有图有真相:

刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试。

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

三、Django基础必备三件套:

from django.shortcuts import HttpResponse, render, redirect

1.HttpResponse

内部传入一个字符串参数,返回给浏览器。

例如:

def index(request):
# 业务逻辑代码
return HttpResponse("OK")

2.render

除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。

将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)

例如:

def index(request):
# 业务逻辑代码
return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})

3.redirect

接受一个URL参数,表示跳转到指定的URL。

例如:

def index(request):
# 业务逻辑代码
return redirect("/home/")

Python学习(二十六)—— Django基础一的更多相关文章

  1. python 学习笔记十五 django基础

    Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. ...

  2. python 学习笔记十六 django深入学习一 路由系统,模板,admin,数据库操作

    django 请求流程图 django 路由系统 在django中我们可以通过定义urls,让不同的url路由到不同的处理函数 from . import views urlpatterns = [ ...

  3. Python学习二十八周(vue.js)

    一.指令 1.一个例子简单实用vue: 下载vue.js(这里实用1.0.21版本) 编写html代码: <!DOCTYPE html> <html lang="en&qu ...

  4. Java开发学习(二十六)----SpringMVC返回响应结果

    SpringMVC接收到请求和数据后,进行了一些处理,当然这个处理可以是转发给Service,Service层再调用Dao层完成的,不管怎样,处理完以后,都需要将结果告知给用户. 比如:根据用户ID查 ...

  5. Python学习(十六)—— 数据库

    一.数据库介绍 数据库(Database,DB)是按照数据结构来组织.存储和管理数据的,并且是建立在计算机存储设备上的仓库. 数据库指的是以一定方式存储在一起.能为多个用户共享.具有尽可能小的冗余度. ...

  6. Python 学习第十九天 django知识

    一,django 知识总结 1,同一个name属性的标签,多个值获取 <form action="/login/" method="POST" encty ...

  7. python 学习笔记十九 django深入学习四 cookie,session

    缓存 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求一个页面,Web服务器将进行所有涵盖数据库查询到模版渲染到业务逻辑的请求,用来创建浏览者需要的页面.当程序访问量大时,耗时必然会更加明显, ...

  8. python 学习笔记十八 django深入学习三 分页,自定义标签,权限机制

    django  Pagination(分页) django 自带的分页功能非常强大,我们来看一个简单的练习示例: #导入Paginator>>> from django.core.p ...

  9. Python 学习 第十六篇:networkx

    networkx是Python的一个包,用于构建和操作复杂的图结构,提供分析图的算法.图是由顶点.边和可选的属性构成的数据结构,顶点表示数据,边是由两个顶点唯一确定的,表示两个顶点之间的关系.顶点和边 ...

  10. 笨办法学Python(二十六)

    习题 26: 恭喜你,现在可以考试了! 你已经差不多完成这本书的前半部分了,不过后半部分才是更有趣的.你将学到逻辑,并通过条件判断实现有用的功能. 在你继续学习之前,你有一道试题要做.这道试题很难,因 ...

随机推荐

  1. xl2tpd[26104]: Maximum retries exceeded for tunnel 33925. Closing

    Sep  5 14:31:50 root charon: 13[ENC] generating QUICK_MODE request 3930082374 [ HASH ]Sep  5 14:31:5 ...

  2. Kubernetes重要概念理解

    Kubernetes重要概念理解 kubernetes是目前最主流的容器编排工具,是下一代分布式架构的王者.2018年的kubernetes第一个版本1.10已经发布.下面整理一下,kubernete ...

  3. springboot 文件上传大小配置

    转自:https://blog.csdn.net/shi0299/article/details/69525848 springboot上传文件大小的配置有两种,一种是设置在配置文件里只有两行代码,一 ...

  4. 【原创】测试基础之http_load(1)简介、安装、使用

    http_load-09Mar2016官方:https://acme.com/software/http_load/ 一 简介 http_load - multiprocessing http tes ...

  5. 路由跟踪表满,日志报错nf_conntrack: table full, dropping packet.

    “连接跟踪表已满,开始丢包”!相信不少用iptables的同学都会见过这个错误信息吧,这个问题曾经也困扰过我好长一段时间.此问题的解决办法有四种(nf_conntrack 在CentOS 5 / ke ...

  6. login_code

    #! -*- coding:utf-8 -*-"""http://www.cnblogs.com/weke/articles/6271206.html前置和后置1.set ...

  7. Confluence 6 匿名访问远程 API

    Confluence 管理员可能希望为匿名用户禁用远程访问 API.这样能够避免恶意软件随意在网站进行批量修改. 希望禁用远程访问 API: 在屏幕的右上角单击 控制台按钮 ,然后选择 General ...

  8. Java的家庭记账本程序(A)

    日期:2019.2.1 博客期:028 星期五 其实我早就开始开发“家庭记账本”的软件了,只不过写博客写的有点晚,我是打算先做web的!因为Android Studio的教程,还是要对应版本,好多问题 ...

  9. vue之指令

    一.什么是VUE? 它是构建用户界面的JavaScript框架(让它自动生成js,css,html等) 二.怎么使用VUE? 1.引入vue.js 2.展示HTML <div id=" ...

  10. laravel 多对多关联 attach detach sync

    用户表和角色表,多对多关联,一个用户有多个角色,一个角色属于多个用户 添加多对多关联 attach: 给1号用户添加1号角色,并把关联表的column字段赋值为$value,后边的数组需要的时候再添加 ...