flask-介绍、配置文件、路由系统
1.flask和pythonweb框架介绍
1.1 框架介绍:
django:大而全,内置的app多,第三方app也多
Flask:小而精,没有过多的内置组件,只完成web框架最基本的功能,需要借助于第三方,完成更丰富的功能
web.py:是一个小巧灵活的Python框架,它简单而且功能强大(国内几乎没有用的)
fastapi:python的异步web框架,不少公司再用,https://fastapi.tiangolo.com/zh/
sanic:python的异步web框架,供支持异步高并发请求的 web 服务
tornado:异步框架,用的比较少了
2.同步框架和异步框架的区别
djagno是同步框架还是异步框架,djagno 3.x以后支持异步
同步框架的意思:一个线程只处理一个请求
异步框架的意思:一个线程可以处理多个请求
异步框架可以很显著的提高并发量
1.2 flask介绍
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用
jinja2:模板语法,django的dtl,非常像
Werkzeug WSGI 符合wsgi协议的web服务器,django使用的是wsgiref
1.3 wsgiref介绍
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块
用wsgiref写web:
#### wsgirf写web
from wsgiref.simple_server import make_server
# mya 就等同于django
def mya(environ, start_response):
#把environ包装成了request
print(environ)
start_response('200 OK', [('Content-Type', 'text/html')])
if environ.get('PATH_INFO') == '/index':
with open('index.html','rb') as f:
data=f.read()
elif environ.get('PATH_INFO') == '/login':
with open('login.html', 'rb') as f:
data = f.read()
else:
data=b'<h1>Hello, web!</h1>'
return [data] # 做成了response
if __name__ == '__main__':
myserver = make_server('', 8008, mya)
print('监听8010')
myserver.serve_forever()
因为我们什么都没有输入,所以返回的是b'
Hello, web!
'
1.4 使用werkzeug写web
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
2.flask快速使用
版本:1.x和2.x,没有本质区别
安装:
pip install flask
# 会自动安装依赖:MarkupSafe, Werkzeug, Jinja2
使用:
flask返回字符串直接使用 return 字符串 就可以
from flask import Flask
app = Flask(__name__)
'''自定义一个名字,字符串类型,因为该文件是执行文件,__name__就等于__main__'''
@app.route('/index')
def index():
return 'hello world'
'''返回字符串不需要使用HttpResponse'''
@app.route('/')
def home():
return 'hello home'
if __name__ == '__main__':
app.run()
3.登陆、显示用户信息小案例(前后端混合)
3.1知识点:
1.注册路由:app.route(路径,methods=[请求方式('GET'/'POST')])
2.新手四件套:
render_template:模板渲染,将数据传到前端使用变量名=数据值的方式来传,在前端使用变量名点或变量名.get('key')或变量名['key']的方式来取值
return redirect('路由'):重定向
return '字符串':返回字符串
return jsonify:返回json格式字符串
3.请求request对象是全局的的,直接导入使用,在不同视图函数中不回混乱
request.method表示请求方式
request.form表示将post请求body体的内容转换成了字典
4.session是全局的,直接导入即可,使用前一定要制定秘钥app.secret_key='xxx',秘钥设置复杂即可,没有其他要求
5.模板的渲染:
兼容django的dtl
更强大,字典可以用.get('key'),.key,['key']来取值
6.for循环:
{% for k,v in 后端传过来的字典变量名.items() %}
{{v.get('key')}}
{{v.key}}
{{v['key']}}
{% endfor %}
7.转换器:
@app.route('/detail/int:pk')
def detail(pk):
...
3.2 代码
flaskproject.py:
from flask import Flask, request, render_template, redirect, session, jsonify
app = Flask(__name__)
# 要使用session,必须设置秘钥,秘钥是配置信息,秘钥设置尽量复杂即可
app.secret_key = 'dsjfhu3w4hy32hcsd'
USERS = {
1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"},
2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"},
3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"},
}
@app.route('/login', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
# return 'adsfsfdff'
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
if username == 'max' and password == '123':
session['name'] = username
return redirect('/')
else:
return render_template('login.html', error='用户名或密码错误')
@app.route('/')
def home():
if session.get('name'):
return render_template('home.html', user_dict=USERS)
else:
return redirect('/login')
@app.route('/detail/<int:pk>')
def detail(pk):
if session.get('name'):
user_detail = USERS.get(pk)
return render_template('detail.html', user=user_detail)
else:
return redirect('/login')
@app.route('/test')
def test():
return jsonify({'name': 'jerry', 'age': 18})
if __name__ == '__main__':
app.run()
detail.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>姓名:{{user.name}}</p>
<p>性别:{{user.gender}}</p>
<p>年龄:{{user.get('age')}}</p>
<p>{{user.text}}</p>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详情</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<input type="submit" value="登录"> {{error}}
</form>
</body>
</html>
4.配置文件方式
方式1:直接使用app.的方式修改。这种方式只能修改debug和secret_key
app.debug=True
app.secret_key='jkfdhgkjsdf'
方式二:用中括号方式来写
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'ajkdshdjh322uirfwhye'
方式三:用配置文件来写(不常用)
flask.py:
app.config.from_pyfile("settings.py")
settings.py:
DEBUG=True
SECRET_KEY='jkdfsyr8743gffhjsd'
方式四:使用类对的方式(常用)
settings.py:
'''基类'''
class BASE(object):
DEBUG = False
'''开发时使用的类'''
class ProductionConfig(BASE):
DATABASE_URL = 'mysql://user@localhost/foo'
'''上线时使用的类'''
class DevelopmentConfig(BASE):
DEBUG = True
DATABASE_URL = 'localhost'
flask.py:
# app.config.from_object('settings.DevelopmentConfig')
# app.config.from_object('settings.ProductionConfig')
flask中的默认配置:
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
5.路由本质
5.1 route源码分析
@app.route('/login')
def index():
pass
本质是:
index = app.route('/login')(index)
进入到route源码:
def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
'''rule是路径,'/login'就是传给了rule,其他参数都给了options'''
...
def decorator(f: T_route) -> T_route:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
可以看到route的返回结果是decorator,所以app.route('/login')(index)就相当于decorator(index)
decorator源码:
def decorator(f: T_route) -> T_route:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
所以此时f就是index,没有加括号不执行,而是一个函数的内存地址。此时self是类Scaffold的对象
而在查看flask的源码得知,flask也是继承了类Scaffold:
其实此时的self就是app,app也是一个Flask的对象:
self.的这个方法就是在创建路由:
add_url_rule(rule, endpoint, f, **options)
查看add_url_rule的源码:
def add_url_rule(
self,
rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[ft.RouteCallable] = None,
provide_automatic_options: t.Optional[bool] = None,
**options: t.Any,
) -> None:
rule就是路由,endpoint默认值为None,自己传也需要传None,view_func就是函数的内存地址,此外我们还需要传一个methods方法,来表示请求的方法。我们知道了路由的本质,也可以自己手动配置路由,不采用装饰器的方式:
def home():
return 'HOME'
app.add_url_rule('/home', endpoint=None, view_func=home, methods=['GET'])
5.2 add_url_rule参数
rule:路由
view_func:视图函数名称
defaults = None 默认值,当url中无参数,并且函数需要参数时,使用defaults中传入
def home(name):
return f'{name}'
app.add_url_rule('/home', endpoint=None, view_func=home, methods=['GET'],defaults={'name':'max'})
endpoint:别名,用来做反向解析
methods:允许的请求方式
strict_slashes:是否要求末尾带斜杠
@app.route('/index', strict_slashes=False)
#访问http://www.xx.com/index/ 或http://www.xx.com/index均可
@app.route('/index', strict_slashes=True)
#仅访问http://www.xx.com/index
redirect_to:重定向
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')
5.3 转换器
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
在路由后面定义一个变量名,该变量名可以为任意数据类型
def home(pk):
return f'{pk}'
app.add_url_rule('/<pk>', endpoint=None, view_func=home, methods=['GET'])
如果我们设置路由为:
app.add_url_rule('/<int:pk>', endpoint=None, view_func=home, methods=['GET'])
如果我们路由按照如下配置,那么根路径后就只能跟整数:
app.add_url_rule('/<int:pk>', endpoint=None, view_func=home, methods=['GET'])
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律