配置文件
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, } 方式一: app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) 方式二: app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}) 字典格式 app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
我们常用的方法
from flask import Flask,render_template,redirect
app = Flask(__name__)
# 配置文件
app.config.from_object("settings.DevelopmentConfig")
@app.route('/index',methods=['GET','POST'])
def index():
return ""
if __name__ == '__main__':
app.run()
settings
class BaseConfig(object):
DEBUG = True
SECRET_KEY = "asudflkjdfadjfakdf"
class ProductionConfig(BaseConfig):
DEBUG = False
class DevelopmentConfig(BaseConfig):
pass
class TestingConfig(BaseConfig):
pass
settings中可以定义多种配置,我们根据不同的环境使用不同的配置
路由
两种添加路由的方式
方式一:
@app.route('/xxxx') # @decorator
def index():
return "Index"
方式二:
def index():
return "Index"
app.add_url_rule('/xxx', "n1", index) #n1是别名
执行@app.route('/xxx')时,首先执行app.route('/xxx'),这个方法的源码如下

所以decorator=app.route('/index',methods=['GET','POST']),然后再使用@decorator装饰函数,可以看到实际上就是执行了self.add_url_rule(rule, endpoint, f, **options),所以两种添加路由的方式本质是一样的
添加路由关系的本质:将url和视图函数封装成一个Rule对象)添加到Flask的url_map字段中
路由中传参
@app.route('/user/<username>') #常用的 不加参数的时候默认是字符串形式的
@app.route('/post/<int:post_id>') #常用的 #指定int,说明是整型的
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')
@app.route('/login', methods=['GET', 'POST'])
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
自定义正则匹配路由
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter
app = Flask(import_name=__name__)
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value)
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val
# 添加到flask中
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(url_for('index', nid='888'))
return 'Index'
if __name__ == '__main__':
app.run()
反向生成URL: url_for
endpoint("name") #别名,相当于django中的name,如果没有定义endpoint,默认为视图函数的函数名
反向解析需要导入:
from flask import Flask, url_for
示例
from flask import Flask,render_template,redirect,url_for
app = Flask(__name__)
@app.route('/index',methods=['GET','POST'],endpoint='n1')
def index():
v1 = url_for('n1')
v2 = url_for('login') # 没有定义endpoint默认为视图函数名
v3 = url_for('logout')
print(v1,v2,v3)
return "Index"
@app.route('/login',methods=['GET','POST'])
def login():
return "login"
@app.route('/logout',methods=['GET','POST'])
def logout():
return "logout"
if __name__ == '__main__':
app.run()
@app.route和app.add_url_rule参数
@app.route和app.add_url_rule参数:
rule, URL规则
view_func, 视图函数名称
defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
endpoint=None, 名称,用于反向生成URL,即: url_for('名称')
methods=None, 允许的请求方式,如:["GET","POST"]
strict_slashes=None, 对URL最后的 / 符号是否严格要求,
如:
@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=None, 重定向到指定地址
如:
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')
或
def func(adapter, nid):
return "/home/888"
@app.route('/index/<int:nid>', redirect_to=func)
subdomain=None, 子域名访问
from flask import Flask, views, url_for
app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'haiyan.com:5000'
@app.route("/", subdomain="admin")
def static_index():
"""Flask supports static subdomains
This is available at static.your-domain.tld"""
return "admin.xxx.com"
#动态生成
@app.route("/dynamic", subdomain="<username>")
def username_index(username):
"""Dynamic subdomains are also supported
Try going to user1.your-domain.tld/dynamic"""
return username + ".your-domain.tld"
if __name__ == '__main__':
app.run()
所有的域名都得与IP做一个域名解析:
如果你想通过域名去访问,有两种解决方式:
方式一:
1、租一个域名 haiyan.lalala
2、租一个公网IP 49.8.5.62
3、域名解析:
haiyan.com 49.8.5.62
4、吧代码放在49.8.5.62这个服务器上,程序运行起来
用户可以通过IP进行访问
方式二:如果是自己测试用的就可以用这种方式。先在自己本地的文件中找
C:\Windows\System32\drivers\etc 找到HOST,修改配置
然后吧域名修改成自己的本地服务器127.0.0.1
加上配置:app.config["SERVER_NAME"] = "haiyan.com:5000"
重定向示例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from flask import Flask,render_template,redirectapp = Flask(__name__)@app.route('/index',methods=['GET','POST'],redirect_to='/new')def index(): return "老功能"@app.route('/new',methods=['GET','POST'])def new(): return '新功能'if __name__ == '__main__': app.run() |
当用户访问/index时会自动重定向到/new
给多个视图添加装饰器
from flask import Flask,render_template,redirect
app = Flask(__name__)
import functools
def wapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
print('before')
return func(*args,**kwargs)
return inner
@app.route('/xxxx',methods=['GET','POST'])
@wapper
def index():
return "Index"
@app.route('/order',methods=['GET','POST'])
@wapper
def order():
return "order"
if __name__ == '__main__':
app.run()
首先要注意的是,装饰器要添加在路由下面,还有就是给多个视图添加同一个装饰器时,由于装饰器装饰后就相当于在执行inner函数,如果视图没有定义endpoint那么都会默认使用函数名,也就是inner,这时多个函数都是inner,会报错,所以要使用
@functools.wraps(func)来保留原函数的函数名等信息
视图函数
Flask中的CBV模式
"""
class IndexView(views.View):
methods = ['GET']
decorators = [wapper, ]
def dispatch_request(self):
print('Index')
return 'Index!'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index1')) # name=endpoint
"""
class IndexView(views.MethodView):
methods = ['GET']
decorators = [wapper, ] # 给所有请求增加装饰器,可以增加多个
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index2')) # name=endpoint
if __name__ == '__main__':
app.run()
Flask中的FBV模式
方式一:
@app.route('/index',endpoint='xx')
def index(nid):
url_for('xx',nid=123)
return "Index"
方式二:
def index(nid):
url_for('xx',nid=123)
return "Index"
app.add_url_rule('/index',index)
请求和响应
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): # 请求相关信息 # request.method # request.args # request.form # request.values # request.cookies # request.headers # request.path # request.full_path # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 响应相关信息 # return "字符串" # return render_template('html模板路径',**{}) # return redirect('/index.html') # response = make_response(render_template('index.html')) # response是flask.wrappers.Response类型 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response return "内容" if __name__ == '__main__': app.run()
from flask import Flask,url_for,request,redirect,render_template,jsonify,make_response
from urllib.parse import urlencode,quote,unquote
app = Flask(__name__)
@app.route('/index',endpoint='xx')
def index():
from werkzeug.datastructures import ImmutableMultiDict
=================
# get_data = request.args
# get_dict = get_data.to_dict()
# get_dict['xx'] = '18'
# url = urlencode(get_dict)
# print(url)
====================
# print(request.query_string)
# print(request.args)
==========================
# val = "%E6%8A%8A%E5%87%A0%E4%B8%AA"
# print(unquote(val)) #吧上面这样的数据转换成中文
#
# return "Index"
# return "Index"
# return redirect()
# return render_template()
# return jsonify(name='alex',age='18') #相当于JsonResponse
=======================
response = make_response('xxxxx') ##如果是返回更多的值,cookie,headers,或者其他的就可用它
response.headers['xxx'] = '123123'
return response
if __name__ == '__main__':
# app.__call__
app.run()
返回json格式数据
from flask import Flask,render_template,redirect,request,jsonify,make_response
return json.dumps({}) # return jsonify({})
模板
Flask使用的是Jinja2模板,所以其语法和Django基本无差别
自定义方法
from flask import Flask,render_template,redirect,request,jsonify,make_response,Markup
app = Flask(__name__)
def gen_input(value):
# return "<input value='%s'/>" %value
return Markup("<input value='%s'/>" %value)
@app.route('/x1',methods=['GET','POST'])
def index():
context = {
'k1':123,
'k2': [11,22,33],
'k3':{'name':'oldboy','age':84},
'k4': lambda x: x+1,
'k5': gen_input, # 当前模板才能调用的函数
}
return render_template('index.html',**context)
当函数返回的是字符串时,为了防止xss攻击,加了验证,所以页面上显示字符串的形式,解决办法,有两种方式
在后端Markup
v5 = Markup("<input type='text' />")
在前端
{{ v4|safe }}
将这些参数传给前端后,前端页面可以采用类似与django的方式渲染
<h1>{{k1}}</h1>
<h1>{{k2.0}} {{k2[0]}} </h1>
<h1>{{k3.name}} {{k3['name']}} {{k3.get('name',888)}}</h1>
<h1>{{k4(66)}}</h1>
<h1>{{k5(99)}}</h1>
这里可以看出flask的渲染方式更贴近python,很多python的语法可以使用,执行函数时也不像django,可以自己加括号执行,还可以传参数
写一个函数在所有的页面都使用
template_global和template_filter
@app.template_global()
def sb(a1, a2):
return a1 + a2
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
这么定义后在前端所有的页面都能调用这两个函数
调用方式:{{sb(1,2)}} {{ 1|db(2,3)}}
filter方法的第一个参数就是|的值
模板继承:和django的一样,extends
先定义一个母板
<!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>
<div >头部</div>
<div>
{% block content %} {% endblock %}
</div>
<div >底部</div>
</body>
</html>
然后继承它
{% extends 'layout.html'%}
{% block content %}
<h1>{{k1}}</h1>
<h1>{{k2.0}} {{k2[0]}} </h1>
<h1>{{k3.name}} {{k3['name']}} {{k3.get('name',888)}}</h1>
<h1>{{k4(66)}}</h1>
<h1>{{k5(99)}}</h1>
{% endblock%}
session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
-
设置:session['username'] = 'xxx'
- 删除:session.pop('username', None)
"""
1. 请求刚刚达到
ctx = RequestContext(...)
- request
- session=None
ctx.push()
ctx.session = SecureCookieSessionInterface.open_session
2. 视图函数
3. 请求结束
SecureCookieSessionInterface.save_session()
"""
from flask import Flask,session
app = Flask(__name__)
app.secret_key = 'sadfasdfasdf'
@app.route('/x1')
def index():
# 去ctx中获取session
session['k1'] = 123
session['k2'] = 123
del session['k2']
return "Index"
@app.route('/x2')
def order():
print(session['k1'])
return "Order"
if __name__ == '__main__':
app.run()
# 1. 请求一旦到来
# app.__call__
# app.wsgi_app
# app.open_session
session的操作和字典相同,默认情况下session存在COOKIE中
源码简单剖析,之前我们已经知道在执行app.run时实际上执行的就是run_simple方法,也就是app(),这时会调用Flask类中的__call__方法

这个方法实际上在执行app.wsgi_app

在wsgi_app方法中首先定义了一个ctx,我们看看self.request_context干了什么

可以看到这个方法实际上就是返回了RequestContext类的对象,而ctx就是这个类的对象,那么这个类中都定义了些什么呢

这里首先定义了request,然后也定义了session,所以这个ctx对象包含了request和session,定义request时,又用到了app中的request_class

这其实是一个类,所以request就是这个类的对象
定义玩ctx后又执行了ctx.push方法

在这个方法中定义了self.session,我们来看看self.app.open_session干了什么

这里实际上执行了self.session_interface.open_session,而session_interface又是一个新的类的对象

这里其实执行的是SecureCookieSessionInterface()的open_session方法

可以看到在这个方法中首先从cookies中获取session,如果是第一次访问,取不到val,则直接返回self.session_class(),如果有值,则将值反序列化,再返回self.session_class(data),我们再看看self.session_class做了什么

我们发现这又是一个类,所以上面返回的是这个类的对象

这个类继承了CallbackDict,而CallbackDict又继承了dict

所以我们知道了self.session_class返回的对象其实是一个特殊的字典,所以我们能像使用字典一样使用session
执行完ctx.push后接着要执行

这个方法其实会去执行我们的视图函数,然后返回

返回时执行了self.finalize_request

这个方法中又执行了self.process_response,在这个方法中又执行了一步save_session的操作

其实是在执行

而session_interface = SecureCookieSessionInterface(),所以我们又要找到SecureCookieSessionInterface中的save_session方法

它实际上就是将数据序列化后写入了cookie
所以session的整体流程就是先从cookie中读取,然后返回一个特殊的字典对象,我们可以修改,最后执行完视图函数后,我们又将修改后的session序列化,然后写入cookie
特殊的装饰器
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, Request, render_template
app = Flask(__name__, template_folder='templates')
app.debug = True
@app.before_first_request # 只有第一次请求来时执行,后面的都不执行
def before_first_request1():
print('before_first_request1')
@app.before_first_request
def before_first_request2():
print('before_first_request2')
@app.before_request # 类似与django中间件中的process_request
def before_request1():
Request.nnn = 123
print('before_request1')
@app.before_request
def before_request2():
print('before_request2')
@app.after_request
def after_request1(response):
print('before_request1', response)
return response
@app.after_request # 类似与django中间件中的process_response
def after_request2(response):
print('before_request2', response)
return response
@app.errorhandler(404) # 当报404错误时返回的内容
def page_not_found(error):
return 'This page does not exist', 404
@app.template_global() # 定义全局的函数,每个页面都能使用
def sb(a1, a2):
return a1 + a2
@app.template_filter() # 定义过滤函数
def db(a1, a2, a3):
return a1 + a2 + a3
@app.route('/')
def hello_world():
return render_template('hello.html')
if __name__ == '__main__':
app.run()
before_request和after_request
before_request是请求在执行视图函数之前会执行的,after_request是请求执行完视图函数会执行的
before_request如果返回None则会继续向后执行,当有多个时会按顺序执行,如果有返回值,那么后面的将不会执行,视图函数也不会执行,而是会按倒序执行after_request(这里和django的中间件有些不同)
有多个before_request时会按顺序执行
有多个after_request时会按倒序执行
闪现
session存在在服务端的一个字典里面,session保存起来,取一次里面还是有的,直到你删除之后才没有了
1、本质:flash是基于session创建的,flash支持往里边放值,只要你取一下就没有了,相当于pop了一下。不仅把值取走,而且把session里的东西去掉
2、闪现有什么用?
from flask import Flask,session,Session,flash,get_flashed_messages,redirect,render_template,request
app = Flask(__name__)
app.secret_key ='sdfsdfsdf'
@app.route('/users')
def users():
# 方式一
# msg = request.args.get('msg','')
# 方式二
# msg = session.get('msg')
# if msg:
# del session['msg']
# 方式三
v = get_flashed_messages()
print(v)
return render_template('users.html',msg=v)
@app.route('/useradd')
def user_add():
# 在数据库中添加一条数据
# 假设添加成功,在跳转到列表页面时,显示添加成功
# 方式一
# return redirect('/users?msg=添加成功')
# 方式二
# session['msg'] = '添加成功'
# 方式三
flash('添加成功')
return redirect('/users')
if __name__ == '__main__':
app.run(debug=True)
flash中的值被取一次就会删除,不会保留
flash还可以分类设置
from flask import Flask, session, flash, get_flashed_messages
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'
@app.route('/x1', methods=['GET', 'POST'])
def login():
flash('我要上向龙1', category='x1')
flash('我要上向龙2', category='x2')
return "视图函数x1"
@app.route('/x2', methods=['GET', 'POST'])
def index():
data = get_flashed_messages(category_filter=['x1'])
print(data)
return "视图函数x2"
if __name__ == '__main__':
app.run()
中间件
通过上面的分析我们知道当请求过来时会先执行app.run-->run.simple-->app()-->__call__-->app.wsgi_app
如果我们自己定义了wsgi_app方法,那么就不会执行原来的,而会执行我们自己定义的了,利用这一点我们可以自己定义一个,让请求来了在执行app.wsgi_app之前和之后能执行一些我们自己的逻辑
from flask import Flask
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'
@app.route('/x2',methods=['GET','POST'])
def index():
return "x2"
class Middleware(object):
def __init__(self,old_wsgi_app):
"""
服务端启动时,自动执行
:param old_wsgi_app:
"""
self.old_wsgi_app =old_wsgi_app
def __call__(self, environ, start_response):
"""
每次有用户请求道来时
:param args:
:param kwargs:
:return:
"""
print('before')
obj = self.old_wsgi_app(environ, start_response)
print('after')
return obj
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
"""
1.执行app.__call__
2.在调用app.wsgi_app方法
"""
这里要注意在执行app.wsgi_app之前我们无法使用request和session,只能使用最原始的数据
蓝图
在之前我们使用flask时所有的内容都是写在一个python文件中的,如果项目规模大了的话,这样做显然是不行的,所以我们需要创建一些项目的目录

目录结构类似django,manage.py是启动文件,内容如下
from s8pro import app
if __name__ == '__main__':
app.run()
在导入s8pro时会执行该目录下的__init__文件,这个文件的内容
from flask import Flask from .views import account from .views import admin from .views import user app = Flask(__name__) app.register_blueprint(account.ac) app.register_blueprint(admin.ad) app.register_blueprint(user.us)
这里我们实例化了一个app对象,然后将它和其它的蓝图关联
accout.py
from flask import Blueprint,render_template
import redis
ac = Blueprint('ac',__name__)
@ac.route('/login')
def login():
conn = redis.Redis()
return render_template('login.html')
@ac.route('/logout')
def logout():
return '123'
admin.py
from flask import Blueprint
ad = Blueprint('ad',__name__,url_prefix='/admin')
@ad.before_request
def bf():
print('before_request')
@ad.route('/home')
def home():
return 'home'
@ad.route('/xxxx')
def xxxx():
return 'xxxx'
user.py
from flask import Blueprint
us = Blueprint('us',__name__)
@us.route('/info')
def info():
return 'info'
在每个视图文件中我们都可以示例化一个蓝图对象,它的使用和app一样,最后只要把app和蓝图对象关联就可以访问到了
蓝图还可以定义访问url的前缀,类似于django中的include
ad = Blueprint('ad',__name__,url_prefix='/admin')
这样访问时url必须是/admin/***
蓝图中还可以定义before_request等方法,访问时只有访问到该蓝图中的内容时才会执行该方法,不是全局的
蓝图用于为应用提供目录划分:
小型应用程序:示例
大型应用程序:示例
其他:
- 蓝图URL前缀:xxx = Blueprint('account', __name__,url_prefix='/xxx')
- 蓝图子域名:xxx = Blueprint('account', __name__,subdomain='admin')
# 前提需要给配置SERVER_NAME: app.config['SERVER_NAME'] = 'xxx.com:5000'
# 访问时:admin.xxx.com:5000/login.html
浙公网安备 33010602011771号