首页 > 技术文章 > flask项目创建

Pythonzrq 2019-12-11 16:22 原文

蓝图blueprint

帮助我们对flask程序进行目录结构的划分

flask     mtv
django    mvc

 

  • before_request
from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.before_request
    def f1():
        print('f1')

    from .views.account import account
    from .views.user import user

    app.register_blueprint(account)
    app.register_blueprint(user)
    return app
from flask import Blueprint,url_for

# 创建了一个蓝图对象
account = Blueprint('account',__name__)
# 两个必要参数'account'蓝图名字;'__name__'蓝图所在的模块或者包,一般为'__name__'变量
# account = Blueprint('account', __name__, url_prefix='/xx') #为url添加前缀,url为/xx/account才能访问account()函数

@account.before_request
def xx():
    print('xx')

 

 

  • url_for
from flask import Blueprint,url_for

# 创建了一个蓝图对象
account = Blueprint('account',__name__)

@account.before_request
def xx():
    print('xx')

@account.route('/login')
def login():
    url = url_for('account.register')
    print(url)
    return 'Login'

@account.route('/register')
def register():
    return 'register'

 

目录结构

 

 创建完成后显示一个crm文件

需要手动创建下图目录结构

 

 

 

例子

创建__init__.py文件

from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.before_request
    def f1():
        print('f1')

    from .views.account import account
    from .views.user import user

    app.register_blueprint(account)
    app.register_blueprint(user)
    return app
View Code

创建manage.py运行文件

from crm import create_app
app = create_app()

if __name__ == '__main__':
    app.run(port=8000)
View Code

创建static用来存放静态文件

创建templates用来存放模板文件

创建views用写视图

在视图创建两个文件

account.py

from flask import Blueprint,url_for

# 创建了一个蓝图对象
account = Blueprint('account',__name__)

@account.before_request
def xx():
    print('xx')

@account.route('/login')
def login():
    url = url_for('account.register')
    print(url)
    return 'Login'

@account.route('/register')
def register():
    return 'register'
View Code

user.py

from flask import Blueprint

user =Blueprint('user',__name__)

@user.route('/user/list')
def user_list():
    return 'user list'

@user.route('/user/add')
def user_add():
    return 'user add'
View Code

 

数据库连接池

原生sql   单例模式

pip3 install DBUtils

快速使用

import time
import threading


import pymysql
from DBUtils.PooledDB import PooledDB

# 创建数据库连接池
POOL = PooledDB(
    creator=pymysql,
    maxconnections=20,
    mincached=2,
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    database='pooldb',
    charset='utf8'
)

# 去数据库连接池获取一个连接
conn = POOL.connection()

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute('select * from tb1')
result = cursor.fetchall()


# 将此连接放还给连接池
conn.close()
View Code

flask应用

import pymysql

from DBUtils.PooledDB import PooledDB

class SQLHelper(object):
    def __init__(self):
        # 创建数据库连接池
        self.pool = PooledDB(
            creator=pymysql,
            maxconnections=5,
            mincached=2,
            blocking=True,
            host='127.0.0.1',
            port=3306,
            user='root',
            password='123',
            database='s23day02',
            charset='utf8'
        )


    def connect(self):
        conn = self.pool.connection()
        cursor = conn.cursor()
        return conn,cursor


    def disconnect(self,conn,cursor):
        cursor.close()
        conn.close()


    def fetchone(self,sql,params=None):
        """
        获取单条
        :param sql:
        :param params:
        :return:
        """
        if not params:
            params = []
        conn,cursor = self.connect()
        cursor.execute(sql, params)
        result = cursor.fetchone()
        self.disconnect(conn,cursor)
        return result


    def fetchall(self,sql,params=None):
        """
        获取所有
        :param sql:
        :param params:
        :return:
        """
        import pymysql
        if not params:
            params = []
        conn, cursor = self.connect()
        cursor.execute(sql,params)
        result = cursor.fetchall()
        self.disconnect(conn, cursor)
        return result


    def commit(self,sql,params):
        """
        增删改
        :param sql:
        :param params:
        :return:
        """
        import pymysql
        if not params:
            params = []

        conn, cursor = self.connect()
        cursor.execute(sql, params)
        conn.commit()
        self.disconnect(conn, cursor)


db = SQLHelper()
View Code
from flask import Blueprint,url_for,request,render_template,session,redirect
from ..utils.sqlhelper import db

# 创建了一个蓝图对象
account = Blueprint('account',__name__)



@account.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')

    # 根据用户名和密码去数据库进行校验
    # 连接/SQL语句/关闭
    result = db.fetchone('select * from user where username=%s and password=%s',[user,pwd])
    if result:
        # 在session中存储一个值
        session['user_info'] = user
        return redirect(url_for('user.user_list'))
    return render_template('login.html',error="用户名或密码错误")
View Code

赠送: SQLhelper类

封装 了数据库操作的相关方法,以便之后业务功能调取.

多用于java

DBUtils连接池的应用及基于单例模式企业SQLHelper的封装

wtforms

对用户提交的数据进行格式校验. (django form )

pip3 install wtforms

知识点:

  • 定义类

    • 定义字段(label/validate/default/choices...)

    • 钩子函数

    • 重写init在init中去数据库中获取数据并赋值 self.hobby.choices = ...

  • 使用类

    • form = Form(),默认只展示标签.

    • form = Form(data={'name':'xx'}) 显示标签+默认值 (用于编辑)

    • form = Form(formdata=request.form) 用于接收用户提交的数据,

if form.validate():
    form.data
else:
    form.errors

 

配置相关

创建setting.py

class Base(object):
    CHARSET = 'utf8'
    PORT = 3306

class Prod(Base):
    HOST = '10.11.11.11'
    USER = 'test'
    PASSWORD = '123'
    DATABASE = 'dbtest'

class Test(Base):
    HOST = '13.16.55.89'
    USER = 'test'
    PASSWORD = '123'
    DATABASE = 'dbtest'

class Dev(Base):
    HOST = '127.0.0.1'
    USER = 'root'
    PASSWORD = '123'
    DATABASE = 's23day02'
View Code

在__init__下

def create_app():
    app = Flask(__name__)
    app.secret_key = '93jls98029ulsdu092ujlkd'

    app.config.from_object('settings.Dev')
   ...
   return app

调用时

from flask import current_app

current_app.config

 注意:程序加载的顺序

flask上下文管理相关

面向对象相关: setattr/getattr

class Foo(object):
    pass

obj = Foo()
obj.x1 = 123 
# object.__setattr__(obj,'x1',123)

print(obj.x1) 
# object.__getattr__(obj,'x1')

 

class Local(object):
    def __init__(self):
        # self.storage = {}
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        self.storage[key] = value

    def __getattr__(self, item):
        return self.storage[item]

obj = Local()

obj.x1 = 123

print(obj.x1)

 

import threading
"""
{
    1231:{x1:0},
    1432:{x1:1},
    ...
    5321:{x1:9}
}
"""
class Local(object):
    def __init__(self):
        # self.storage = {}
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        # self.storage[key] = value
        ident = threading.get_ident() # 1233
        if ident in self.storage:
            self.storage[ident][key] = value
        else:
            self.storage[ident] = {key:value}

    def __getattr__(self, item):
        ident = threading.get_ident()
        return self.storage[ident][item]

obj = Local()

def task(arg):
    obj.x1 = arg
    print(obj.x1)

for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()

 

 flask上下文管理机制?

 

当用户请求到来之后,flask内部会创建两个对象:
    ctx = ReqeustContext(),内部封装request/sesion
    app_ctx = AppContext(),内部封装app/g
然后会将此对象通过各自的LocalStack对象:
    _request_ctx_stack = LocalStack()
    _app_ctx_stack = LocalStack()
    将各自的对象添加到local中.

Local是一个特殊结构,他可以为每个线程(协程)维护一个空间进行存取数据. 
LocalStack的作用是将Local中维护成一个栈.
内部更细节的结构我也研究过:
    storage = {
        1212:{stack:[ctx,]}
    }
    
    storage = {
        1212:{stack:[app_ctx,]}
    }
    
    
视图函数如果想要获取:request/session/app/g,只需要导入即可,导入的本质是去各自storage中获取各自的对象,并调用封装其内部:request/session/app/g. (获取栈顶的数据top)

请求处理完毕,将各自storage中存储的数据进行销毁. 

 

 flask离线脚本

  • 离线脚本
    • web运行时: 启动网站,等待用户通过浏览器发来请求.
      离线脚本: 写一个py文件临时做一些事情.  

面向对象的特殊方法

class Foo:
    
    def __enter__(...):
        pass
    
    def __exit__(...):
        pass
    
obj = Foo()
with obj:
    pass

flask的配置文件

app = Flask(__name__)

# 去对象中读取配置文件
app.config.from_object('xx.xxx') # 5

# 获取配置文件
print(app.config) # 15

@app.route('/index')
def index():
    return "123"
    
app.run()

app对象中包含了配置文件.

上下文管理基本流程

当用户请求到来时候,flask会将ctx/app_ctx对象放到各自的local中,以后再去获取.

离线脚本

把文件的内容导到数据库
把某个数据库表中的数据导出到另外一个数据库等.
import pymysql
from flask import current_app
from crm import create_app

# 方式一:获取配置文件
# app = create_app()
# print(app.config)

# 方式二:获取配置文件(居多)

app1 = create_app()
# 将此对象放到Local中
with app1.app_context():
    print(current_app.config) # top
    app2 = create_app()
    with app2.app_context():
        print(current_app.config) # top
    print(current_app.config) # top

看Flask源码流程

项目启动,即:运行之后发生了什么?

from flask import Flask

# 1.实例化Flask对象,执行Flask类的 __init__方法.
app = Flask(__name__)

@app.route('/login')
def login():
    return "login"

@app.route('/index')
def index():
    return "index"

if __name__ == '__main__':
    app.run()

处理用户请求阶段

  执行 __call__方法

 

 

 

 

 

 

什么是g?

 

相当于是flask中一次请求周期中可用的一个 "全局变量",本质上是放在local对象的app_ctx中. 

应用场景:权限校验.

 

 

总结

  1. 偏函数

  2. threading.loal对象

    obj = threading.local()
    
    obj.x1 = 123

     

  3. 蓝图
    面试题:flask蓝图和django的app有什么区别?
        - 相同:
            可以做业务的划分,django可以将不同业务放到不同app中,flask放在蓝图中. 
            使用时,都需要注册.
            支持在URL中添加前缀.
        - 不同点:
            django的app比flask蓝图涵盖的东西更多. models
            url反向解析,flask中要加蓝图为前缀; django和app无关.
            before request的粒度.
            
    目录结构实例

     

  4. DBUtils
  5. wtforms
  6. flask相关问题

你都用过flask相关的哪些组件?

DBUtils
wtforms

推荐阅读