初识Flask
Flask介绍
flask是一个短小精悍、可扩展性强的一个Web框架。
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
werkzeug
werkzeug是实现WSGI的一个模块
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简单使用
from flask import Flask app=Flask(__name__) @app.route('/index') def index(): return 'hello world' if __name__ == '__main__': app.run()
配置文件
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.from_object("settings.DevelopmentConfig") 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
import_name, static_url_path=None, 设置静态文件url static_folder='static', 默认静态文件路径 static_host=None, host_matching=False, subdomain_matching=False, template_folder='templates', 默认模板路劲 instance_path=None, instance_relative_config=False, root_path=None
路由系统
- @app.route('/user/<username>')
- @app.route('/post/<int:post_id>')
- @app.route('/post/<float:post_id>')
- @app.route('/post/<path:path>')
- @app.route('/login', methods=['GET', 'POST'])
路由系统参数:
@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'] = 'wupeiqi.com:5000' @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "static.your-domain.tld" @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()
反向生成url
- endpoint,反向生成URL,默认函数名 @app.route('/index/',methods=['GET'],endpoint='n1') def hello(): url=url_for('n1') #没有定义endpoint url=url_for('hello') - 动态路由: @app.route('/index/<int:nid>',methods=['GET','POST']) def index(nid): print(nid) #动态路由反向生成:url_for("index",nid=777) return "Index"
路由系统的使用:
FBV
def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner @app.route('/index.html',methods=['GET','POST'],endpoint='index') @auth def index(): return 'Index' 或 def index(): return "Index" self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) or app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) app.view_functions['index'] = index
CBV
def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner class IndexView(views.View): methods = ['GET'] decorators = [auth, ] def dispatch_request(self): print('Index') return 'Index!' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint 或 class IndexView(views.MethodView): methods = ['GET'] decorators = [auth, ] def get(self): return 'Index.GET' def post(self): return 'Index.POST' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
自定义正则
from flask import Flask,url_for app = Flask(__name__) # 步骤一:定制类 from werkzeug.routing import BaseConverter 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 # 步骤二:添加到转换器 app.url_map.converters['reg'] = RegexConverter """ 1. 用户发送请求 2. flask内部进行正则匹配 3. 调用to_python(正则匹配的结果)方法 4. to_python方法的返回值会交给视图函数的参数 """ # 步骤三:使用自定义正则 @app.route('/index/<reg("\d+"):nid>') def index(nid): print(nid,type(nid)) print(url_for('index',nid=987)) return "index" if __name__ == '__main__': app.run()
请求
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 获取get方法的参数 # request.form 获取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 "内容" if __name__ == '__main__': app.run()
响应
# 响应相关信息 return "字符串" return render_template('html模板路径',**{}) return redirect('/index') #自定制响应头 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
模板
基本数据类型
基本数据类型:可以执行python语法,如:
#字典 {{values.get('name')}} {{values['name']}} #列表 {{values[0]}} #函数 {{func(可传参数)}} #循环 {%for item in xxx%} {%endfor%} 传入函数 - django,自动执行 - flask,不自动执行
全局定义函数
@app.template_global() def sb(a1, a2): # {{sb(1,9)}} return a1 + a2 @app.template_filter() def db(a1, a2, a3): # {{ 1|db(2,3) }} return a1 + a2 + a3
模板(继承、块、安全、宏定义)
继承和块
- 模板继承 layout.html <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>模板</h1> {% block content %}{% endblock %} </body> </html> tpl.html {% extends "layout.html"%} {% block content %} {{users.0}} {% endblock %}
include
include {% include "form.html" %} form.html <form> asdfasdf asdfasdf asdf asdf </form>
宏定义
相当于定义了一个函数,
在全局都可以调用
{% macro ccccc(name, type='text', value='') %} <h1>宏</h1> <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> <input type="submit" value="提交"> {% endmacro %} {{ ccccc('n1') }} {{ ccccc('n2') }}
安全
安全 - 前端: {{u|safe}} - 后端: MarkUp("<a></a>")
Session
当请求刚到来:flask读取cookie中session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoib2xkYm95,
将该值解密并反序列化成字典,放入内存以便视图函数使用。
视图函数: @app.route('/ses') def ses(): session['k1'] = 123 session['k2'] = 456 del session['k1'] return "Session" session['xxx'] = 123
当请求结束时,flask会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。
闪现
闪现,在session中存储一个数据,读取时通过pop将数据移除。
from flask import Flask,flash,get_flashed_messages @app.route('/page1') def page1(): flash('临时数据存储','error') flash('sdfsdf234234','error') flash('adasdfasdf','info') return "Session" @app.route('/page2') def page2(): print(get_flashed_messages(category_filter=['error'])) return "Session"
中间件
call方法什么时候出发? - 用户发起请求时,才执行。 任务:在执行call方法之前,做一个操作,call方法执行之后做一个操作。 class Middleware(object): def __init__(self,old): self.old = old def __call__(self, *args, **kwargs): ret = self.old(*args, **kwargs) return ret if __name__ == '__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run()
特殊装饰器
@before_request
#在执行视图函数之前,requset都要经过(before_request)
@after_request(response)
必须:return response
#在执行视图函数之后,requset都要经过(after_request)
@after_request/@before_request实例
@app.before_request def x1(): print('before:x1') return '滚' @app.before_request def xx1(): print('before:xx1') @app.after_request def x2(response): print('after:x2') return response @app.after_request def xx2(response): print('after:xx2') return response @app.route('/index') def index(): print('index') return "Index"
@template_global
模板中有使用方法
@template_filter
模板中有使用方法
@errorhandler
出现错误时,自定制返回的内容
@app.errorhandler(404) def not_found(arg):
#arg 错误信息 print(arg) return "没找到"
蓝图
目标:给开发者提供目录结构
1.创建与项目名相同的文件夹
2.在该文件下创建__init__.py
from flask import Flask def create_app(): app = Flask(__name__) return app
3.在与文件同一级的目录下创建manage.py
from crm import create_app app=create_app() if __name__ == '__main__': app.run()
4.创建该项目的静态文件和模板
5.蓝图的关系的创建
user.py
from flask import Blueprint us =Blueprint('us',__name__) @us.route('/login') def login(): return 'login' @us.route('/logout') def logout(): return 'logout'
__init__.py
from flask import Flask from .views.user import us def create_app(): app = Flask(__name__) app.register_blueprint(us) return app
6.蓝图前缀+before方法
可以为每个蓝图加上前缀,比如127.0.0.1/admin/xxx/xxx
127.0.0.1/web/xxx/xxx
加上前缀:
在__init__.py文件里面:
from flask import Flask from .views.user import us from .views.account import ac def create_app(): app = Flask(__name__) app.register_blueprint(us,url_prefix='/admin') app.register_blueprint(ac,url_prefix='/web') return app
- 在__init__.py 里面加上@before_request 方法把所有的请求都会经过
- 在每一个蓝图加上@before_request 那么就意味着只有该蓝驱的所有请求经过
before_request源码流程图
示例
Session+Flask实现用户登录
from flask import Flask,render_template,request,session,redirect app=Flask(__name__) #session 如果需要用到session 需要加盐. # 本质上他是放在cookies里,并不是像django一样放在数据库 app.secret_key ='213fdasfa123df' @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') if user=='ming' and pwd=='123': session['user_info']={'username':user} return redirect('/index') return render_template('login.html',error='用户名或密码错误') # return render_template('login.html',**{'error':'用户名或者密码错误'}) @app.route('/index') def index(): user_info=session.get('user_info') if user_info: return render_template('index.html') return redirect('/login') if __name__ == '__main__': app.run()
学生管理
@app.route('/index') def index(): if not session.get('user'): return redirect(url_for('login')) return render_template('index.html',stu_dic=STUDENT_DICT) #版本二: import functools def auth(func): @functools.wraps(func) def inner(*args,**kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args,**kwargs) return ret return inner @app.route('/index') @auth def index(): return render_template('index.html',stu_dic=STUDENT_DICT) #应用场景:比较少的函数中需要额外添加功能。 #版本三:before_request @app.before_request def xxxxxx(): if request.path == '/login': return None if session.get('user'): return None return redirect('/login')