1.入门程序
from flask import Flask
app = Flask(__name__)
app.run()
flask提供了自动重启服务的功能:调试模式
app.run(debug=True)
2.定义路由
@route('/user/<username>')
@route('/post/<int:uid>')
@route('/post/<float:price>')
@route('/post/<path:path>')
路由装饰器:
def route(self, rule, **options): """Like :meth:`Flask.route` but for a blueprint. The endpoint for the :func:`url_for` function is prefixed with the name of the blueprint. """ def decorator(f): endpoint = options.pop("endpoint", f.__name__) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
from flask import Flask
app = Flask(__name__)
# 视图函数
@app.route("/hello/") -->/hello /hello/
def hello():
return "hello world"
app.run()
基于类的视图(即插视图)
另一种注册路由的方式:app.add_url_rule("/hello", view_func=hello)
@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
<img src="{{ url_for('static', filename='/js/jquery.js') }}" />
参数一:endpoint
3.配置文件
# 导入配置文件 app.config.from_object('app.secure') app.config.from_object('app.setting') app.config['DEBUG'] --配置文件的变量必须大写 在业务逻辑层使用配置文件: from flask import current_app current_app.config['KEY']
4.返回结果
4.1 json 【API】
对于基本类型:
return flask.jsonify(data)
对于对象类型:
return json.dumps(obj, default=lambda o:o.__dict__)
4.2 context-type:text/plain 返回纯文本
from flask import make_response
# 默认 content-type:text/html
# 返回response对象
headers = {
'content-type':'application/json',
# 'content-type':'text/plain',
'location':'www.baidu.com'
}
response = make_response("hello wolrdd", 200)
response.headers = headers
# return response
# return 'hello flask', 200, headers
4.3 返回带cookie的页面
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
5.session
session['k']=value
# session.pop()
6.将视图函数拆分多个文件中
7.request参数
7.1 url?q=xxx&page=xxx形式的参数,【get请求】
通过request对象获取请求参数:
from flask import request
request.args['q'] 或 request.args.get('q')
request.args是不可变字典, 转化成可变字典:a = request.args.to_dict()
7.2 form post
request.form接受post请求参数
7.3 URL方式接受参数
@app.route('/detail/<int:uid>') def detail(uid): pass
8.WTFroms参数验证,验证层
安装WTForms:pipenv install wtforms
from wtforms import Form, StringField, IntegerField from wtforms.validators import Length, NumberRange, DataRequired class SearchForm(Form): # DataRequired要求参数不能为空格 q = StringField(validators=[DataRequired(message='关键字不能为空格'), Length(min=1,max=30, message='关键字的长度应该在1-30')]) page = IntegerField(validators=[NumberRange(min=1, max=20)], default=1)
自定义验证器:
业务需求:对邮箱进行验证,除了验证形式的合法性,还要验证数据库中是否存在
class RegisterForm(Form): email = StringField(validators=[DataRequired(), Length(8, 64), Email(message='邮箱格式不合法')]) password = StringField(validators=[DataRequired(message='密码不能为空'), Length(6,32)]) nickname = StringField(validators=[DataRequired(), Length(2, 10, message='昵称长度2-10')]) # 这个函数由WTForm自动调用,field为email属性 def validate_email(self, field): if User.query.filter_by(email=field.data).first(): raise ValidationError('电子邮箱已经被注册') # 如果不合法,抛出指定的异常
视图函数:
from flask import Flask, make_response, jsonify, request from app.forms.book import SearchForm from . import web # 参数 @web.route('/book/search', methods=['GET', 'POST']) def search(): form = SearchForm(request.args) if form.validate(): print('验证通过') q = form.q.data.strip() #去空格 page = form.page.data # 调用业务逻辑层 result = {'q': q, 'page': page} return jsonify(result) else: print('验证失败') return jsonify(form.errors)
9.数据库操作【ORM】 code first sqlalchemy ->flask_SQLAlchemy
安装flask_SQLAlchemy:pipenv install flask-sqlalchemy
安装数据库驱动:mysq->cymysql 或者pymysql
from sqlalchemy import Column, Integer, String, Float class Book(object): id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) author = Column(String(20), nullable=False) price = Column(Float, nullable=True, default=0) isbn = Column(String(15), nullable=False, unique=True) summery = Column(String(200), default='没有简介')
10.AppContext, RequestContext, Flask, Request关系【LocalProxy】
12.模板渲染
render(html, data)
对于css、js文件,直接返回给客户端
13.静态文件【css,js,images..】和 模板文件【HTML】
Flask内部仍然使用路由、视图函数对静态文件进行返回。
静态文件的默认位置:__name__/static
模板文件的默认位置:__name__/templates
修改默认位置:
app = Flask(__name__, static_folder='view_model/static', templates_folder=' xxx ') # 相对路径
模板文件也可以放在蓝图下面:在蓝图的__init__文件,Blueprint('web', templates_folder=' xxx')
14. 模板引擎:jinja2 【官网:http://jinja.pocoo.org/docs/2.10/】
14.1 基本设置
为了让IDE【pycharm】更友好点(定位模板页面的路径),将对应文件夹设置为模板文件夹:
设置模板引擎:
实例:
@web.route('/index', methods=['GET']) def index(): data = { 'name':'张三', 'age':20 } return render_template('index.html', data=data)
对于字典和对象类型的data,可以使用点来访问,也可以使用索引
<body> <div> <p>姓名:{{data.name}}</p> <p>年龄:{{data.age}}</p> </div> </body>
字符串【raw】:默认字符串当做纯字符串
{% set str='<input type="text">' %} {{ str }}
如果需要HTML解析的字符串,在后台使用Markup(‘’)构造
宏定义:
{% macro xx(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}" /> {% endmacro %} {{ xx('age') }}
14.2 流程控制语句
所有的流程控制语句都要被包含在{% %}内。
(1)条件判断语句
比较运算符、and 、or都可以使用
{% if data.age < 20 %} {{ data.name }} {% elif data.age == 20 %} <p>年龄为 20</p> {% else %} <p>年龄大于20</p> {% endif %}
(2)循环语句
{% for i in range(5) %} <h3>{{ i }}</h3> {% endfor %}
14.3 使用模板继承
模板页:layout.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>鱼书</title> </head> <body> {% block head %} head {% endblock %} {% block content %}
content {% endblock %} {% block foot %} footer {% endblock %} </body> </html>
继承模板页:
{% extends 'layout.html' %} {% block content %} {{ super() }} <p>姓名:{{data.name}}</p> <p>年龄:{{data.age}}</p> {% if data.age < 20 %} {{ data.name }} {% elif data.age == 20 %} <p>年龄为 20</p> {% else %} <p>年龄大于20</p> {% endif %} {% for i in range(5) %} <h3>{{ i }}</h3> {% endfor %} {% endblock %}
14.4 过滤器【filter】
default:
{{ data.name | default('未名',False) }} 如果不存在name属性,显示未名,【异常处理】
boolean=True => None,False, '', {}, [] -->default
str = False or 'hello' ->hello
str = None or 'hello' ->hello
str = '' or 'hello' ->hello
length:
{{ data | length() }} 输出data的长度
16.消息闪现
@web.route('/index', methods=['GET']) def index(): data = { 'name':'张三', 'age':20 } flash('登录成功', category='ok') flash('成功进入系统', category='error') return render_template('index.html', data=data)
使用消息:
使用set定义的变量,作用域为blocks的全局变量
{% set message = get_flashed_messages(category_filter='error') %} {{ message }}
19.密码加密 【werkzeug.security】
from werkzeug.security import generate_password_hash
20.redirect重定向
redirect(url_for('web.login')) # 蓝图名.endpoint
21.cookie
response = make_response()
response.set_cookie(key, value, timeout)
flask提供了管理登录的插件:pipenv install flask_login 帮助文档
login_user(user) 并不是将用户的所有信息都保存到cookie下,而是只需要保存用户的唯一标识ID。因此,在User类中需要定义一个方法get_id(self): return self.id
或者让User继承UserMixin,该类提供了更多的登录相关的方法、属性。
访问权限控制:插件装饰器
user.py
@login_manager.user_loader def get_user(uid): return User.query.get(int(uid))
gift.py
@web.route('/my/gift') @login_required def my_gift(): return 'my gift'
@web.route('/login', methods=['GET', 'POST']) def login(): form = LoginForm(request.form) if request.method == 'POST' and form.validate(): user = User.query.filter_by(email=form.email.data).first() if user and user.check_password(form.password.data): # 使用登录管理器 管理登录 login_user(user, remember=True) next = request.args.get('next') if not next or not next.startswith('/'): next = url_for('web.index') return redirect(next) else: flash('账号不存在或者密码错误') return render_template('auth/login.html', form=form)