P01 Flask框架概述(flask与Django的区别)
所提供的组件
Django所提供的组件:
orm/session/cookie/admin/form/modelform/路由/视图/模板/中间件/分页/auth/contenttype/缓存/信号/多数据库连接
Flask所提供的组件:
第三方组件非常齐全,路由/视图/模板/中间件/session
请求处理
Django:
请求处理是逐一封装和处理。(数据一路传递,经过许多的中介,最后传递到我这来)
Flask:
利用上下文管理来实现的。(数据一路传递到某个位置,不会传递到我这来,当我需要的时候再去取出来,这种处理机制称为上下文管理)
P02 今日概要(day1要做的任务)
- flask的快速使用
- 实现一个xx系统
- 蓝图(写flask时的标准的目录结构)
P03 内容回顾(Django的知识回顾,不管)
P04 flask快速上手(安装并编写第一个flask程序)
4.1 安装
pip install flask
4.2 flask所需要的依赖——wsgi Werkzeug(flask一定会调用该依赖)
···
from werkzeug.serving import run_simple
def func(environ, start_response):
print('请求来了nia!')
pass
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func) #访问一进来就执行以第三个参数为函数名的函数
···
4.3 快速使用Flask
from flask import Flask
app = Flask(__name__) # Flask类的实例
@app.route('/index')
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
4.4 Flask与wsgi (Flask是基于wsgi实现的)
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ,start_response):
return 'xx' #此处应该写的就是flask源码内容
def run(self):
run_simple('127.0.0.1', 5000, self) #若第三个参数为对象,则执行对象中的__call__方法
app = Flask()
if __name__ == '__main__':
app.run()
4.5 总结
- flask框架是基于werkzeug的wsgi实现的,flask自己没有wsgi。
- 用户请求一旦到来,就会调用
app.__call__方法。
P05 用户登#录示例()
- render_template:
render_template是 Flask 框架提供的一个方法,用于渲染模板文件并生成 HTML 响应。 - 所有新建的html文件都应该放在一个新建的template文件目录下,html文件默认放在名为template文件夹下,要想修改文件夹名字,需要在创建Flask对象时修改参数template_folder(template_folder = gouba,就可以将html文件放在gouba目录下 )
- render_template类似于Django中的HttpResponse
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/login')
def login():
return render_template('login.html')
if __name__ == '__main__':
app.run()
-
登录实例代码
- 在app.route()方法中,
methods参数用于指定可以接受的 HTTP 请求方法。 - request.method用于判断发过来的请求类型
- request.form.get()用于获取post请求中的参数
redirect方法用于重定向用户的请求。当用户访问某个路由时,可以使用redirect方法将其重定向到另一个路由或 URL。
- 在app.route()方法中,
from flask import Flask, render_template,request,redirect
app = Flask(__name__)
@app.route('/login', methods = ['GET', 'POST']) #让login方法既可以接受get,也可以接受post方法
def login(): #默认情况该函数只能接受get请求
#get
if request.method == 'GET': #判断是否为get请求
return render_template('login.html')
#post 通过request.form来接收请求体传过来的值(用户数据)
user = request.form.get('user')
pwd = request.form.get('pwd')
error = '用户名或密码错误'
if user == 'shuhao' and pwd == "shu123456":
return redirect('/index') #跳转路径
return render_template('login.html', error = error) #将error数据传到前端
@app.route('/index')
def index():
return '首页'
if __name__ == '__main__':
app.run()
P06 用户编辑实例
6.1 Jinja2前置知识
- Jinja2 是一个流行的 Python 模板引擎, 通常用于将动态数据插入到静态模板中,以生成动态的网页内容。
- 在 Jinja2 中,
{% %}用于包含控制结构,如循环、条件语句等 {{ }}则用于包含表达式,用于显示变量值或执行函数。.items()方法可以用于遍历字典中的每个键值对
6.2 用户编辑实例代码
-
两种post传参方法 (nid == key)
@app.route('/edit') #接收参数方式1 "/edit?nid={{key}}" <!--传参方式1-->@app.route('/del/<int:nid>') #接收参数方式2 "/del/{{key}}" <!--传参方式2--> -
url_for
- 导入url_for包,在app.route('/index', endpoint='idx')中,
endpoint参数用于指定该路由的名称,即路由的终点。为路由指定一个唯一的标识符,以便在应用程序中引用该路由;使用url_for()函数生成 URL 。例如,在下面的代码中,'/index'是路由的路径,’idx‘是该路由的终点名称。当需要在应用程序中生成指向该路由的 URL 时,可以使用url_for('idx')来引用它,这将生成指向'/index'路由的 URL。url = url_for('idx') print(url) 会输出'/index' @app.route('/index', endpoint='idx') def index(): return 'Hello, World!'
- 导入url_for包,在app.route('/index', endpoint='idx')中,
-
request.args.get是 Flask 中用于获取 URL 查询参数的方法,拿到的数据是字符串类型
from flask import Flask, render_template, request ,redirect, url_for
app = Flask(__name__)
#数据
DATA_DICT = {
1: {'name':'陈硕', "age":73},
2: {'name':'汪洋', "age":84},
}
@app.route('/login', methods = ['GET','POST']) #指定了该路由可以接受GET和POST请求。
def login():
# 发get请求不用拿数据
if request.method == 'GET':
return render_template('login.html')
user = request.form.get('user')
pwd = request.form.get('pwd')
error = '账号或密码错误请重新输入'
if user == 'shuhao' and pwd == '123456':
return redirect('/index') #登录成功跳转到index页面
return render_template('login.html', error = error)
# index首页
@app.route('/index',endpoint='idx')
def index():
data_dict = DATA_DICT
return render_template('index.html', data_dict = data_dict) #将数据传到index.html中
# 修改
@app.route('/edit',methods=['POST','GET']) #接收参数方式1 edit接收post与get请求
def edit():
nid = int(request.args.get('nid')) #先拿要修改的数据的id值 /nid为str类型---->int类型
# get请求
if request.method == "GET":
info = DATA_DICT[nid] #根据id找到该数据
return render_template('edit.html', info = info) #将参数传到前端
# post请求
user = request.form.get('user')
age = request.form.get('age')
# 获取了id,user,age
DATA_DICT[nid]['name'] = user
DATA_DICT[nid]['age'] = age
return redirect(url_for('idx'))
# 删除
@app.route('/del/<int:nid>') #接收参数方式2
def delete(nid):
print(nid, type(nid))
del DATA_DICT[nid]
return redirect(url_for('idx'))
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录哦</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post">
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" name="提交"><span style="color:red">{{error}}</span>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>用户列表</h1>
<table border="1">
<thead> <!--定义表格的头部 -->
<tr> <!--表格行标签-->
<th>ID</th> <!-- 设置表格列标题 -->
<th>用户名</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody> <!-- 定义表格的主体部分 -->
{% for key, value in data_dict.items() %} <!-- Jinja2 模板引擎中的语法。-->
<tr>
<td>{{key}}</td>
<td>{{value.name}}</td>
<td>{{value.age}}</td>
<td>
<a href="/edit?nid={{key}}">编辑</a> <!--传参方式1--> <!--a标签默认发get请求-->
<a href="/del/{{key}}">删除</a> <!--传参方式2-->
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编辑</title>
</head>
<body>
<h1>修改</h1>
<form method="post"> <!--提交表单数据时使用post方法,当前页面提交-->
<input type="text" name="user" value="{{info.name}}">
<input type="text" name="age" value="{{info.age}}">
<input type="submit" value="提交">
</form>
</body>
</html>
P07 总结
- flask路由
@app.route('/login', methods=['GET', 'POST'])
def login():
pass
- 路由的参数
url:通常用来指定特定资源的标识符或者要执行的操作。
methods:用于指定可以接受的 HTTP 请求方法。
endpoint:如果不修改,默认值为函数名, 不能重名
- 动态路由
@app.route('/index')
def login():
pass
@app.route('/index/<name>')
def login(name):
pass
@app.route('/index/<int:nid>')
def login(nid):
pass
- 获取数据
-
后端获取前端提交的数据:
from flask import request @app.route('/index'): def login(): request.args # GET形式传递的参数 request.form # POST形式传递的参数
-
前端拿后端数据:
通过render_template('xx.html', data)将参数data放在html文件后面从而传到前端xx.html中,前端再通过模板处理获得数据。
P08 基于session实现用户登录(保存用户会话信息)
8.1 相关介绍
session是一个在服务器和客户端之间存储数据的机制。它允许你在不同的请求之间存储用户特定的信息,例如用户的身份验证状态、用户偏好设置等等。- 在flask中,session存放在用户的浏览器中。flask默认的session数据在服务端不存储,而是通过加密的方式存在cookie中.
- session依赖于secret_key,因此在使用session之前,要设置一个secret_key。
8.2使用session
-
在登录时,用session保存用户名
@app.route('/login', methods = ['GET','POST']) def login(): if request.method == 'GET': return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') error = if user == 'shuhao' and pwd == '123456': session['name'] = 'shuhao' #session保存用户名 return redirect('/index') return render_template('login.html', error = error)
-
设置secret_key
app = Flask(__name__) app.secret_key = 'weg2gg55heh5h8ah' #设置secret_key
-
在各项操作前检查当前用户是否已经登录
# index首页 @app.route('/index',endpoint='idx') def index(): username = session.get('name') #检查当前用户是否已经登录 if not username: return redirect(url_for('login')) data_dict = DATA_DICT return render_template('index.html', data_dict = data_dict) # 删除 @app.route('/del/<int:nid>') def delete(nid): username = session.get('name') #检查当前用户是否已经登录 if not username: return redirect(url_for('login')) print(nid, type(nid)) del DATA_DICT[nid] return redirect(url_for('idx'))
- 在上述应用中,session 主要用于用户身份验证。在登录时,如果用户提供了正确的用户名和密码,则会将用户名存储在 session 中。之后,每当用户访问受限页面时,会检查 session 中是否存在用户名,如果存在则表示用户已登录,否则将用户重定向到登录页面。
P09 装饰器实现用户认证
9.1 相关介绍
- 装饰器的作用是在被装饰的函数执行之前或之后执行一些额外的操作,而不需要修改被装饰的函数本身。
- 在 Python 中,函数的
__name__属性用于获取函数的名称。
9.2 装饰器的使用
- 被装饰函数的name属性
def auth(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner #返回一个函数
@auth
def login():
pass
print(login.__name__) #打印结果为inner
在装饰器中使用了内部函数,而该内部函数的名称是 inner,因此通过装饰器装饰后的函数的 __name__ 属性将会是内部函数的名称。具体来说,在你提供的代码中,login 函数被 auth 装饰器装饰,而 auth 装饰器返回了一个内部函数 inner。因此,通过装饰器装饰后的 login 函数实际上是 inner 函数,其 __name__ 属性的值也是 inner。
import functools
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner #返回一个函数
@auth
def login():
pass
print(login.__name__) #打印结果为login
functools.wraps 装饰器可以将原始函数的元数据(如名称、文档字符串等)复制到内部函数中,从而保留原始函数的属性。
-
多装饰器装饰
import functools def auth0(func): print(0) @functools.wraps(func) def inner(*args, **kwargs): return func(*args, **kwargs) return inner #返回一个函数 def auth1(func): print(1) @functools.wraps(func) def inner(*args, **kwargs): return func(*args, **kwargs) return inner #返回一个函数 @auth0 @auth1 name为login, login执行的是inner函数 def login(): pass print(login.__name__) #打印结果为 1 0 login函数被多个装饰器装饰时,先执行最近的装饰器函数,因此先打印1。
-
基于装饰器的用户登录
装饰器
auth的作用是,在被装饰的路由函数执行之前先验证用户是否已登录。如果用户已登录,则正常执行被装饰的路由函数;如果用户未登录,则重定向到登录页面。被装饰的路由函数作为参数func传递给 auth 函数。
from flask import Flask, render_template, request ,redirect, url_for, session
import functools
app = Flask(__name__)
app.secret_key = 'sgw1e2ghw3og4he7wrr8go0ego'
#数据
DATA_DICT = {
1: {'name':'陈硕', "age":73},
2: {'name':'汪洋', "age":84},
}
def auth(func): #将被装饰的路由函数作为参数func传递给 auth 函数
@functools.wraps(func)
def inner(*args, **kwargs):
username = session.get('name')
if not username:
return redirect(url_for('login'))
return func(*args, **kwargs)
return inner
@app.route('/login', methods = ['GET','POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
user = request.form.get('user')
pwd = request.form.get('pwd')
error = '账号或密码错误请重新输入'
if user == 'shuhao' and pwd == '123456':
session['name'] = 'shuhao'
return redirect('/index')
return render_template('login.html', error = error)
# index首页
@app.route('/index',endpoint='idx')
@auth #auth装饰器返回inner函数,路由index与inner绑定,因此访问index时,执行的是auth中的inner函数,而inner函数中的return func调用被装饰的路由函数执行index函数
def index():
username = session.get('name')
if not username:
return redirect(url_for('login'))
data_dict = DATA_DICT
return render_template('index.html', data_dict = data_dict)
# 修改
@app.route('/edit',methods=['POST','GET'])
@auth
def edit():
nid = int(request.args.get('nid'))
# get请求
if request.method == "GET":
info = DATA_DICT[nid]
return render_template('edit.html', info = info)
# post请求
user = request.form.get('user')
age = request.form.get('age')
# 获取了id,user,age
DATA_DICT[nid]['name'] = user
DATA_DICT[nid]['age'] = age
return redirect(url_for('idx'))
# 删除
@app.route('/del/<int:nid>')
@auth
def delete(nid):
print(nid, type(nid))
del DATA_DICT[nid]
return redirect(url_for('idx'))
if __name__ == '__main__':
app.run()
P10 蓝图(构建业务功能可拆分的目录结构)
10.1 相关介绍
在flask中,蓝图(Blueprint)是一种组织和注册路由的方式,它可以帮助你将应用程序分成一系列相对独立的模块或组件。具体来说,蓝图允许你在不同的模块中定义路由,并将它们注册到主应用程序中。这样,你可以将相关功能或模块分组到单独的蓝图中,然后将这些蓝图注册到应用程序中,而不是将所有的路由都定义在单个文件中。
10.2 使用蓝图
-
在 Flask 中,使用
flask.Blueprint类来创建蓝图,并使用app.route()装饰器来定义路由。from flask import Blueprint bp = Blueprint('bp', __name__) #bp为蓝图的名字 @bp.route('/f1') def f1(): return 'f1' @bp.route('/f2') def f2(): return 'f2' -
在主应用程序中,使用
app.register_blueprint()方法来注册蓝图:from flask import Flask from .views.yl import bp #导入新创建的蓝图对象 def create_app(): app = Flask(__name__) app.secret_key = 'asdfgfs' @app.route('/index') def index(): return 'index' app.register_blueprint(bp, url_prefix = '/web') #url_prefix用于指定该蓝图中所有路由的 URL 前缀。它允许你为蓝图中的所有路由指定一个统一的前缀,从而避免重复书写相同的 URL 前缀。 return app这样,蓝图中定义的路由就会被注册到主应用程序中,并可以通过相应的 URL 访问到。
P11 总结
-
flask和django的区别?
django是一个大而全,集成了很多方便组件的重武器,flask短小精悍,扩展性强,有很多的第三方组件,通过这些第三方组件可以构建与django一样提供很多功能的项目。
-
flask的session是以加密的形式保存在浏览器的cookie上
-
装饰器相关
functools
多装饰器应用
-
蓝图