Flask总结

1.配置文件

2.before_request/after_request

3.路由系统

4.视图

5.模板

6.session

7.flash

8.蓝图(blueprint)

flask-组件

1.flask-session

2.DBUtils

3.wtforms

4.SQLAlchemy

5.flask-script

6.flask-migrate

偏函数

上下文管理

用flask写一个hello world文件

from flask import Flask
app=Flask(__name__)
@app.route("/index")
def index():
    return "Hello Word"
if __name__=="__main__":
    app.run()

       配置文件    

1.新建settings.py 存放配置文件
class Config(object):
    DEBUG = False
    SECRET_KEY = "asdfasdfasdf"
#上线环境
class ProductionConfig(Config):
    DEBUG = False
#开发环境
class DevelopmentConfig(Config):
    DEBUG = True
#测试环境
class TestingConfig(Config):
    TESTING = True
其他文件要引入配置文件中的内容 如:
 app.config.from_object('settings.DevelopmentConfig')
引入整个py文件:app.config.from_pyfile("settings.py")

   before_request和after_request  

before_request和after_request
相当于Django中的中间件
after_request的视图中必须有参数,且必须有返回值
1.before_request---->目标请求 ----->after_request
@app.before_request
def a1():
    print("a2")
@app.after_request
def c1(response):
    print("c1")
    return response
def index():
    print("index")
    return render_template("index.html")
执行顺序:a2--->index---->c1
2.在走before_request时如果返回了值,后面的before_request就不再执行,目标函数也不执行
会把after_request都走一遍
@app.before_request
def a1():
    print("a1")
@app.before_request   
def d1(response):
    print("d1")
    return response
@app.after_request
def c1(response):
    print("c1")
    return response
    return response
def index():
    print("index")
    return render_template("index.html")
执行顺序: a1--->d1--->c1

    路由系统   

路由的两种注册方法:
    1.使用装饰器
        @app.route("/login")
    2. app.add_url_rule(rule='/login', view_func=index)
1.定义methods
    @app.route("/login",methods=['GET','POST'])
2.动态路由
    URL: http://www.xx.com/index/1/88
    @app.route('/index/<string:nid>/<int:num>')
也可以 @app.route('/index/<nid>/<num>')
      def index(a,b):pass
3.endpoint ,为url起别名,根据别名可以反向生成URL(默认endpoint是函数名)
  url_for:反向解析
  url_for()它把函数名称作为第一个参数。它可以接受任意个关键字参数,每个关键字参数对应URL中的变量
  1.自定义endpoint
      @app.route('/user/<username>',endpoint="aaa")
    def profile(username):print(url_for('aaa',username='zhao')  /user/zhao
  2.使用默认的endpoint
    @app.route('/login')
    def login():pass
    @app.route('/user/<username>')
    def profile(username):pass
    with app.test():
        print(url_for('login')) /login
        print(url_for('login', next='/')) /login?next=/
        print(url_for('profile', username='zhao')) /user/zhao
4.支持自定义正则
@app.route('/index/<string:nid>/<int:num>/<regex("\d+"):xxxx>')
5.支持FBV和CBV
from flask import Flask,url_for,views
class IndexView(views.MethodView):
    methods = ['GET','POST']
    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='ci')) name="ci" 指别名
View Code

         视图     

from flask import request,render_template,redirect
1.请求:
    request.method
    request.args
    request.form
    request.cookies
    request.headers
    request.path
    request.full_path
    request.url
    request.files
    obj = request.files['the_file_name']
2.响应:
    1.return "xxxx" 返回字符串
    2.return render_template("index.html",msg="xxx") 返回模板
      return render_template('html模板路径',**{"mag="xxx”,arg":12})
    3.return redirect() 重定向
    4.response = make_response(render_template('index.html'))
    5. response.set_cookie('key', 'value')
    6. response.delete_cookie('key')
    7.return send_file()
    def get_img(file_name):
    file_path = os.path.join(setting.RESOURCE_IMG_PATH,file_name)
    return send_file(file_path)
@app.route('/setco')
def setco():
    from flask import make_response
    obj = make_response("返回内容...")
    obj.set_cookie('a1','123')
    return obj
给视图添加装饰器:
        - 装饰器必须设置functools.wappers
        - 紧挨着放在视图之上

from functools import wraps
def is_login(func):
    @wraps(func) #保留函数元信息
    def inner(*args,**kwargs):
        user_info = session.get('user')
        if not user_info:
            return redirect('/login')
        ret=func()
        return ret
    return inner
@app.route("/book")
@is_login  #必须紧贴着函数
def book():
    return "Book"
View Code

         模板     

Flask使用的是Jinja2模板,所以其语法和Django无差别
1.支持python语法
2.支持模板继承
3.自定义函数

服务端:
def jerd():
    return '<h1>jerd</h1>'
@app.route('/login', methods=['GET', 'POST'])
def login():
    return render_template('login.html', ww=jerd)
HTML:需要加()和safe
<body>
    {{ww()|safe}}
</body>
4.全局函数
1.global:
    @app.template_global()
    def test4(arg):
        return arg + 100
    所有视图中都可以调用:
        {{ test4(666) }}
2.filter:
    @app.template_filter()
    def db(a1, a2, a3):
        return a1 + a2 + a3
    所有视图中都可以调用:
        {{ 1|db(2,3)}}

     Session   

session的本质是字典
flask的session存放在cookie中
1.导入session
from flask import session
2.设置key值:app.secret_key="xxxxx"
3.操作:
    1.设置:session["zhao"]="zgf"
    2.获取:session.get("zhao")
    3.删除:del session.["zhao"]
在客户端的cookie中能看到session和sessionid

cookie和session的区别?
    cookie,是保存在用户浏览器端的键值对,可以用来做用户认证。
    session,将用户会话信息保存在服务端{adfasdfasdf:"...",99dfsdfsdfsd:'xxx'},依赖cookie将每个用户的随机字符串保存到用户浏览器上;
    
    django,session默认保存在数据库;django_session表
    flask,session默认将加密的数据写用户cookie中。

      flash,闪现   

基于session实现
from flask import flash,get_flashed_messages
    @app.route('/set_flash')
    def set_flash():
        flash('666',category='error')
        flash('999',category='log')
        return "设置成功"
    
    @app.route('/get_flash')
    def get_flash():
        data = get_flashed_messages(category_filter=['error'])
        print(data)
        return "或成功"

    蓝图(blueprint)  

app---蓝图----视图
1. 目录结构的划分(解耦)。
2. 单蓝图中应用before_request
3. URL划分
    app.register_blueprint(account,url_prefix='/user')
4. 反向生成URL
    url_for('蓝图.函数名')
    url_for('蓝图.endpoint')
    url_for('蓝图.endpoint',nid=1)
    print(url_for('account.login'))
在app下注册蓝图
app=Flask(__name__)
app.register_blueprint(bpmanager)
在视图中:
from flask import Blueprint
bpmanager=Blueprint('bpmanager',__name__)
@bpmanager.route("/delete/",methods=["GET","POST"])
def delete():pass

    flask-session   

1.flask-session的作用:
将flask中的session由加密存到cookie中的方式更换为放置到其他的数据源
如:redis/memcached/filesystem/mongodb/sqlalchemy(数据库)
2.简述flask-session的原理?
1.请求进来走完before-request后走到open-session方法,该方法从cookie中读取session_id
对应的随机字符串。
2.如果未获取到这个字符串,就创建一个随机字符串并在内存中创建个特殊的字典
如果能获取到这个字符串,就根据这个随机字符串去redis中获取原来设置的值,并在在内存创建字典
3.在视图函数中,对内存中的字典进行操作
4.当请求结束时,执行save_session方法,
1.该方法去读取内存中特殊的字典,并将字典序列化成字符串
2.将字符串写到redis中
3.将随机字符串写到cookie中
操作:
1.安装:pip3 install flask-session
2.在配置文件中:
    SESSION_TYPE='redis'
    SESSION_REDIS = Redis(host='127.0.0.1',port=6379)
3.引入session
from flask_session import Session
app = Flask(__name__)
Session(app)

   DBUtils 数据库连接池 

1.pip3 install DBUtils 
2.配置连接池:
import pymysql
from DBUtils.PooledDB import PooledDB, SharedDBConnection
POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=20,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=0,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123456',
    database='day118',
    charset='utf8'
)
使用:
    conn = POOL.connection()
    cursor = conn.cursor()
    cursor.execute('select * from users')
    result = cursor.fetchall()
    conn.close()
    
将mysql数据库的操作模块化:
def on_open(cur=pymysql.cursors.DictCursor):
    conn = POOL.connection()
    cursor = conn.cursor(cursor=cur)
    return conn,cursor

def on_close(conn,cursor):
    cursor.close()
    conn.close()

def fetchone(sql,args,cur=pymysql.cursors.DictCursor):
    """
    获取单条数据
     获得的数据默认以字典的形式,如果要以元祖展示,传参时设置cur=None
    """
    conn,cursor = on_open(cur)
    cursor.execute(sql, args)
    result = cursor.fetchone()
    return result

def fetchall(sql,args,cur=pymysql.cursors.DictCursor):
    """
    获取多条数据
    """
    conn, cursor = on_open(cur)
    cursor.execute(sql, args)
    result = cursor.fetchall()
    return result

def exec_sql(sql,args,cur=pymysql.cursors.DictCursor):
    """
    添加/删除/修改
    :param sql: insert into table(%s,%s) values(....)
    """
    conn, cursor = on_open(cur)
    cursor.execute(sql, args)
    conn.commit()
1.DBUtils的作用:
    创建数据库连接池
2.简述数据库连接池的原理?
    1.启动时会在内存中维护一个连接池
    2.当请求需要连接数据库时则去连接池中获取一个连接,如果有空闲的连接就去获取
     没有则等待或报错
    3.使用完毕后,需要将连接归还到连接池中
3.连接池在最开始启动时,最大连接数是100,是创建了100个链接吗?
不是
View Code

  wtforms:对用户请求数据做表单验证  

pip3 install wtforms 
https://www.cnblogs.com/wupeiqi/articles/8202357.html

自定义校验类:
from wtforms.fields import simple
from wtforms.fields import html5
from wtforms.fields import core
from wtforms import widgets
from wtforms import validators
from wtforms import Form


class TestForm(Form):
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired()
        ],  #错误信息
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'}  #错误信息样式
    )
    """
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    pwd_confirm = simple.PasswordField(
        label='重复密码',
        validators=[
            validators.DataRequired(message='重复密码不能为空.'),
            validators.EqualTo('pwd', message="两次密码输入不一致")
        ], #自定义了比较
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    email = html5.EmailField(
        label='邮箱',
        validators=[
            validators.DataRequired(message='邮箱不能为空.'),
            validators.Email(message='邮箱格式错误')
        ],
        widget=widgets.TextInput(input_type='email'),
        render_kw={'class': 'form-control'}
    )

    gender = core.RadioField(
        label='性别',
        choices=(
            (1, ''),
            (2, ''),
        ),
        coerce=int
    )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(
        label='爱好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.SelectMultipleField(
        label='喜好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        widget=widgets.ListWidget(prefix_label=False),
        option_widget=widgets.CheckboxInput(),
        coerce=int
    )
    """
    cls_id = core.SelectField(
        label='请选择班级',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
        choices=[],
        coerce=int
    )
    #重写构造方法
    def __init__(self, *args, **kwargs):
        super(TestForm, self).__init__(*args, **kwargs)

        self.cls_id.choices = sqlhelper.fetchall(sql='select id,title from classes',args=[],cur=None)
    #钩子函数
    def validate_name(self,field):
        """
        对name进行验证时的钩子函数
        :param field:
        :return:
        """
        if field != 'root':
            raise validators.ValidationError("用户名必须是root")
1.choices字段需要设置coerce=int 
2.从数据库显示到页面上的数据要重写构造方法进行实时更新
3.钩子函数:validate_字段名():pass 字段验证后执行定义的钩子函数
服务端:
1.get请求:
     if request.method == 'GET':
        form = TestForm()
        return render_template('add_cls.html',form=form)
2.post请求。实例化时添加formdata再验证
form = TestForm(formdata=request.form)
if form.validate():
   数据都在form.data
   
页面:
    <form method="post" novalidate>
        <p>
            {{form.title.label}} : {{form.title}} <span style="color: red">{{form.title.errors.0}}</span>
        </p>
        <input type="submit" value="添加">
    </form>
View Code

   pipreqs:查看安装包依赖  

pip3 install pipreqs 
进入到项目里:pipreqs ./ --encoding=utf-8
安装依赖
install -r requirement.txt

virtualenv 创建虚拟环境(纯净的环境)

 

pip3 install virtualenv
在公司中询问是否安装虚拟环境
    1.需要指定目录:virtualenv  文件名      virtualenv  venv
    2.激活 进入到新建项目中的scripts中执行activate  
    cd venv\Scripts  执行active
    命令提示符变了,有个(venv)前缀,表示当前环境是一个名为venv的Python环境。
    3.退出:deactivate
    此时就回到了正常的环境,现在pip或python均是在系统Python环境下执行。
在linux中安装虚拟环境
1. pip3 install virtualenv
2.为virtualenv创建软连接
    ln -s /software/py3/bin/virtualenv /usr/bin/virtualenv1
    删除软连接:rm -rf /usr/bin/virtualenv1
3.在当前目录下创建虚拟环境  virtualenv1  venv1
4.激活 source venv1/bin/activate
5.退出:deactivate

 

   偏函数  

使用偏函数可以通过有效地“冻结”那些预先确定的参数,然后在运行时,当获得需要的剩余参数后,可以将他们解冻,传递到最终的参数中
应用场景:调用sum函数时,我们已经知道了其中的一个参数,我们可以通过这个参数,重新绑定一个函数,然后加上其他参数去调用即可

def add(a, b):
    return a + b;
print(add(3, 5)) #8


使用偏函数
import functools
def add(a,b):
    return a+b
func1=functools.partial(add,3)
print(func1)   #functools.partial(<function add at 0x0000028C9C2CFBF8>, 2)
print(func1(5)) #8

   上下文管理   

 

 

RequestContext:封装请求上下文的对象
AppContext:封装应用上下文的对象
LocalStack : 通过此类实例来操作Local对象,提供了pop、top、push方法
Local:为每一个线程开辟一块独立的空间,用来存放数据
LocalProxy:通过此类中的方法来取出request属性和session属性
1.
请求上下文:实例化一个RequestContext对象,并将请求的所有相关信息request和session封装到RequestContext对象中
应用上下文:实例化一个AppContext对象,将current_app, g封装到AppContext中
2.通过LocalStack类将RequestContext对象,AppContext对象添加到Local中(调用push方法)
Local类为每一个线程开辟一块独立的空间,创建了一个字典来保存数据,这个字典的key是用线程的唯一标识存放自己的数据
3.在视图函数中调用request时会执行LocalProxy中对应的魔法方法__getattr__或__setitem__
又通过偏函数调用Localstark中top方法去Local获取到数据,取的是列表的最后一个值。
4.请求终止时还是通过LocalStack的pop方法 将Local中将值在列表中pop掉

内置的session流程:
push中会调用session里面open_session方法,通过这个方法帮助我们获取用户原有的session信息,有就获取,没有就返回一个空的类似字典的数据结构,
赋值给对象中的session,当使用的时候触发LocalProxy对像里对魔法方法通过偏函数用Localstark中方法去Local获取到数据
使用完后,调用session对像的save_session方法,将数据加密写到用户的cookie中,这个操作在after_request之后
posted @ 2018-08-24 10:09  JERD  阅读(189)  评论(0编辑  收藏  举报