第一篇:Django简介
第一篇:Django简介
一、纯手写一个简易版的web框架
为了便于理解Django及web服务内部的原理,我们可以自己动手写一个简易版的web框架,便于理解思路。
Django框架为后端框架,为了连接前端与数据库。
1、软件开发架构
cs架构 客户端 服务端
bs架构 浏览器 服务端
# 本质bs也是cs
2、HTTP协议
- HTTP协议是应用层协议,属于网络协议
网络协议
HTTP协议 数据传输是明文
HTTPS协议 数据传输是密文
websocket协议 数据传输是密文
- 四大特性
1.基于请求响应
2.基于TCP、IP作用于应用层之上的协议
3.无状态
4.短/无链接
- 数据格式
请求首行
请求头
\r\n
请求体
- 相应状态码
1XX
2XX 200
3XX
4XX 403 404
5XX 500
3、简易的socket服务端
如何实现网址后缀名不同,而返回不同的内容?
我们先建一个简单的服务端,代码如下
# 你可已经web框架比作为服务端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 不写默认是TCP链接
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024) # 接收到客户端发来的数据
print(data) # 我们这里先打印以下数据
conn.close()
我们使用浏览器进行访问,访问地址为127.0.0.1:8080
,为本地环回测试。
我们使用三次url进行测试,第一次为 127.0.0.1:8080; 第二次为127.0.0.1:8080/index; 第三次为127.0.0.1/login;便可以在服务端得到以下数据
通过观察数据,我们发现使用 utf-8进行解码之后,数据是已字符串的形式进行显示的,所以我们可以操作字符串,来拿到我们想要的链接的后缀,代码如下
# 你可已经web框架比作为服务端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 不写默认是TCP链接
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024) # 接收到客户端发来的数据
# print(data) # 我们这里先打印以下数据
data = data.decode('utf-8')
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 根据http协议,此为请求头,请求体,和\r\n
current_path = data.split(' ')[1] # 我们可以操作字符串,拿到后缀
# 对后缀进行简单的判断
if current_path == '/index':
conn.send('data from index'.encode('utf-8'))
elif current_path == '/login':
conn.send('data from login'.encode('utf-8'))
else:
conn.send(b'hello world') # 只有英文和数字,可以方便转为bytes
conn.close()
于是我们可以看到,如果访问 127.0.0.1/index,我们可以看到
如果访问 127.0.0.1/login,我们可以看到
如果访问 127.0.0.1,我们可以看到
同时我们可以在服务端直接发送html界面,来实现以下效果,代码修改如下:
if current_path == '/index':
# 打开html文件
with open('02 html界面.html', mode='rb') as f:
conn.send(f.read())
到了这里,我们虽然可以实现简单的交互,但是我们发现,该代码还是有很多不足之处。
1.代码重复(服务端代码所有人都要重复写)
2.手动处理http格式的数据,并且只能拿到url后缀,而且其他数据获取繁琐(数据格式一样处理的代码其实也大致一样 重复写)
3、不能实现并发【同一服务端只能由一个客户端进行访问】
4、wsgiref模块
wsgiref模块对socket server部分做了一定的优化,减少了造轮子,同时变得更加精简,我们利用wsgiref模块来替换我们自己写的web框架的socket server部分。
"""
wsgiref模块
1.请求来的时候解析http格式的数据 封装成大字典
2.响应走的时候给数据打包成符合http格式 再返回给浏览器
"""
我们使用 127.0.0.1/index 进行访问,虽然在print(env)
这里会报错,但是我们会发现
from wsgiref.simple_server import make_server
def run(env, response): # 监听到客户端,make_server会给run传递两个参数
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
print(env) #
pass
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
"""
会实时监听127.0.0.1:8080地址 只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
flask启动源码
make_server('127.0.0.1',8080,obj)
__call__
"""
server.serve_forever() # 启动服务端
所以,env为一个大字典,里面包含了各种各样的信息
我们接着对wsgiref的后续代码进行补充,可以实现和上面自己的web服务一样的服务,同时,比自己写的精简不少。
后续补充代码如下
from wsgiref.simple_server import make_server
def run(env, response): # 监听到客户端,make_server会给run传递两个参数
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
print(env) # 大字典 wsgiref模块帮你处理好http格式的数据 封装成了字典让你更加方便的操作
response('200 OK', []) # 响应首行 响应头
current_path = env.get('PATH_INFO')
if current_path == '/index':
return [b'data from index']
elif current_path == '/login':
return [b'data from login']
return [b'404 error']
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
"""
会实时监听127.0.0.1:8080地址 只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
flask启动源码
make_server('127.0.0.1',8080,obj)
__call__
"""
server.serve_forever() # 启动服务端
但是,到了这里,又会出现新的问题,如果你的后缀页面太多,难道要一直使用elif
进行添加吗?【参考函数字典】
代码如下:
from wsgiref.simple_server import make_server
def index(env): # 这里传env是为了让函数内部更好的处理数据
return 'data from index'
def login(env):
return 'data from login'
def error(env):
return 'data not found'
# url与函数的对应关系
urls = [
('/index', index),
('/login', login)
]
def run(env, response): # 监听到客户端,make_server会给run传递两个参数
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
print(env) # 大字典 wsgiref模块帮你处理好http格式的数据 封装成了字典让你更加方便的操作
response('200 OK', []) # 响应首行 响应头
current_path = env.get('PATH_INFO')
# 定义一个变量 存储匹配到的函数名
func = None
for url in urls:
if current_path == url[0]:
# 将url对应的函数名赋值给func
func = url[1]
break # 匹配到一个之后 应该立刻结束for循环,减少无用匹配
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
# 统一将返回的函数返回的转码成二进制,并返回给浏览器
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
"""
会实时监听127.0.0.1:8080地址 只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
flask启动源码
make_server('127.0.0.1',8080,obj)
__call__
"""
server.serve_forever() # 启动服务端
好了,已经到了这里,你以为结束了吗?还远远没有,我们发现代码全部写在了一个文件中,看起来很冗余,不方便进行拓展。【参考软件开发目录规范】
我们将代码分文件进行存储。分为三个py文件和一个文件夹(用来存放html文件)。
- start.py
from wsgiref.simple_server import make_server
from urls import urls
from views import *
def run(env, response): # 监听到客户端,make_server会给run传递两个参数
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
"""
print(env) # 大字典 wsgiref模块帮你处理好http格式的数据 封装成了字典让你更加方便的操作
response('200 OK', []) # 响应首行 响应头
current_path = env.get('PATH_INFO')
# 定义一个变量 存储匹配到的函数名
func = None
for url in urls:
if current_path == url[0]:
# 将url对应的函数名赋值给func
func = url[1]
break # 匹配到一个之后 应该立刻结束for循环,减少无用匹配
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
# 统一将返回的函数返回的转码成二进制,并返回给浏览器
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
"""
会实时监听127.0.0.1:8080地址 只要有客户端来了
都会交给run函数处理(加括号触发run函数的运行)
flask启动源码
make_server('127.0.0.1',8080,obj)
__call__
"""
server.serve_forever() # 启动服务端
-
urls.py
存放路由与视图函数对应关系
from views import *
# url与函数的对应关系
urls = [
('/index', index),
('/login', login)
]
-
views.py
用来存放视图函数(后端业务逻辑),也就是放访问到特定的后缀时访问该函数。
def index(env): # 这里传env是为了让函数内部更好的处理数据
return 'data from index'
def login(env):
return 'data from login'
def error(env):
return 'data not found'
-
temlates文件夹
用来存放html页面文件
总结:
如此一来,按照功能的不同拆分之后,后续添加功能只需要在urls.py书写对应关系然后去views.py书写业务逻辑即可。
5、动静态网页
-
静态网页
简单来说,就是页面上的数据是直接写死的,不会改变。
-
动态网页
网页上的数据是实时获取的。
eg: 1.后端获取当前时间展示到html页面上 2.数据是从数据库中获取的数据展示到html页面上
6、后端获取当前时间展示到html页面上
此时,我们直接从 views.py 和 urls.py 文件中添加即可。效果如下。
代码如下:
"""urls.py中"""
urls = [
('/index', index),
('/login', login),
('/get_time', get_time)
]
"""views.py"""
def get_time(env):
import datetime
current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
# 如何将后端获取到的数据"传递"给html文件?
with open('templates/01 myhtml.html', mode='rt', encoding='utf-8') as f:
# 在后端将html页面处理好之后再返回给前端
data = f.read().replace('message', current_time)
return data # data就是一堆字符串
"""01 myhtml.html"""
<div>
<h1>这是从后端传来的数据</h1>
<h1>message</h1>
</div>
7、字典数据传给html文件
将一个字典传递给html文件 并且可以在文件上方便快捷的操作字典数据,访问效果如下
这里我们需要导入一个jinja2
模块,来进行操作,代码如下
"""templates/02 get_dict.html"""
<h1>我是一个页面</h1>
{{ user }}
{{ user.get('username')}} # 类似python取字典
{{ user.age }} # 类似js取字典
{{ user['hobby'] }} # 类似python取字典
"""urls.py"""
urls = [
('/index', index),
('/login', login),
('/get_time', get_time),
('/get_dict', get_dict) # 添加路由对象关系
]
"""views.py"""
def get_dict(env):
from jinja2 import Template
# 创建一个字典
user_dic = {
'username': 'yangyi',
'age': 18,
'hobby': 'study'
}
with open('templates/02 get_dict.html', mode='rt', encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
# 给get_dict.html传递了一个值 页面上通过变量名user就能够拿到user_dict
res = tmp.render(user=user_dic)
# 返回给浏览器数据
return res
8、数据从数据库中获取的展示到html页面上
我们先使用navicat快速创建一个数据库,数据库效果如下。
浏览器展示效果如下:
具体代码如下:
"""get_data.html"""
<div class="container"> # 使用bootstrap进行渲染
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">用户数据</h1>
<table class="table table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>password</th>
<th>hobby</th>
</tr>
</thead>
<tbody>
<!-- [{},{},{},{},{}]--> # jinja2模块可以实现数据for循环
{% for user_dict in user_list %}
<tr>
<td>{{ user_dict.id}}</td>
<td>{{ user_dict.username}}</td>
<td>{{ user_dict.password}}</td>
<td>{{ user_dict.hobby}}</td>
</tr>
{% endfor%}
</tbody>
</table>
</div>
</div>
</div>
"""usls.py"""
urls = [
('/index', index),
('/login', login),
('/get_time', get_time),
('/get_dict', get_dict),
('/get_data', get_data)
]
"""views.py"""
def get_data(env):
# 导入pymysql模块,用来操作数据库
import pymysql
# 去数据库中获取数据 传递给html页面 借助于模版语法 发送给浏览器
from jinja2 import Template
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
db='day59',
charset='utf8',
autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 写sql语句
sql = 'select * from user_info'
# 受影响的行数
affect_rows = cursor.execute(sql)
data_list = cursor.fetchall() # [{},{},{}]
# 将获取到的数据传递给html文件
with open('templates/03 get_data.html', mode='rt', encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user_list=data_list)
return res
9、jinja2模块之模块语法
pip3 install jinja2
"""模版语法是在后端起作用的"""
# 模版语法(非常贴近python语法)
{{ user }}
{{ user.get('username')}}
{{ user.age }}
{{ user['hobby'] }}
# 实现for循环
{% for user_dict in user_list %}
<tr>
<td>{{ user_dict.id}}</td>
<td>{{ user_dict.username}}</td>
<td>{{ user_dict.password}}</td>
<td>{{ user_dict.hobby}}</td>
</tr>
{% endfor%}
10、自定义模块总结
简易版本web框架流程图如下
二、Django框架介绍
1、python三大主流web框架
-
Django
特点:大而全 自带的功能特别特别特别的多 类似于航空母舰 不足之处: 有时候过于笨重
-
flask
特点:小而精 自带的功能特别特别特别的少 类似于游骑兵 第三方的模块特别特别特别的多,如果将flask第三方的模块加起来完全可以盖过django 并且也越来越像django 不足之处: 比较依赖于第三方的开发者
-
tornado
特点:异步非阻塞 支持高并发
-
总结:
"""web模块分为三部分""" A:socket部分 B:路由与视图函数对应关系(路由匹配) C:模版语法 """django""" A用的是别人的 wsgiref模块 B用的是自己的 C用的是自己的(没有jinja2好用 但是也很方便) """flask""" A用的是别人的 werkzeug(内部还是wsgiref模块) B自己写的 C用的别人的(jinja2) """tornado""" A,B,C都是自己写的
2、Django安装
- 正常启动要求
# 如何让你的计算机能够正常的启动django项目
1.计算机的名称不能有中文
2.一个pycharm窗口只开一个项目
3.项目里面所有的文件也尽量不要出现中文
4.python解释器尽量使用3.4~3.6之间的版本
(如果你的项目报错,你点击最后一个报错信息,去源码中把逗号删掉)
- 版本
# django版本问题
1.X 2.X 3.X(直接忽略)
这里使用django 1.11.11版本
- 安装
# django安装
pip3 install django==1.11.11
# 或者使用pycharm进行安装
3、django基本操作
- 命令行执行
# 1.创建django项目
"""先切换到要创建的文件路径,然后进行创建"""
django-admin startproject mysite # 创建一个名为mysite的django项目
mysite文件夹
manage.py
mysite文件夹
__init__.py
settings.py
urls.py
wsgi.py
# 2.启动django项目
"""
一定要先切换到项目目录下
cd /mysite
"""
python3 manage.py runserver # 启动django服务器
# http://127.0.0.1:8000/
# 3.创建应用
"""
Next, start your first app by running python manage.py startapp [app_label].
"""
python3 manage.py startapp app01
应用名应该做到见名知意
- pycharm操作
# 1 new project 选择左侧第二个django即可
# 2 启动
1.还是用命令行启动
2.点击绿色小箭头即可
# 3 创建应用
1.pycharm提供的终端直接输入完整命令
2.pycharm
tools
run manage.py task提示
# 4 修改端口号以及创建server【可选】
edit confi....
刚创建的django项目都出错【我裂开了】,仔细观察,我们发现是路径拼接方式不对,稍作修改即可。
创建app01,文件夹中文件如下。
4、应用
django是一款专门用来开发app的web框架。一个app就是一个独立的功能模块.
"""
django框架就类似于是一所大学(空壳子)
app就类似于大学里面各个学院(具体功能的app)
比如开发淘宝
订单相关
用户相关
投诉相关
创建不同的app对应不同的功能
选课系统
学生功能
老师功能
"""
重点:创建的应用一定要去配置文件中注册
将settings.py中的参数进行修改,方式如下
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 全写
'app01', # 简写
]
# ps:你在用pycharm创建项目的时候 pycharm可以帮你创建一个app并且自动注册
主要文件介绍
-mysite项目文件夹
--mysite文件夹
---settings.py 配置文件
---urls.py 路由与视图函数对应关系(路由层)
---wsgi.py wsgiref模块(不考虑)
--manage.py django的入口文件
--db.sqlite3 django自带的sqlite3数据库(小型数据库 功能不是很多还有bug)
--app01文件夹
---admin.py django后台管理
---apps.py 注册使用
---migrations文件夹 数据库迁移记录
---models.py 数据库相关的 模型类(orm)
---tests.py 测试文件
---views.py 视图函数(视图层)
命令行与pycharm的区别
命令行创建不会自动有templates文件夹 需要你自己手动创建而pycharm会自动帮你创建并且还会自动在配置文件中配置对应的路径。
# pycharm创建
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
]
# 命令行创建
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
]
"""
也就意味着你在用命令创建django项目的时候不单单需要创建templates文件夹还需要去配置文件中配置路径
'DIRS': [os.path.join(BASE_DIR, 'templates')]
"""
django小白必会三板斧
-
HttpResponse
返回字符串类型的数据
from django.shortcuts import render, HttpResponse, redirect # Create your views here. def index(request): return HttpResponse('曾经沧海难为水,除却巫山不是云。')
-
render
返回html文件的
def get_html(request): # 自动去tempaltes文件夹下帮你查找文件 return render(request, 'myhtml.html')
如何实现向html文件中如何传参?
第一种:
# 第一种
"""show_arg.html"""
<div>{{ data }}</div>
<div>{{ date }}</div>
"""views.py"""
def show_arg(request):
user_dict = {
'name': 'yangyi',
'age': 18,
'gender': 'male'
}
# 第一种传值方式:更加的精确 节省资源(需要将数据赋值给一个接受的变量中,如 data date)
return render(request, 'show_arg.html', {'data': user_dict, 'date': 123})
效果如下所示
第二种:
# 第二种
"""show_arg.html"""
<div>{{ user_dict }}</div>
"""views.py"""
def show_arg(request):
user_dict = {
'name': 'yangyi',
'age': 18,
'gender': 'male'
}
# 第二种传值方式:当你要传的数据特别多的时候
"""locals会将所在的名称空间中所有的名字全部传递给html页面"""
return render(request, 'show_arg.html', locals())
-
redirect
重定向
# 1、重定向去其它地址 def new_addr(request): return redirect('https://www.youku.com') # 2、重定向去自定义地址 def new_addr(request): return redirect('/home/') def home(request): # 被重定向的函数也得注册 return HttpResponse('from home')
5、总结
"""我们在使用django框架的时候的步骤"""
1、使用pycharm创建一个django项目
2、将配置文件中templates的注册路径进行修改
3、创建应用
4、注册应用
5、书写views.py文件
6、配置urls.py文件【注意:要导入views模块】 # from app01 import views