Flask 是一个基于 Python 开发并且依赖 jinja2 模板和 Werkzeug WSGI 服务的一个微型框架,对于 Werkzeug 本质是 Socket 服务端,其用于接收 http 请求并对请求进行预处理,然后触发 Flask 框架,开发人员基于 Flask 框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助 jinja2 模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
"微"(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的 "微" 意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是 "微小" 的,但它已准备好在需求繁杂的生产环境中投入使用。
pip3 install flask
- #Flask依赖一个实现wsgi协议的模块:werkzeug
- from werkzeug.wrappers import Request, Response
- @Request.application
- def hello(request):
- return Response('Hello World!')
- if __name__ == '__main__':
- from werkzeug.serving import run_simple
- run_simple('localhost', 4000, hello)
flask 依赖 wsgi,实现 wsgi 模块:wsgiref,werkzeug,uwsgi
- Django:无socket,依赖第三方模块wsgi,中间件,路由系统(CBV,FBV),视图函数,ORM。cookie,session,Admin,Form,缓存,信号,序列化。。
- Flask:无socket,中间件(需要扩展),路由系统,视图(CBV)、第三方模块(依赖jinja2),cookie,session弱爆了
- from flask import Flask
- app = Flask(__name__)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
template_folder:模板所在文件夹的名字
root_path:可以不用填,会自动找到,当前执行文件,所在目录地址
在 return render_template 时会将上面两个进行拼接,找到对应的模板地址
static_folder:静态文件所在文件的名字,默认是 static,可以不用填
static_url_path:静态文件的地址前缀,写成什么,访问静态文件时,就要在前面加上这个
- app = Flask(__name__, template_folder = 'templates', static_url_path = '/xxxxxx')
如:在根目录下创建目录,templates 和 static,则 return render_template 时,可以找到里面的模板页面;如在 static 文件夹里存放 11.png,在引用该图片时,静态文件地址为:/xxxxxx/11.png
- #方式一
- @app.route('/index.html',methods=['GET','POST'],endpoint='index')
- def index():
- return 'Index'
- #方式二
- def index():
- return "Index"
- self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) #endpoint是别名
- or
- app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"])
- app.view_functions['index'] = index
添加路由关系的本质:将 url 和视图函数封装成一个 Rule 对象,添加到 Flask 的 url_map 字段中
- from flask import Flask,render_template,request,redirect,session
- app = Flask(__name__)
- app.secret_key = "sdsfdsgdfgdfgfh" # 设置session时,必须要加盐,否则报错
- def wrapper(func):
- def inner(*args,**kwargs):
- if not session.get("user_info"):
- return redirect("/login")
- ret = func(*args,**kwargs)
- return ret
- return inner
- @app.route("/login",methods=["GET","POST"]) # 指定该路由可接收的请求方式,默认为GET
- def login():
- if request.method=="GET":
- return render_template("login.html")
- else:
- # print(request.values) #这个里面什么都有,相当于body
- username = request.form.get("username")
- password = request.form.get("password")
- if username=="haiyan" and password=="123":
- session["user_info"] = username
- # session.pop("user_info") #删除session
- return redirect("/index")
- else:
- # return render_template("login.html",**{"msg":"用户名或密码错误"})
- return render_template("login.html",msg="用户名或者密码错误")
- @app.route("/index",methods=["GET","POST"])
- @wrapper #自己定义装饰器时,必须放在路由的装饰器下面
- def index():
- # if not session.get("user_info"):
- # return redirect("/login")
- return render_template("index.html")
- if __name__ == '__main__':
- app.run(debug=True)
debug = True 是指进入调试模式,服务器会在 我们的代码修改后, 自动重新载入,有错误的话会提醒,每次修改代码后就不需要再手动重启
点击查看详情
- - request
- - request.form #POST请求的数据
- - request.args #GET请求的数据,不是完全意义上的字典,通过.to_dict可以转换成字典
- - request.querystring #GET请求,bytes形式的
- - response
- - return render_tempalte()
- - return redirect()
- - return ""
- v = make_response(返回值) #可以把返回的值包在了这个函数里面,然后再通过.set_cookie绑定cookie等
- - session
- - 存在浏览器上,并且是加密的
- - 依赖于:secret_key
GET 请求:
URL 为: http://127.0.0.1:5000/login?name='胡冲'&nid=2
- from urllib.parse import urlencode,quote,unquote
- def login():
- if request.method == 'GET':
- s1 = request.args
- s2 = request.args.to_dict()
- s3 = urlencode(s1)
- s4 = urlencode(s2)
- s5 = unquote(s3)
- s6 = unquote(s4)
- s7 = quote("胡冲")
- print('s1',s1)
- print('s2',s2)
- print('s3',s3)
- print('s4',s4)
- print('s5',s5)
- print('s6',s6)
- print('s7',s7)
- return render_template('login.html')
- #############结果如下####################
- s1 ImmutableMultiDict([('name', "'胡冲'"), ('nid', '2')])
- s2 {'name': "'胡冲'", 'nid': '2'}
- s3 name=%27%E8%83%A1%E5%86%B2%27&nid=2
- s4 name=%27%E8%83%A1%E5%86%B2%27&nid=2
- s5 name='胡冲'&nid=2
- s6 name='胡冲'&nid=2
- s7 %E8%83%A1%E5%86%B2
- 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目录
- @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'])
1
2
3
4
5
6
7
8
9
|
DEFAULT_CONVERTERS = {
'default'
'string'
'any'
'path'
'int'
'float'
'uuid'
}
|
endpoint("name") #别名,相当于 django 中的 name
- from flask import Flask, url_for
- @app.route('/index',endpoint="xxx") #endpoint是别名
- def index():
- v = url_for("xxx")
- print(v)
- return "index"
- @app.route('/zzz/<int:nid>',endpoint="aaa") #endpoint是别名
- def zzz(nid):
- v = url_for("aaa",nid=nid)
- print(v)
- return "index2"
- @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),#当为False时,url上加不加斜杠都行
- 访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
- @app.route('/index',strict_slashes=True)#当为True时,url后面必须不加斜杠
- 仅访问 http://www.xx.com/index
- redirect_to=None, 由原地址直接重定向到指定地址,原url有参数时,跳转到的新url也得传参,注意:新url中不用指定参数类型,直接用旧的参数的类型
- 如:
- @app.route('/index/<int:nid>', redirect_to='/home/<nid>') # 访问index时,会直接自动跳转到home,执行home的函数,
- 不执行index的
- 或
- 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"
- # =============== 子域名访问============
- @app.route("/static_index", subdomain="admin")
- def static_index():
- return "admin.bjg.com"
- # ===========动态生成子域名===========
- @app.route("/index",subdomain='<xxxxx>')
- def index(xxxxx):
- return "%s.bjg.com" %(xxxxx,)
扩展 Flask 的路由系统,让他支持正则, 这个类必须这样写,必须去继承 BaseConverter
- from flask import Flask,url_for
- from werkzeug.routing import BaseConverter
- app = Flask(__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
- # 添加到converts中
- app.url_map.converters['regex'] = RegexConverter
- # 进行使用
- @app.route('/index/<regex("\d+"):nid>',endpoint='xx')
- def index(nid):
- url_for('xx',nid=123)
- return "Index"
- if __name__ == '__main__':
- app.run()
- def auth(func):
- def inner(*args, **kwargs):
- result = func(*args, **kwargs)
- return result
- return inner
- class IndexView(views.MethodView):
- # methods = ['POST'] #只允许POST请求访问
- decorators = [auth,] # 如果想给所有的get,post请求加装饰器,就可以这样来写,也可以单个指定
- def get(self): #如果是get请求需要执行的代码
- v = url_for('index')
- print(v)
- return "GET"
- def post(self): #如果是post请求执行的代码
- return "POST"
- app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) #name即FBV中的endpoint,指别名
- if __name__ == '__main__':
- app.run()
View Code
- 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 = "把几个"
- # 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()
Flask 使用的是 Jinja2 模板,所以其语法和 Django 无太大差别
Flask 中模板里面,执行函数时,需要带()才执行
方法一:在后端使用 Markup,等价于 Django 里的 mark_safe
- v = Markup("<input type='text' />")
方法二:在前端使用 safe
{{v1|safe}}
Flask 中自定义模板方法的方式和 Bottle 相似,创建一个函数并通过参数的形式传入 render_template,
run.pyindex.html
- from flask import Flask,url_for,render_template,Markup
- app = Flask(__name__)
- def test(a,b): #自定义的标签,此方法在使用时,需要在render_temlate中传入到指定以页面使用
- return a+b
- @app.template_global() # 不需要传入,可直接在页面使用
- def sb(a1, a2):
- return a1 + a2 + 100
- @app.template_filter() #不需要传入,使用时要在一个值(此值作为第一个参数传入到过滤器中)的后面加入|,然后再加参数
- def db(a1, a2, a3):
- return a1 + a2 + a3
- @app.route('/index')
- def index():
- v1 = "字符串"
- v2 = [11,22,33]
- v3 = {"k1":"v3","sdf":"sdgfgf"}
- v4 = "<input type='text' />"
- v5 = Markup("<input type='text' />")
- return render_template("index.html",v1=v1,v2=v2,v3=v3,v4=v4,v5=v5,test=test)
- if __name__ == '__main__':
- app.run(debug=True)
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width">
- <title>Title</title>
- </head>
- <body>
- {{ v1 }}
- <ul>
- {% for foo in v2 %}
- <li>{{ foo }}</li>
- {% endfor %}
- {{ v2.1 }}
- {% for k,v in v3.items() %}
- <li>{{ k }} {{ v }}</li>
- {% endfor %}
- {{ v3.k1 }}
- {{ v3.get("k1") }}
- {{ v4|safe }}
- {{ v5 }}
- <h1>{{ test(1,2) }}</h1>
- <p>{{ sb(1,2) }}</p>
- <p>{{ 1| db(2,3) }}</p>
- </ul>
- </body>
- </html>
PS: 模板继承的方法和 django 的一样。
只有定义的东西在很多地方去使用的时候才去用它,
html
- { % macro xx(name, type = 'text', value = '') %
- } < input type = "{{ type }}"name = "{{ name }}"value = "{{ value }}" >
- <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
- <
- input
- type
- ="{{ type }}"
- name
- ="{{ name }}" value="{{ value }}">
- {% endmacro %}
- {{ xx('n1') }}
相当于在页面上定义了一个名为 xx 的'函数',这个函数接收 3 个参数,我们给 type 和 value 写上了默认值,此时调用,我们还需要传入一个参数,我们此时传入了一个 n1,则
页面上会生成 3 个 input 框,name 都为 n1
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
- from flask import Flask,url_for,session
- app = Flask(__name__)
- app.secret_key = "sdsfdgdgdgd"
- app.config['SESSION_COOKIE_NAME'] = 'session_lvning' #设置session的名字
- @app.route('/index/')
- def index(nid):
- #session本质上操作的是字典, 所有对session操作的方法与字典方法相同
- #session的原理:如果下一次访问的时候带着随机字符串,会把session里面对应的
- # 值拿到内存,假设session保存在数据库,每执行一次链接一次数据库,每次都要时时更新的话,会非常损耗数据库的效率
- session["xxx"] = 123
- session["xxx2"] = 123
- session["xxx3"] = 123
- session["xxx4"] = 123
- del session["xxx2"] #在这删除了,真正存储的时候是没有xxx2的
- return "ddsf"
- if __name__ == '__main__':
- app.run()
关于 session 的配置
- app.config['SESSION_COOKIE_NAME'] = 'session_lvning'
- - session超时时间如何设置? 'PERMANENT_SESSION_LIFETIME': timedelta(days=31)
- 以下是跟session相关的配置文件
- """
- '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, #是否每次都跟新
- 'PERMANENT_SESSION_LIFETIME': timedelta(days=31)
基本使用
自定义 Session
第三方 session
蓝图用于为应用提供目录划分:
小型应用程序: 示例
大型应用程序: 示例
其他:
session 存在在服务端的一个字典里面,session 保存起来,取一次里面还是有的,直到你删除之后才没有了
flash 是基于 session 创建的,flash 支持往里边放值,只要你取一下就没有了,相当于 pop 了一下。不仅可以拿到值,而且可以把其从 session 里的去掉,
基于 Session 实现的用于保存数据的集合,其特点是:使用一次就删除。
某个数据仅需用一次时,可以使用闪现
- 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()# 获取flash中的值
- print(v)
- msg = ''
- return render_template('users.html',msg=msg)
- @app.route('/useradd')
- def user_add():
- # 在数据库中添加一条数据
- # 假设添加成功,在跳转到列表页面时,显示添加成功
- # 方式一
- # return redirect('/users?msg=添加成功')
- # 方式二
- # session['msg'] = '添加成功'
- # 方式三
- flash('添加成功')
- return redirect('/users')
- if __name__ == '__main__':
- app.run(debug=True)
在函数执行之前或函数执行之后想做点事情,有 2 种方式
第一种:装饰器
第二种:flask 里面的扩展,相当于 django 中的中间件
- from flask import Flask,session,Session,flash,get_flashed_messages,redirect,render_template,request
- app = Flask(__name__)
- app.secret_key ='sdfsdfsdf'
- @app.before_request
- def process_request1():
- print('process_request1')
- @app.after_request
- def process_response1(response):
- print('process_response1')
- return response
- @app.before_request
- def process_request2():
- print('process_request2')
- @app.after_request
- def process_response2(response): #参数也得有
- print('process_response2')
- return response #必须有返回值
- @app.route('/index')
- def index():
- print('index')
- return 'Index'
- @app.route('/order')
- def order():
- print('order')
- return 'order'
- @app.route('/test')
- def test():
- print('test')
- return 'test'
- if __name__ == '__main__':
- app.run()
运行结果:
还有一个 @app.before_first_request:表示,当程序运行起来,第一个请求来的时候就只执行一次,下次再来就不会在执行了
View Code
- 自定义标签和过滤器在页面上的调用方式:{{sb(1,2)}} {{ 1|db(2,3)}}
来源: https://www.cnblogs.com/huchong/p/8227606.html