前言
学习了 python 这么久,终于到了Django 框架。这可以说是 python 名气最大的web 框架了,那么从今天开始会开始从 Django框架的安装到使用一步步的学习,这系列博客不会像前端的那样水了(立个 flag),希望可以成为高质量的博客。那么本篇博客介绍 Django 的安装以及如何在电脑上运行第一个 Django 应用。
Django 的安装
Django 的安装很简单,在 win 和 mac 上面都可以使用 pip 安装命令安装,也可以通过 pycharm 安装,或者下载文件在命令行使用安装工具安装。
接下来我在 ubuntu 上安装做示例。
在这里还安装了一个依赖包 pytz。这是用来做时区转换的一个第三方库。
其他平台的 pip 安装方式一样,不过要选用 python3的对应 pip 来安装,因为现在的 Django 版本已经不支持 python2了。
虚拟环境的安装
什么是虚拟环境
对真实的 python 解释器的一个拷贝版本
事实有效,可以独立存在并运行解释 python 代码
可以在计算机上拷贝多个虚拟环境
为什么要使用虚拟环境
保证真实环境的纯净性
框架的多版本共存
方便做框架的版本迭代
降低多框架共存的维护成本
安装虚拟环境
通过 pip 安装虚拟环境库
因为我之前已经下载好了,所以这里直接显示请求已经完成,并且后面是安装的绝对路径。
前往目标文件夹
这个文件夹是你用来保存虚拟环境的文件夹,该文件夹一旦确定就不要轻易更改。
这个 py3-env1是创建的一个纯净虚拟环境。
创建纯净的虚拟环境
virtualenv 虚拟环境名 (py3-env2)
终端启动虚拟环境
cd py3-env1\Scripts activate
进入虚拟环境下的 python 开发环境
python3
关闭虚拟环境
deactivate
Pycharm的开发配置
添加:创建项目 -> Project Interpreter -> Existing interpreter -> Virtualenv Environment | System Interpreter -> 目标路径下的 python.exe 删除:Setting -> Project -> Project Interpreter -> Show All
mac 电脑从第三步直接到最后一步就好
了解
# 创建非纯净环境:# -- virtualenv-clone 本地环境 虚拟环境名
# Mac配置终端,在终端运行虚拟环境# 在用户根目录下的.bash_profile(没有需手动创建)文件中设置# alias 终端指令名(env-py3)='/Library/Virtualenv/虚拟环境名/bin/python3'# alias 终端指令名(env-pip3)='/Library/Virtualenv/虚拟环境名/bin/pip3'
HTTP 协议
因为 Django 框架应用层是采用的 HTTP 协议,所以有必要了解 HTTP 协议。
什么是 HTTP 协议
HTTP(HyperText Transport Protocol) 是超文本传输协议,而 HTTPS 也归属于 HTTP 协议,S 代表安全。
基于 TCP/IP协议基础上的应用层协议,底层实现仍为 socket
基于请求-响应模式:通信一定是从客户端开始,服务端接收到客户端一定会做出对应响应
无状态:协议不对任何一次通信状态和任何数据做保存
无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接。
HTTP 工作原理
一次 HTTP 连接称之为一个事务,过程可以分为四步
客户端与服务端建立连接
客户端发生一个 HTTP 协议指定格式的请求
服务端接收请求后,回应一个 HTTP 协议指定格式的响应
客户端将服务端的响应展现给用户
HTTP 状态码
1开头:
2开头:
3开头:
4开头:
5开头:
请求报文
# 请求行 请求头 请求体''' POST / HTTP/1.1\r\n Host: 127.0.0.1:8001\r\n Connection: keep-alive\r\n Upgrade-Insecure-Requests: 1\r\n User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n \r\n usr=abc&pwd=123 '''
响应报文
# 响应行 响应头 响应体''' HTTP/1.1 200 OK\r\n Content-type:text/html\r\n \r\n Login Success '''
使用原生 socket 完成和浏览器的交互
目录结构
01_socket -- 01_client.html:前台通过form表单访问后台的页面 -- 01_login.html:后台测试渲染给前台的登录页面 -- 01_index.html:后台测试渲染给前台的主页 -- 01_server.py:后台服务器文件
因为 B/S架构的客户端已经由浏览器写好,所以只需要关注服务器端就ok。
服务器端代码
from socket import socket# 设置响应头(包含响应行)RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html;charset=utf-8\r\n\r\n' # 连续两个\r\n表示响应头结束# 设置服务器 socket 相关信息server = socket() server.bind('', 8080) # 空字符串表示绑定本机server.listen(5) print(('服务:http://localhost:8080'))while True: # 获取以 http 协议发来的请求 client, addr = server.accept() data = client.recv(1024) # 数据报文 包含请求行 请求头 请求体 print(data) client.send(RESP_HEADER) # /index => 响应主页 # /login => 登录页面 # 错误 => 404 # 数据 data, 字节形式 => 字符串形式 strData = str(data, encodeing) # 解析请求的数据,分析得到路由 my_route = strData.split('\r\n')[0].split(' ')[1] # 后台没有设置的路由,统统由404来处理 dt = b'404' # 设置的路由返回响应的页面文件 if my_route == '/index': with open('index 页面路径', 'rb') as f: dt = f.read() if my_route == '/login': with open('login 页面路径', 'rb') as f: dt = f.read() # /favicon.ico该请求是往后台请求标签图标 if my_route == '/favicon.ico': with open('favicon.ico', 'rb') as f: dt = f.read() # 服务器发送响应体 client.send(dt) # 一次循环,代表一次响应,也就是一次事务的完成,要关闭 http 请求连接 client.close()
修改返回数据,完善响应体
# 字符串client.send(b'HTTP/1.1 200 OK\r\n') client.send(b'\r\n') client.send(b'Normal Socket Web')
# html代码,请求头要设置支持 html 代码client.send(b'HTTP/1.1 200 OK\r\n') client.send(b'Content-type:text/html\r\n') client.send(b'\r\n') client.send(b'<h1>Normal Socket Web</h1>')
# html文件(同级目录建立一个index.html页面)client.send(b'HTTP/1.1 200 OK\r\n') client.send(b'Content-type:text/html\r\n') client.send(b'\r\n')# 利用文件方式读取页面with open('01_index.html', 'rb') as f: dt = f.read() client.send(dt)
拓展
修改接收数据,模拟后台路由
# 分析接收到的数据data = client.recv(1024)# 保证接收到的数据作为字符串进行以下处理data = str(data, encoding='utf-8')# 拆分出地址位route = data.split('\r\n')[0].split(' ')[1]# 匹配地址,做出不同的响应if route == '/index': with open('01_index.html', 'rb') as f: dt = f.read()elif route == '/login': with open('01_login.html', 'rb') as f: dt = f.read()else: dt = b'404'client.send(dt)
框架演变
目录结构
02_frame -- favicon.ico -- index.html -- manage.py
manage.py
import socketimport pymysql# 响应头RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n'# 请求处理def index(): # 以字节方式读取文件 with open('index.html', 'rb') as f: dt = f.read() return dtdef ico(): with open('favicon.ico', 'rb') as f: dt = f.read() return dtdef user(): # 数据库操作 conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root') cur = conn.cursor(pymysql.cursors.DictCursor) cur.execute('select * from user') users = cur.fetchall() print(users) users = '''%d:%s %d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name']) return users.encode('utf-8')# 设置路由urls = { # 请求路径与请求处理函数一一对应 '/index': index, '/favicon.ico': ico, '/user': user }# 设置socketdef serve(host, port): server = socket.socket() server.bind((host, port)) print('start:http://' + host + ':' + str(port)) server.listen(5) while True: sock, addr = server.accept() data = sock.recv(1024) data = str(data, encoding='utf-8') print(data) route = data.split('\r\n')[0].split(' ')[1] resp = b'404' if route in urls: resp = urls[route]() sock.send(RESP_HEADER) sock.send(resp) sock.close()# 启服务if __name__ == '__main__': serve('127.0.0.1', 8002)
项目演变
目录结构
03_proj -- template -- index.html -- user.html favicon.ico start.py urls.py views.py
index.html
<h1>{{ name }}</h1>
user.html
<table border="1"> <tr> <th>id</th> <th>name</th> <th>password</th> </tr> {% for user in users%} <tr> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.password}}</td> </tr> {% endfor %}</table>
start.py
from wsgiref.simple_server import make_serverfrom urls import urlsdef app(env, response): print(env) # 设置响应头 response("200 OK", [('Content-type', 'text/html')]) route = env['PATH_INFO'] print(route) data = urls['error']() if route in urls: data = urls[route]() # 返回二进制响应体 return [data]if __name__ == '__main__': server = make_server('127.0.0.1', 8003, app) print('start:http://127.0.0.1:8003') server.serve_forever()
urls.py
from views import * urls = { '/index': index, '/favicon.ico': ico, '/user': user, 'error': error }
views.py
import pymysql# 利用jinja2来渲染模板,将后台数据传给前台from jinja2 import Templatedef index(): with open('templates/index.html', 'r') as f: dt = f.read() tem = Template(dt) resp = tem.render(name='主页') return resp.encode('utf-8')def ico(): with open('favicon.ico', 'rb') as f: dt = f.read() return dtdef user(): # 数据库操作 conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root') cur = conn.cursor(pymysql.cursors.DictCursor) cur.execute('select * from user') users = cur.fetchall() print(users) with open('templates/user.html', 'r') as f: dt = f.read() tem = Template(dt) resp = tem.render(users=users) return resp.encode('utf-8')def error(): return b'404'