Flask
引入
1. 下载:pip3 install flask
2. 特点:短小、精悍、
3. 依赖wsgi:werkzurg
学习werkzurg:
实例一:
from werkzeug.wrappers import Request,Response from werkzeug.serving import run_simple def run(environ, str_response): return [b'abc'] if __name__ == '__main__': run_simple('localhost', 4000, run)
示例二:
from werkzeug.wrappers import Request,Response from werkzeug.serving import run_simple @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': run_simple('localhost', 4000, hello)
Flask示例:
from flask import Flask, render_template,request,redirect,session app = Flask(__name__) # 加盐,否则使用session会报错 app.secret_key = 'assxax' @app.route('/login', methods=['GET','POST']) # 路由采用装饰器 def login(): if request.method == 'GET': return render_template('login.html') # request.args -> get传出来的参数 user = request.form.get('user') pwd = request.form.get('pwd') if user == 'cui' and pwd == '123': session['user']=user # session存放在加密的cookie中 # 登陆成功 return redirect('/index') return render_template('login.html', error='用户名或密码错误') # return render_template('login.html', **{'error':'用户名或者密码错误'}) @app.route('/index') def index(): user = session.get('user') if not user: return redirect('/login') return render_template('index.html') if __name__=='__main__': app.run()
1. 谈谈你对django和flask的认识? django提供了大量的组件,比如ORM,admin,form,modelform,缓存等,只需要配置在settings中就可以使用 flask 轻量级,短小精悍但可扩展性强,适用于开发小型网站,同时提供大量第三方组件,flask与第三方组件结合类似于Django 2. flask和django最大不同点: flask中的 request/session 都是导入进来的 django的request是参数,session依附于request对象中。 3. flask知识点 - 模板+静态文件配置在 app=Flask(__name__, ...)中配置 - 路由 @app.route('/index',methods=['GET','POST']) - 请求 request.form request.args request.method - 响应 return '' return render return redirect - session session['xx'] = 123 session.get('xx')
Flask基础
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
反向解析 endpoint='n1'(给路由起个别称) / url_for('index', nid=999) 动态路由: @app.route('/index/<int:nid>', methods=['GET', 'POST'], endpoint='n1') def index(nid): print(nid) print(url_for('n1', nid=999)) # /index/999 return 'Index' 路由+视图 a. 路由设置的两种方式 #方式1 @app.route('/xxx') def index(): return 'Index' # 方式2 def index(): return 'Index' app.add_url_rule('/xxx', None, index) 注意事项: - 不要让endpoint重名 - 如果重名,函数也一定要相同 b. 参数 rule, URL规则 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') view_func, 视图函数名称 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 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, 重定向到指定地址 subdomain=None, 子域名访问 c. CBV import functools from flask import Flask, views app = Flask(__name__) def wrapper(func): @functools.wraps(func) def inner(*args, **kwargs): return func(*args, **kwargs) return inner class UserView(views.MethodView): methods = ['GET'] # 表示只允许get请求 decorators = [wrapper, ] # 加装饰器 def get(self, *args, **kwargs): return 'GET' def post(self, *args, **kwargs): return 'POST' app.add_url_rule('/user', None, UserView.as_view('uuu')) if __name__ == '__main__': app.run() d. 自定义正则 from flask import Flask, views, 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()
FBV
request.method request.args 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 "字符串" return jsonify({'k1':'v1'}) return render_template('html模板路径',**{}) / return render_template('html模板路径',xx=xx) return redirect('/index') 定制响应头: obj = make_response('index') obj.headers['xxx'] = '123' # 设置响应头 obj.set_cookie('key', 'value') return obj
- 给前端传数据的方式: @app.route('/tpl') def tpl(): context = { 'users':['alex', 'fengxiao'] } # 方式一 return render_template('tpl.html', **context) # 后台调用用: {{users.0}}/ {{users[0]}} # 方式二 return render_template('tpl.html', context=context) # 后台调用用: {{context.users.0}} / {{context.users[0]}} # 方式三 return render_template('tpl.html', **{'context':context}) # 后台调用用: {{context.users.0}} / {{context.users[0]}} - 基本数据类型:可以执行python语法,如:dict.get() list['xx] - 传入函数 -django,自动执行 -flask, 不自动执行 - 全局定义模板函数 # 直接可以在前端模板中使用,不需要在后端传过去 @app.template_global() # 调用方式:{{xx(7,3)}} def xx(a1, a2): return a1+a2 @app.template_filter() # 调用方式: {{2|x2(4,4)}} def x2(a1, a2, a3): return a1+a2+a3 - 模板的继承 - include {% include 'xx.html' %} - 模板中定义函数,多次使用 {% macro ccc(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {{ ccc('n1') }} {{ ccc('n2') }} - 安全 - 前端: {{txt|safe}} - 后端: 'txt': Markup('<input type="text">')
-- 存在加密的cookie中,放在浏览器 -- 当请求刚到来,flask读取cookie中session对应的值:eyJrMiI6NDU2fQ.Xnh0JQ.o56bxIwkRdAH86A8C7YSVZn5ApA。将该值解密并反序列化成字典,放入内存 视图函数: @app.route('/ses') def ses(): session['k1'] = 123 session['k2'] = 456 del session['k1'] return 'Session' 当请求结束后,flask会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。
from flask import Flask, flash, get_flashed_messages @app.route('/page1') def page1(): flash('临时数据存储', 'error') flash('sxd', 'error') flash('数据存储', 'info') return 'Session' @app.route('/page2') def page2(): print(get_flashed_messages(category_filter=['error'])) # 将打印['临时数据存储', 'sxd', '临时数据存储', 'sxd'],且只能取一次 return 'Session'
- call方法什么时候触发? 在用户发起请求时执行 - 任务:要求在执行call方法之前,做一个操作,call方法执行之后做一个操作 - 方案一:改源码 - 方案二:添加中间件 class Middleware(object): def __init__(self, old): self.old = old def __call__(self, *args, **kwargs): print('前') ret = self.old(*args, **kwargs) print('后') return ret if __name__ == '__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run()
目标:给开发者提供目录 其他: 自定义模板,静态文件 某一类url添加前缀 给一类url添加before_request 示例: 目录结构: crm -crm -templates -login.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>login</h1> </body> </html> -views account.py from flask import Blueprint, render_template ac = Blueprint('ac', __name__) @ac.before_request def x1(): print('app.before_request') @ac.route('/login') def login(): return render_template('login.html') @ac.route('/logout') def logout(): return 'Logout' user.py from flask import Blueprint uc = Blueprint('uc', __name__) @uc.route('/list') def list(): return 'list' @uc.route('/detail') def detail(): return 'detail' -__init.py from flask import Flask from .views.account import ac from .views.user import uc def create_app(): app = Flask(__name__) app.register_blueprint(ac) app.register_blueprint(uc, url_prefix='/api') # url_prefix给路径前面指定前缀 - http://127.0.0.1:5000/api/list return app manage.py from crm import create_app app = create_app() if __name__ == '__main__': app.run()
示例程序:学生管理加认证功能 ** 版本一:直接给每个函数加认证 @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 == 'cui' and pwd == '123': session['user'] = user # session存放在加密的cookie中 注意:一旦用到session就要在配置中加入 SECRET_KEY = 'assxax' # 登陆成功 return redirect('/index') return render_template('login.html', error='用户名或密码错误') @app.route('/delete/<int:nid>') def delete(nid): if not session.get('user'): return redirect(url_for('login')) del STUDENT_DICT[nid] return redirect(url_for('index')) 版本二:使用装饰器:适用比较少的函数添加额外功能 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('/delete/<int:nid>') @auth def delete(nid): del STUDENT_DICT[nid] return redirect(url_for('index')) 版本三:before_request @app.before_request # 类似于django的中间件,在每个视图访问之前执行 def xxx(): if request.path=='/login': return None # 直接通过 if session.get('user'): return None return redirect('/login') 装饰器归纳: 1. before_request *** 2. after_request *** 示例: from flask import Flask app = Flask(__name__) @app.before_request def x1(): print('before:x1') # before_request若有return 则后续直接执行所有的after_request @app.before_request def xx1(): print('before:xx1') @app.after_request def x2(request): print('after:x2') return request @app.after_request def xx2(request): print('after:xx2') return request @app.route('/index') def index(): print('index') return 'Index' # 执行顺序:x1 xx1 xx2 x2 if __name__ == '__main__': app.run() 3. before_first_request # 只执行一次 4. template_filter 5. template_global 6. errorhandler 定制错误信息/页面 @app.errorhandler(404) def not_found(arg): print(arg) return 'error' 知识点: Q:给一个字符串路径'settings.Foo',要求找到该路径下的类,以及获取类中的大写字段 test.py: import importlib path = 'settings.Foo' p,c = path.rsplit('.',maxsplit=1) m = importlib.import_module(p) cls = getattr(m, c) for key in dir(cls): if key.isupper(): print(key,getattr(cls,key)) settings.py: class Foo: DEBUG = True LOST = False
作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离) import threading from threading import local import time obj = local() def task(i): obj.xx = i time.sleep(2) print(obj.xx, i) for i in range(10): t = threading.Thread(target=task, args=(i,)) t.start() 问题: - 如何获取一个线程的唯一标记 -- threading.get_ident() import threading def task(i): print(threading.get_ident(), i) for i in range(10): t = threading.Thread(target=task, args=(i,)) t.start() - 根据字典自定义一个类似于threading.local功能? import threading, time DICT = {} # {123:{'xx':1}} def task(i): ident = threading.get_ident() if ident in DICT: DICT[ident]['xx'] = i else: DICT[ident] = {'xx': i} time.sleep(2) print(DICT[ident]['xx'], i) for i in range(10): t = threading.Thread(target=task, args=(i,)) t.start() - 根据字典自定义一个为每个协程开辟空间进行存取数据? import threading, time, greenlet DICT = {} def task(i): ident = greenlet.getcurrent() # 协程 # ident = threading.get_ident() # 线程 if ident in DICT: DICT[ident]['xx'] = i else: DICT[ident] = {'xx': i} time.sleep(2) print(DICT[ident]['xx'], i) for i in range(10): t = threading.Thread(target=task, args=(i,)) t.start() - 通过getattr/setattr 构造出threading.local的加强版(协程) import threading import time try: import greenlet get_ident = greenlet.getcurrent except Exception as e: get_ident = threading.get_ident class Local(object): DIC = {} def __getattr__(self, item): ident = get_ident() if ident in self.DIC: return self.DIC[ident].get(item) return None def __setattr__(self, key, value): ident = get_ident() if ident in self.DIC: self.DIC[ident][key] = value else: self.DIC[ident] = {key: value} obj = Local() def task(i): obj.xx = i time.sleep(2) print(obj.xx) for i in range(10): t = threading.Thread(target=task, args=(i,))
上下文管理:
1. 上下文管理: request
a. wsgi -> __call__ -> wsgi_app
b. ctx = RequestContext(session, request)
ctx.push()
c. LocalStack, 把ctx对象添加到local中
d. Local
__storage__={
1321:{stack: [ctx,]}
}
e. 视图函数
ctx.request
ctx.session(取值为空)
2. 上下文管理: session
a. wsgi -> __call__ -> wsgi_app
b. ctx = RequestContext(session, request)
ctx.push()
c. LocalStack, 把ctx对象添加到local中
d. Local
__storage__={
1321:{stack: [ctx,]}
}
e. 视图函数
ctx.request
获取ctx.session,给session赋值(从cookie中读取数据) => open_session
其他:
- app
- g
请求上下文管理(ctx):request,session:
- 请求到来之后wsgi会触发__call__方法,由__call__方法再次调用wsgi_app方法
- 在wsgi_app方法中:
- 首先将请求相关+空session封装到一个RequestContext对象中,即:ctx
- 将ctx交给LocalStack对象,再由LocalStack将ctx添加到Local中,
Local结构(LocalStack作用?):
__storage__ = {
1231:{stack: [ctx,]}
}
- 在请求的cookie中,提取名称 为sessionid对应的值,对cookie进行加密+反序列化,再次赋值给ctx中的session
-> 视图函数
- 把session中的数据再次写入cookie中
- 将ctx删除
- 结果返回给用户浏览器
- 断开socket连接
flask-session - 把默认session保存在redis中
pip3 install flask-session import redis from flask import Flask, session, request # from flask.globals import _request_ctx_stack # from flask.sessions import SecureCookieSessionInterface from flask_session import Session app = Flask(__name__) # app.session_interface = SecureCookieSessionInterface() app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = redis.Redis(host='111.11.11', port=6379, password='123') Session(app) @app.route('/login') def login(): session['user'] = 'alex' return 'aff' @app.route('/home') def index(): print(session.get('user')) return 'xx' if __name__=='__main__': app.run() - 原理 - session数据保存到redis session:随机字符串:1:q2qwqdsxsxsdsdsdsd session:随机字符串:2:sxxzcsxsxsdsdsdsdz session:随机字符串:3:zxzxxzzdsxsxsdsdsdsd - 随机字符串返回给用户 随机字符串 源码: from flask_session import RedisSessionInterface
补充:
import functools def index(a1, a2): return a1 + a2 # 原来的调用方式 # ret = index(1, 23) # print(ret) # 偏函数,帮助开发者自动传递参数 new_func = functools.partial(index, 666) ret = new_func(1) print(ret)
class Base(object): def func(self): print('Base') class Foo(Base): def func(self): # 方式1 根据mro的顺序执行方法 super(Foo,self).func() # 方式2 主动执行Base类的方法 Base.func(self) print('Foo') obj = Foo() print(obj.func) ########################################################## class Base(object): def func(self): super(Base, self).func() print('Base.fun') class Bar(object): def func(self): print('Bar.fun') class Foo(Base, Bar): pass # 示例1 obj = Foo() obj.func() print(Foo.__mro__) # 示例2 会报错 'super' object has no attribute 'func' obj = Base() obj.func()
class Stack(object): def __init__(self): self.data = [] def push(self, val): return self.data.append(val) def pop(self): return self.data.pop() def top(self): return self.data[-1] _stack = Stack() _stack.push('alex') _stack.push('ex') print(_stack.pop()) print(_stack.pop())
__init__ __str__ __repr__ __new__ 单例/rest_framework序列化 __call__, flask源码请求入口, django请求入口 __getattr__ __setattr__ __delattr__ __setitem__ __getattr__ __delitem__ eg: class Foo(object): def __getitem__(self, item): return 1 def __setitem__(self, key, value): pass def __delitem__(self, item): pass obj = Foo() print(obj['k1']) # __getitem__ obj['k1'] = 123 # __setitem__ del obj['k1'] # __delitem__ __setattr__和__setitem__区别: class Student(): def __setattr__(self, key, value): print('调用了setattr') self.__dict__[key] = value def __setitem__(self, key, value): print('调用了setitem') self.__dict__[key] = value s = Student() s.age = 1 # 调用__setattr__ 方法 s['name'] = 'tom' # 调用 __setitem__ 方法 __dict__ , api封装返回数据时候:BaseResponse __mro__, 继承顺序
def func(): pass class Foo(object): def func(self): pass # # 执行方式1 # obj = Foo() # obj.func() # 方法 # # 执行方式2 # Foo.func(12) # 函数
# @media属性 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> .pg{ width: 100%; background-color: saddlebrown; } @media (min-width: 666px) { .pg{ background-color: green; } } @media (min-width: 999px) { .pg{ background-color: red; } } </style> </head> <body> <div> <div class="pg">asz</div> </div> </body> </html>
-引擎: -innoDB - 支持事务 - 锁 -行锁 - 终端 begin; select * from db where id = 1 for update; # 加锁 commit; # 解锁 -表锁 - 终端 begin; select * from db for update; # 加锁 commit; # 解锁 - pymysql cursor.execute('select * from db for update') # 加锁 - django with trancation.automic(): models.User.objects.all().for_update() - 什么时候需要加锁? - 计数 - 应用场景: - 商品数量 -myisam - 不支持事务 - 锁 - 表锁 - 查询速度快
from flask import Flask from flask.globals import request, session app = Flask(__name__) @app.route('/index') def index(): # 对象中存在method、执行__getattr__() print(request.method) # request['method'] request里面没有该方法 # request+1 request里面没有该方法 # session是LocalProxy对象 # LocalProxy对象中的__setitem__ # session['x'] = 123 return 'Index' if __name__ == '__main__': app.run() # app.__call__() # app.wsgi_app ''' 第一阶段:请求到来 将request和session相关数据封装到ctx = ResponseContext对象中 再通过LocalStack将ctx添加到Local中 __storage__={ 1231:{'stack': [ctx(request, session)]} } 第二阶段: 视图函数获取request或session数据 方式一:直接找LocalStack获取 from flask.globals import _request_ctx_stack print(_request_ctx_stack.top.request.method) 方式二: 通过代理LocalProxy获取 from flask.globals import request print(request.method) '''
数据库连接池
# pip3 install DBUtils import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection POOL = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 maxshared=3, # 链接池中最多共享的链接数量,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='s9day118', charset='utf8' ) def task(): conn = POOL.connection() cursor = conn.cursor(pymysql.cursors.DictCursor) cursor.execute('select * from record') result = cursor.fetchall() cursor.close() conn.close() print(result) import threading for i in range(30): t = threading.Thread(target=task) t.start()
作用:
1. 生成HTML标签
2. form表单验证
安装
pip3 install wtforms
示例1:
前端: from flask import Flask, render_template, request, redirect from wtforms import Form from wtforms.fields import simple, html5, core from wtforms import widgets from wtforms import validators from utils import sqlhelper app = Flask(__name__) class LoginForm(Form): name = simple.StringField( label='用户名', validators=[ validators.DataRequired(message='用户名不能为空'), validators.Length(min=2, max=18, message='用户名长度必须大于%(min)d且小于%(max)d'), ], widget=widgets.TextInput(), render_kw={'placeholder': '请输入用户名'}, ) pwd = simple.PasswordField( label='密码', validators=[ validators.DataRequired(message='密码不能为空.'), validators.Length(min=2, max=18, message='密码长度必须大于%(min)d且小于%(max)d'), ], render_kw={'placeholder': '请输入密码'}, ) @app.route('/login', methods=['POST', 'GET']) def login(): if request.method == 'GET': form = LoginForm() print(form.name, type(form.name)) # form.name是StringField()对象 StringField().__str__ print(form.pwd, type(form.pwd)) # form.PasswordField()对象 PasswordField().__str__ return render_template('login.html', form=form) form = LoginForm(formdata=request.form) if form.validate(): return redirect('http://www.baidu.com') return render_template('login.html', form=form) return 'ss' class RegisterForm(Form): name = simple.StringField( label='用户名', validators=[ validators.DataRequired() ], widget=widgets.TextInput(), render_kw={'class': 'form-control'}, default='cui' ) 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, '足球'), (3, '羽毛球'), ), coerce=int, default=[1,] ) favor = core.SelectMultipleField( label='喜好', choices=( (1, '篮球'), (2, '足球'), ), widget=widgets.ListWidget(prefix_label=False), option_widget=widgets.CheckboxInput(), coerce=int, default=[1, 2] ) @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'GET': form = RegisterForm() return render_template('register.html', form=form) form = RegisterForm(formdata=request.form) if form.validate(): print('用户提交数据通过格式验证,提交的值为:', form.data) return redirect('http://www.baidu.com') return render_template('register.html', form=form) class UserForm(Form): roles = core.SelectField( label='权限', # 要求每次实例化都要执行 choices=sqlhelper.fetch_all('select id, name from tb1',[], type=None), # choices=(), coerce=int ) name = simple.StringField(label='用户') @app.route('/user', methods=['GET','POST']) def user(): if request.method == 'GET': # form = UserForm(data={'name': 'cui'}) # 设置默认值 form = UserForm() return render_template('user.html', form=form) if __name__ == '__main__': app.run() utils/sqlhelper.py: import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection POOL = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 maxshared=3, # 链接池中最多共享的链接数量,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='s9day119', charset='utf8' ) def fetch_all(sql,args, type = pymysql.cursors.DictCursor): conn = POOL.connection() cursor = conn.cursor(cursor=type) cursor.execute(sql, args) data_list = cursor.fetchall() close(cursor, conn) return data_list
后端: login.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> <form action="" method="POST" novalidate> <p>{{form.name.label}}{{form.name}}{{form.name.errors.0 }}</p> <p>{{form.pwd.label}}{{form.pwd}}{{form.pwd.errors[0]}}</p> <p><input type="submit" value="提交"></p> </form> </body> </html> register.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> <form action="" method="post" novalidate> {% for item in form %} <p>{{item.label}}:{{item}}{{item.errors.0}}</p> {% endfor %} <input type="submit" value="注册"> </form> </body> </html> user.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> {% for item in form %} <p>{{item.label}}:{{item}}{{item.errors.0}}</p> {% endfor %} <input type="submit" value="注册"> </body> </html>
示例2:Meta补充 定制csrf_token
前端: from _md5 import md5 from flask import Flask, render_template, request, redirect from wtforms import Form from wtforms.csrf.core import CSRF from wtforms.fields import simple, html5, core from wtforms import widgets from wtforms import validators app = Flask(__name__) class MyCSRF(CSRF): def setup_form(self, form): self.csrf_context = form.meta.csrf_context() self.csrf_secret = form.meta.csrf_secret return super(MyCSRF, self).setup_form(form) def generate_csrf_token(self, csrf_token): gid = self.csrf_secret + self.csrf_context token = md5(gid.encode('utf-8')).hexdigest() return token def validate_csrf_token(self, form, field): print(field.data, field.current_token) if field.data != field.current_token: raise ValueError('Invalid CSRF') class LoginForm(Form): name = simple.StringField( label='用户名', validators=[ validators.DataRequired(message='用户名不能为空'), validators.Length(min=2, max=18, message='用户名长度必须大于%(min)d且小于%(max)d'), ], widget=widgets.TextInput(), render_kw={'placeholder': '请输入用户名'}, ) pwd = simple.PasswordField( label='密码', validators=[ validators.DataRequired(message='密码不能为空.'), validators.Length(min=2, max=18, message='密码长度必须大于%(min)d且小于%(max)d'), # validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{5,}", # message='密码至少5个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符') ], render_kw={'placeholder': '请输入密码'}, ) class Meta: # -- CSRF # 是否自动生成CSRF标签 csrf = True # 生成CSRF标签name csrf_field_name = 'csrf_token' # 自动生成标签的值,加密用的csrf_secret csrf_secret = 'xxxxxx' # 自动生成标签的值,加密用的csrf_context csrf_context = lambda x: request.url # 生成和比较csrf标签 csrf_class = MyCSRF @app.route('/login', methods=['POST', 'GET']) def login(): if request.method == 'GET': form = LoginForm() return render_template('login.html', form=form) form = LoginForm(formdata=request.form) if form.validate(): return redirect('http://www.baidu.com') return render_template('login.html', form=form) return 'ss' if __name__ == '__main__': app.run()
后端: <form action="" method="POST" novalidate> {{form.csrf_token}} <p>{{form.name.label}}{{form.name}}{{form.name.errors.0 }}</p> <p>{{form.pwd.label}}{{form.pwd}}{{form.pwd.errors[0]}}</p> <p><input type="submit" value="提交"></p> </form>
示例三:钩子函数
from _md5 import md5 from flask import Flask, render_template, request, redirect from wtforms import Form from wtforms.csrf.core import CSRF from wtforms.fields import simple, html5, core from wtforms import widgets from wtforms import validators app = Flask(__name__) class LoginForm(Form): name = simple.StringField( label='用户名', validators=[ validators.DataRequired(message='用户名不能为空'), validators.Length(min=2, max=18, message='用户名长度必须大于%(min)d且小于%(max)d'), ], widget=widgets.TextInput(), render_kw={'placeholder': '请输入用户名'}, ) pwd = simple.PasswordField( label='密码', validators=[ validators.DataRequired(message='密码不能为空.'), validators.Length(min=2, max=18, message='密码长度必须大于%(min)d且小于%(max)d'), # validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{5,}", # message='密码至少5个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符') ], render_kw={'placeholder': '请输入密码'}, ) # 钩子函数 def validate_name(self, field): """ 自定义name字段规则,例:用户名名字必须以old开头 :param field: :return: """ # 最开始初始化时,self.data中已经有所有的值 print('钩子函数获取的值', field.data) if not field.data.startswith('old'): raise validators.ValidationError("用户名必须以old开头") # 继续后续验证 # raise validators.StopValidation("密码不一致") # 当前字段不再继续后续验证 @app.route('/login', methods=['POST', 'GET']) def login(): if request.method == 'GET': form = LoginForm() return render_template('login.html', form=form) form = LoginForm(formdata=request.form) if form.validate(): print(form.data) return redirect('http://www.baidu.com') return render_template('login.html', form=form) return 'ss' if __name__ == '__main__': app.run()
回顾
1.
两类: 请求上下文管理 应用上下文管理 流程: 请求到来: 将请求和session相关数据封装到ctx=RequestContext对象中, 将app和g封装到app_ctx = AppContext对象中 再通过LocalStack对象将ctx、app_ctx封装到Local对象中。 问题: Local是什么?作用? 存储数据,在flask中根据每一个协程,开辟一部分空间,让数据进行存储,保证数据与数据隔离。 类似于threading.local的作用,但是是他的升级版。 __storage__={ 1231:{}, 1231:{} } LocalStack是什么?作用? 将storage里面stack的值维护成一个栈 storage={ 1231:{stack:[ctx,]} } 获取数据: 通过LocalProxy对象+偏函数,调用LocalStack去Local中获取响应ctx、app_ctx中封装的值 问题:为什么要把ctx = request/session app_ctx = app/g ? 因为离线脚本需要使用app_ctx 请求结束: 调用LocalStack的pop方法,将ctx和app_ctx移除
2. threading.Local 为每个线程开辟独立的空间
4. 数据库优化方案
- 避免使用select*
- 固定长度在前面
- 内存代替表,如:性别等 例如:django中的chioces=
- 读写分离
- 分库
- 分表
- 水平分表(将表进行拆分)
- 垂直分表(将不经常用的表放到另外一张表中)
- 命中索引
- 组合索引代替索引合并
- 尽量使用短索引
- 如果取一条数据时,使用limit 1
select id,name from tb where name='alex' limit 1;
问题: - form 对象为什么可以被for循环? 答:变为可迭代对象 class Foo(object): # def __iter__(self): # return iter([11,22,33]) def __iter__(self): yield 1 yield 2 obj = Foo() for item in obj: print(item) - __new__ 方法的返回值决定对象到底是什么 class Bar(object): class Foo(object): def __new__(cls, *args, **kwargs): # return super(Foo, cls).__new__(cls, *args, **kwargs) # return 123 return Bar() obj = Foo() # __new__ 方法返回什么。对象就返回什么 print(obj) - metaclass - 创建类时,先执行type的__init__。 - 类在实例化时候,执行type的__call__,__call__方法的返回值就是实例化的对象 -__call__内部调用: - 类.__new__,创建对象 - 类.__init__,对象的初始化 - class MyType(type): def __init__(self, *args, **kwargs): super(MyType, self).__init__( *args, **kwargs) print('111') def __call__(cls, *args, **kwargs): obj = cls.__new__(cls) cls.__init__(obj) return obj class Foo(object, metaclass=MyType): a1 = 123 def __init__(self): pass def __new__(cls, *args, **kwargs): return object.__new__(cls) def func(self): return 666 # Foo是类 # Foo是MyType的一个对象 obj=Foo() print(obj) # 创建类时先执行type的__init__方法 # 当一个类在实例化时候,执行type的__call__方法,__call__方法的返回值就是实例化的对象
SQLAlchemy
1. 是一个ORM框架
2. 作用:帮助我们使用类和对象快速实现数据库操作
原生:
- MySQLdb:python2
- pymysql:pymysql2 / pymysql3
1. 单表操作(示例) 创建单表: from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index, create_engine Base = declarative_base() # 单表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=False) def create_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day120?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.create_all(engine) def drop_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day120?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.drop_all(engine) if __name__=='__main__': # create_all() drop_all() 表操作: from models import Users from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index, create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day120?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) # 根据Users类对users表进行增删改查 SessionFactory = sessionmaker(bind=engine) session = SessionFactory() # 1. 增 # # 单条增加 # # obj = Users(name='alex') # # session.add(obj) # # # 多条增加 # session.add_all([ # Users(name='lolo'), # Users(name='lo') # ]) # # session.commit() # session.close() # 2. 查 # res = session.query(Users).all() # for row in res: # print(row.id, row.name) # res = session.query(Users).filter(Users.id !=2) # for row in res: # print(row.id, row.name) # res = session.query(Users).filter(Users.id >=2).first() # print(res) # 3. 删 # res = session.query(Users).filter(Users.id >2).delete() # session.commit() # 4. 改 # session.query(Users).filter(Users.id == 2).update({Users.name:'lol'}) # session.query(Users).filter(Users.id == 2).update({'name':'loser'}) # session.commit() session.query(Users).filter(Users.id == 2).update({'name':Users.name+'666'}, synchronize_session = False) session.commit() 基本增删改查: # 添加 session.add(对象) session.add_all([ 对象1, 对象2 ]) session.commit() # 查询 session.query(Users).all() session.query(Users).filter(Users.id>4) # 删除 session.query(Users).filter(Users.id>4).delete() # 修改 session.query(Users).filter(Users.id>4).update({Users.age:18}) 常用操作: # 1. 指定列 res = session.query(Users.id, Users.name.label('cname')).all() for item in res: # print(item) print(item[0], item.id, item.cname) # 2. 默认条件and ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all() # 3. between ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all() # 4. in ret = session.query(Users).filter(Users.id.in_([1,3,4])).all() ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all() # 5. 子查询 ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter(Users.name =='eric'))).all() # 6. and、or的嵌套 from sqlalchemy import and_, or_ session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all() session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all() session.query(Users).filter( or_( Users.id < 2, and_(Users.name == 'eric', Users.id > 3), Users.extra != "" )).all() # 7. filter_by ret = session.query(Users).filter_by(name='alex').all() # 8. 通配符 ret = session.query(Users).filter(Users.name.like('e%')).all() # 9. 限制 (切片) ret = session.query(Users)[1:2] # 10.排序 ret = session.query(Users).order_by(Users.name.desc()).all() ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all() # 11.分组 from sqlalchemy.sql import func ret = session.query( Users.depart_id, func.count(Users.id), ).group_by(Users.depart_id).having(func.count(Users.id) >=2).all() for item in ret: print(item) # 12. 组合 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union(q2).all() # 去重 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union_all(q2).all() # 不去重
2. FK 建表: from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey,create_engine from sqlalchemy.orm import relationship Base = declarative_base() class Depart(Base): __tablename__ = 'depart' id = Column(Integer, primary_key=True) title = Column(String(32),index=True,nullable=False) class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=False) depart_id = Column(Integer, ForeignKey('depart.id')) dp = relationship("Depart", backref='pers') # 用于反向查询 def create_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.create_all(engine) def drop_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.drop_all(engine) if __name__=='__main__': create_all() # drop_all() 操作: from models import Users,Depart from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) SessionFactory = sessionmaker(bind=engine) # 根据Users类对users表进行增删改查 session = SessionFactory() 1. 查询所有用户 ret = session.query(Users).all() for row in ret: print(row.id, row.name, row.depart_id) 2. 查询所有用户+所属部门名称 ret = session.query(Users.id, Users.name, Depart.title).join(Depart).all() for row in ret: print(row.id, row.name, row.title) 3. relationship字段 查询所有用户+所属部门名称 ret = session.query(Users).all() for row in ret: print(row.id,row.name,row.depart_id, row.dp.title) 4. relationship字段 查询销售部所有人员 obj = session.query(Depart).filter(Depart.title=='销售').first() for row in obj.pers: print(row.id, row.name, obj.title) 5. 创建一个名称叫IT部门,再在该部门中添加一个员工:bor # 方式1: d1 = Depart(title='IT') session.add(d1) session.commit() u1 = Users(name='bor', depart_id=d1.id) session.add(u1) session.commit() # 方式2 u1 = Users(name='gor', dp=Depart(title='MIT')) session.add(u1) session.commit() 6. 创建一个名称为:王者荣耀,再在该部门添加员工:a/b/c d1 = Depart(title='王者荣耀') d1.pers = [Users(name='a'), Users(name='b'), Users(name='c'),] session.add(d1) session.commit() session.close()
3. M2M 建表: from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, create_engine, UniqueConstraint from sqlalchemy.orm import relationship Base = declarative_base() # 多对多 class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=False) course_list = relationship("Course", secondary='student2course', backref='student_list') class Course(Base): __tablename__ = 'course' id = Column(Integer, primary_key=True) title = Column(String(32), index=True, nullable=False) class Student2Course(Base): __tablename__ = 'student2course' id = Column(Integer, primary_key=True, autoincrement=True) student_id = Column(Integer, ForeignKey('student.id')) course_id = Column(Integer, ForeignKey('course.id')) __table_args__ = ( UniqueConstraint('student_id', 'course_id', name='uix_stu_cou'), # 联合唯一索引 # Index('ix_id_name', 'name', 'extra'), # 联合索引 ) def create_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.create_all(engine) def drop_all(): engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) Base.metadata.drop_all(engine) if __name__=='__main__': create_all() # drop_all() 操作: from models import Student,Course,Student2Course from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://root:123456@127.0.0.1:3306/s9day121?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) SessionFactory = sessionmaker(bind=engine) # 根据Users类对users表进行增删改查 session = SessionFactory() # 1. 录入数据 session.add_all([ Student(name='ab'), Student(name='cd'), Course(title='数学'), Course(title='大数据') ]) session.add_all([ Student2Course(student_id=1, course_id=1), Student2Course(student_id=1, course_id=2), Student2Course(student_id=2, course_id=1), ]) session.commit() # 2.三种表关联 ret1 = session.query(Student2Course.id, Student.name, Course.title).join(Student, Student2Course.student_id == Student.id).join(Course, Student2Course.course_id == Course.id).order_by(Student2Course.id.asc()).all() print(ret1) # 3. 找出 'ab' 选的所有课 ret = session.query(Student2Course.id, Student.name, Course.title).join(Student, Student2Course.student_id == Student.id).join(Course, Student2Course.course_id == Course.id).filter(Student.name=='ab').order_by(Student2Course.id.asc()).all() print(ret) # 方式2 obj = session.query(Student).filter(Student.name=='ab').first() for item in obj.course_list: print(item.title) # 4. 选了'数学'的所有人 obj = session.query(Course).filter(Course.title =='数学').first() for item in obj.student_list: print(item.name) # 5. 创建一个课程,创建两个学生,两个学生选新建的课程 obj = Course(title='英语') obj.student_list=[Student(name='ed'), Student(name='ef')] session.add(obj) session.commit() session.close()
封装: - 方法封装到类中 class File: def file_add():pass def file_update():pass def file_del():pass def file_fetch():pass - 数据封装到对象中 class File: def __init__(self, name, age): self.name = name self.age = age def file_add():pass def file_update():pass def file_del():pass def file_fetch():pass obj1 = File('oldboy', 18) obj1 = File('boy', 20) 应用: -session/request封装到RequestContext对象中。 -app/g封装到AppContext中。 继承:如果多个类中有相同的方法,为了避免重复编写,可以将其放在父类(基类)中 class Base(object): def xxx():pass class File(Base): def __init__(self, name, age): self.name = name self.age = age def file_add():pass def file_update():pass class Work(Base): def file_del():pass def file_fetch():pass - 应用: rest_framework中的视图类的继承 多态(鸭子模型): 天生支持多态,对于参数来说可以传入任何类型的对象,只要保证有想要的send方法即可 class Msg(object): def send():pass class WX(object): def send():pass def func(arg): arg.send() -进阶 __init__:初始化 __new__:创建对象 __getattr__ :对象.xx __setattr__ __delattr__ __getitem__: 对象['xx'] __setitem__ __delitem__ __mro__: 查找成员继承顺序 __str__ __repr__ __iter__ __dict__ -高级:metaclass 1. 类创建 class Foo(object):pass Foo = type('Foo',(object,),{}) 2. 如何指定类由自定义type创建? class MyType(type): pass class Foo(object,metaclass = MyType): # __metaclass__ = MyType # py2 pass Foo = MyType('Foo',(object,),{}) 3. 默认执行顺序 class Foo(object): pass obj = Foo() 类创建后执行type里面的__init__方法 实例化后执行type的__call__方法 在__call__方法中又会执行当前类的__new__方法,再调用这个类的__init__方法 4. 如果一个类自己或者基类中指定了metaclass,那么该类就是由metaclass指定的type或者MyType来创建。 class MyType(type): def __init__(self, *args, **kwargs): print('11') super(MyType, self).__init__(*args, **kwargs) class Base(object, metaclass=MyType): pass # 同 Base = MyType('Base',(object,),{}) class Foo(Base): pass 同: class MyType(type): def __init__(self, *args, **kwargs): print('11') super(MyType, self).__init__(*args, **kwargs) def with_metaclass(arg): Base = MyType('Base',(arg,),{}) return Base class Foo(with_metaclass(object)): pass
a. 下载安装 pip3 install flask_sqlalchemy b. demo/__init__.py 导入并实例化SQLAlchemy from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() 注意事项: - db必须在导入蓝图之前 - 必须导入models.py 代码: from flask import Flask from flask_session import Session # 第一步:导入并实例化 from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() from demo.views.user import us from .models import * def create_app(): app = Flask(__name__) app.config.from_object('settings.ProConfig') app.register_blueprint(ac) app.register_blueprint(us) # flask_Session:第1步 实例化 # Session(app) # 第三步 依赖app中的配置文件 db.init_app(app) return app c. 初始化 db.init_app(app) d. 在配置文件中写入配置 ########## SQLAlchemy ########配置文件 SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:123456@127.0.0.1:3306/s9day122?charset=utf8" SQLALCHEMY_POOL_SIZE = 10 SQLALCHEMY_MAX_OVERFLOW = 5 e. 创建models.py demo/models.py from sqlalchemy import Column, Integer, String from demo import db class Users(db.Model): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32), index=True, nullable=False) depart_id = Column(Integer) f. 生成表(离线脚本, 使用app上下文) from demo import db,create_app app=create_app() app_ctx = app.app_context() with app_ctx: # __enter__ 通过LocalStack放入Local中 db.create_all() # 调用LocalStack放入Local中获取app 再去app中获取配置 #__exit__ g. 基于ORM对数据库进行操作 demo/views/user.py from flask import Blueprint from demo import db from demo import models us = Blueprint('us',__name__) @us.route('/index') def index(): # 使用SQLAlchemy在数据库中插入一条数据 # db.session.add(models.Users(name='alex', depart_id=1)) # db.session.commit() # db.session.remove() res = db.session.query(models.Users.name).all() print(res) db.session.remove() return 'Index'
pip3 install flask-script 功能: 在manage_use_flask-script.py中写入: a.增加runserver from demo import create_app from flask_script import Manager app = create_app() manage = Manager(app) if __name__ == '__main__': manage.run() # 文件运行起来后在终端输入 # python manage_use_flask-script.py runserver 或 # python manage_use_flask-script.py runserver -h 127.0.0.1 -p 8005 b. 位置传参 from demo import create_app from flask_script import Manager app = create_app() manage = Manager(app) @manage.command def task(arg): print(arg) if __name__ == '__main__': manage.run() # 文件运行起来后在终端输入 # python manage_use_flask-script.py task 12 c. 关键字传参 from demo import create_app from flask_script import Manager app = create_app() manage = Manager(app) @manage.option('-n','--name', dest='name') @manage.option('-u','--url', dest='url') def cmd(name, url): ''' 自定义命令 执行:python manage_use_flask-script.py cmd -n cui -u http://www.baidu.com :param name: :param url: :return: ''' print(name,url) if __name__ == '__main__': manage.run() # 文件运行起来后在终端输入 # python manage_use_flask-script.py cmd -n cui -u http://www.baidu.com
pip insatll pipreqs
在终端输入:
pipreqs ./ --encoding=utf-8

浙公网安备 33010602011771号