一、认识flask
1、短小精悍、可扩展性强 的一个web框架
注意:上下文管理机制
2、依赖wsgi:werkzurg
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)
注意__init__和__call__的区别:
class Foo(): def __init__(self): print('init') def __call__(self, *args, **kwargs): print('call') a = Foo() a() #init #call
一个简单的flask代码
from flask import Flask app = Flask(__name__)
@app.route('/index') def index(): return 'hello world' if __name__ == '__main__': app.run('local
二、一个有登录功能的flask例子
1、template可以类似django修改
2、static有两种方式,
A: a、 static_folder="staticccc" b、将static文件夹命名为 staticccc c、img引用的时候用staticccc/xxxxx
B: a、 static_url_path='/vvvvv' b、 img引用的时候用 /vvvvv/xxxxx
from flask import Flask,render_template,request,redirect,session # app = Flask(__name__,template_folder="templates",static_folder="staticccc",static_url_path='/vvvvv') app = Flask(__name__,template_folder="templates",static_folder="static") app.secret_key = 'asdfasdf' @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') #获取的是post的信息 pwd = request.form.get('pwd') if user == 'oldboy' and pwd == '666': session['user'] = user 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()
index.html
<body> <h1>欢迎使用</h1> <img src="/static/111.png" alt=""> </body>
login.html
<body> <h1>用户登录</h1> <form method="post"> <input type="text" name="user"> <input type="password" name="pwd"> <input type="submit" value="提交">{{error}} </form> </body>
三、flask基础
1、配置文件 2、路由系统 3、视图 4、请求相关 5、响应 6、模板渲染 7、session 8、闪现 9、中间件 10、蓝图(blueprint) 11、特殊装饰器
1、django和flask的区别
django:是一个大而全的框架,内部提供了很多组件,如orm,admin,forms,分页等很多方便的组件,只需在配置里配置下就可以了
flask: 是一个轻量级的框架,短小精悍,可扩展性很强,适用于开发小的网站,他提供了很多第三方的组件,只需要跟这些组件结合起来也可以创造一个类似django的框架 ,可定制型强
就我个人而言,我比较偏向于。。。。。
2、flask和django最大的不同点
request/session,
django需要导入,参数;flask直接调用
3、flask知识点
--模板+静态文件 ,app=Flask(__name__,......)
--路由
@app.route('/index',methods=['GET'])
--请求
request.form
request.args
request.method
--响应
“”
render
redirect
--session
session['xx']=123
session.get('xx')
4、配置文件导入原理, ----即给一个路径‘settings.Foo’,可以找到类并获取去其中的大写的静态字段
settings.py
class Foo(): DEBUG = True TEXT = True
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))
#DEBUG True
#TEXT True
5、配置文件使用
app.config.from_object('settings.Dev')
test.py
from flask import Flask app = Flask(__name__) print(app.config) app.config.from_object('settings.Dev') print(app.config) if __name__ == '__main__': app.run()
settings.py
class Base(object): #无论哪种环境都需要的类 XX = 123 class Pro(Base): #生产环境 DEBUG = False class Dev(Base): #开发环境 DEBUG = True
6、路由系统
实例:
from flask import Flask,url_for app = Flask(__name__) app.route('/index/<int:nid>',methods=['GET','POST']) def index(nid): print(url_for("index",nid=888))
知识点:
---endpoint,反向生成URL,默认函数名 ---url_for('endpoint') ---动态路由: /index/<int:nid> def index(nid): print(nid) return "index"
7、请求和响应相关
请求相关消息
# 请求相关信息
# request.method
# request.args
# request.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
响应相关消息
A、响应体
return render_template('xxx.html') return redirect('/index') return 'xxx' return jsonify({'k1':'v1'})
B、响应头
obj = make_response('asdf') obj.headers['xxxx'] = '123' obj.set_cookie('key','value') return obj
8、学生管理系统
版本一几乎不用,版本二在某些函数前做定制,版本在全局使用的时候用到
版本一: @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')
app.py
from flask import Flask,render_template,request,redirect,session,url_for,jsonify,make_response,Markup,flash,get_flashed_messages app = Flask(__name__) app.config.from_object("settings.DevelopmentConfig") STUDENT_DICT = { 1:{'name':'王龙泰','age':38,'gender':'中'}, 2:{'name':'小东北','age':73,'gender':'男'}, 3:{'name':'田硕','age':84,'gender':'男'}, } @app.before_request def xxxxxx(): if request.path == '/login': return None if session.get('user'): return None return redirect('/login') @app.route('/login',methods=["GET","POST"]) def login(): print('login') if request.method == 'GET': return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') if user == 'oldboy' and pwd == '666': session['user'] = user return redirect('/index') return render_template('login.html',error='用户名或密码错误') @app.route('/index') def index(): print('index') return render_template('index.html',stu_dic=STUDENT_DICT) @app.route('/delete/<int:nid>') def delete(nid): del STUDENT_DICT[nid] return redirect(url_for('index')) @app.route('/detail/<int:nid>') def detail(nid): info = STUDENT_DICT[nid] return render_template('detail.html',info=info) if __name__ == '__main__': app.run()
login.html
<body> <h1>用户登录</h1> <form method="post"> <input type="text" name="user"> <input type="password" name="pwd"> <input type="submit" value="提交">{{error}} </form>
index.html
<body> <h1>学生列表</h1> <table border="1"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>选项</th> </tr> </thead> <tbody> {% for k,v in stu_dic.items() %} <tr> <td>{{k}}</td> <td>{{v.name }}</td> <td>{{v.age}}</td> <td>{{v.gender}}</td> <td> <a href="/detail/{{k}}">查看详细</a> | <a href="/delete/{{k}}">删除</a> </td> </tr> {% endfor %} </tbody> </table> </body>
detail.html
<body> <h1>学生详细</h1> <ul> {% for item in info.values() %} <li>{{item}}</li> {% endfor %} </ul> </body>
9、模板
6. 模板渲染 - 基本数据类型:可以执行python语法,如:dict.get() list['xx'] - 传入函数 - 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 "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("asdf")
10、session
当请求刚到来:flask读取cookie中session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoib2xkYm95,将该值解密并反序列化成字典,放入内存以便视图函数使用。 视图函数: @app.route('/ses') def ses(): session['k1'] = 123 session['k2'] = 456 del session['k1'] return "Session" session['xxx'] = 123 session['xxx'] 当请求结束时,flask会读取内存中字典的值,进行序列化+加密,写入到用户cookie中。
settings.py
from datetime import timedelta class Config(object): DEBUG = False TESTING = False SECRET_KEY = "asdfasdfas23" DATABASE_URI = 'sqlite://:memory:' 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 PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
11、闪现
在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"
12、中间件
- 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()
13、特殊装饰器
1. before_request 2. after_request 示例: from flask import Flask app = Flask(__name__) @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" @app.route('/order') def order(): print('order') return "order" if __name__ == '__main__': app.run() 3. before_first_request from flask import Flask app = Flask(__name__) @app.before_first_request def x1(): print('123123') @app.route('/index') def index(): print('index') return "Index" @app.route('/order') def order(): print('order') return "order" if __name__ == '__main__': app.run() 4. template_global 5. template_filter 6. errorhandler @app.errorhandler(404) def not_found(arg): print(arg) return "没找到"
14、内容慧谷
1. django和flask区别? 2.什么是wsgi? web服务网关接口,wsgi是一个协议,实现该写一个的模块: - wsgiref - werkzeug 实现其协议的模块本质上就是socket服务端用于接收用户请求,并处理。 一般web框架基于wsgi实现,这样实现关注点分离。 wsgiref示例: from wsgiref.simple_server import make_server def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, run_server) httpd.serve_forever() werkzeug示例: from werkzeug.wrappers import Response from werkzeug.serving import run_simple def run_server(environ, start_response): response = Response('hello') return response(environ, start_response) if __name__ == '__main__': run_simple('127.0.0.1', 8000, run_server) Flask源码入口: from werkzeug.wrappers import Response from werkzeug.serving import run_simple class Flask(object): def __call__(self,environ, start_response): response = Response('hello') return response(environ, start_response) def run(self): run_simple('127.0.0.1', 8000, self) app = Flask() if __name__ == '__main__': app.run() 3. Flask提供功能 - 配置文件 - 所有配置都在app.config中 - app.config["xx"] = 123 - app.config.from_object("类的路径") - 应用:importlib、getattr - django中间件 - rest framework全局配置 - session - 加密后放置在用户浏览器的cookie中。 - 流程: - 请求到来 - 视图函数 - 请求结束 - 配置文件 - 闪现 - 基于session实现 - 路由 - 装饰器(带参数) - 自定义装饰器放下面 - 参数 - url_for - 视图 - FBV - 请求和响应 - 请求:request - 响应: 4种 - 模板 - ... - 特殊装饰器 - before_first_request - before_request - after_request - template_global() - template_filter() - errorhandler(404) - 中间件 今日内容: 1. 路由+视图 2. session实现原理(源码) 3. 蓝图 4. threading.local 5. 上下文管理(第一次)
15、路由+视图
a. 路由设置的两种方式: @app.route('/xxx') def index(): return "index" def index(): return "index" app.add_url_rule("/xxx",None,index) 注意事项: - 不用让endpoint重名 - 如果重名函数也一定要相同。
16、路由+视图之 参数
rule, URL规则 view_func, 视图函数名称 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') methods=None, 允许的请求方式,如:["GET","POST"] strict_slashes=None, 对URL最后的 / 符号是否严格要求, redirect_to=None, 重定向到指定地址 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 subdomain=None, 子域名访问
实例
需要去hosts文件里将 127.0.0.1 wupeiqi.com,将ip和域名配置下
from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'wupeiqi.com:5000' """ 127.0.0.1 wupeiqi.com 127.0.0.1 web.wupeiqi.com 127.0.0.1 admin.wupeiqi.com """ # http://admin.wupeiqi.com:5000/ @app.route("/", subdomain="admin") def admin_index(): return "admin.your-domain.tld" # http://web.wupeiqi.com:5000/ @app.route("/", subdomain="web") def web_index(): return "web.your-domain.tld" # http://sdsdf.wupeiqi.com:5000/ # http://sdfsdf.wupeiqi.com:5000/ # http://asdf.wupeiqi.com:5000/ @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()
17、路由+视图之 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'] 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('uuuu')) if __name__ == '__main__': app.run()
18、路由+视图之 自定义正则
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()
19、session原理
session流程图(加分项)
20、蓝图
目标:给开发者提供目录结构 其他: - 自定义模板、静态文件 - 某一类url添加前缀 -----用以加版本号如,v1.... - 给一类url添加before_request
A、小蓝图(template、views放一起,大蓝图类似django分布)目录
B、manage.py
from crm import create_app app = create_app() if __name__ == '__main__': app.run()
init.py
from flask import Flask from .views.account import ac from .views.user import uc def create_app(): app = Flask(__name__) # @app.before_request # def x1(): # print('app.before_request') app.register_blueprint(ac) app.register_blueprint(uc,url_prefix='/api') return app
accout.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'
21、threading.local【和flask无任何关系】
flask里local()原码
作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。 import threading from threading import local import time obj = local() def task(i): obj.xxxxx = i time.sleep(2) print(obj.xxxxx,i) for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() 问题: - 如何获取一个线程的唯一标记? threading.get_ident() - 根据字典自定义一个类似于threading.local功能? import time import threading DIC = {} def task(i): ident = threading.get_ident() if ident in DIC: DIC[ident]['xxxxx'] = i else: DIC[ident] = {'xxxxx':i } time.sleep(2) print(DIC[ident]['xxxxx'],i) for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() - 根据字典自定义一个为每个协程开辟空间进行存取数据。 import time import threading import greenlet DIC = {} def task(i): # ident = threading.get_ident() ident = greenlet.getcurrent() if ident in DIC: DIC[ident]['xxxxx'] = i else: DIC[ident] = {'xxxxx':i } time.sleep(2) print(DIC[ident]['xxxxx'],i) for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() - 通过getattr/setattr 构造出来 threading.local的加强版(协程) import time import threading 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.xxxxx = i time.sleep(2) print(obj.xxxxx,i) for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start()
22、上下文管理
请求到来时候: # ctx = RequestContext(self, environ) # self是app对象,environ请求相关的原始数据 # ctx.request = Request(environ) # ctx.session = None # 将包含了request/session的ctx对象放到“空调” { 1232:{ctx:ctx对象} 1231:{ctx:ctx对象} 1211:{ctx:ctx对象} 1111:{ctx:ctx对象} 1261:{ctx:ctx对象} } 视图函数: from flask import reuqest,session request.method 请求结束: 根据当前线程的唯一标记,将“空调”上的数据移除。
23、上下文管理前戏
1、偏函数
# by luffycity.com 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)
2、执行父类方法
- super和执行类的区别? """ class Base(object): def func(self): print('Base.func') class Foo(Base): def func(self): # 方式一:根据mro的顺序执行方法 # super(Foo,self).func() # 方式二:主动执行Base类的方法 # Base.func(self) print('Foo.func') obj = Foo() obj.func() """ #################################### class Base(object): def func(self): super(Base, self).func() print('Base.func') class Bar(object): def func(self): print('Bar.func') class Foo(Base,Bar): pass # 示例一 # obj = Foo() # obj.func() # print(Foo.__mro__) # 示例二 # obj = Base() # obj.func()
3、面向对象中特殊方法 setattr/getattr注意事项:
class Foo(object): def __init__(self): # self.storage = {} object.__setattr__(self,'storage',{}) def __setattr__(self, key, value): print(key,value,self.storage) obj = Foo() obj.xx = 123
4、基于列表实现栈
class Stack(object): def __init__(self): self.data = [] def push(self,val): self.data.append(val) def pop(self): return self.data.pop() def top(self): return self.data[-1] _stack = Stack() _stack.push('佳俊') _stack.push('咸鱼') print(_stack.pop()) print(_stack.pop())
24、flask-session
pip3 install flask-session 掌握: - 使用 # by luffycity.com import redis from flask import Flask,request,session from flask.sessions import SecureCookieSessionInterface from flask_session import Session app = Flask(__name__) # app.session_interface = SecureCookieSessionInterface() # app.session_interface = RedisSessionInterface() app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = redis.Redis(host='140.143.227.206',port=6379,password='1234') Session(app) @app.route('/login') def login(): session['user'] = 'alex' return 'asdfasfd' @app.route('/home') def index(): print(session.get('user')) return '...' if __name__ == '__main__': app.run() - 原理: - session数据保存到redis session:随机字符串1:q23asifaksdfkajsdfasdf session:随机字符串2:q23asifaksdfkajsdfasdf session:随机字符串3:q23asifaksdfkajsdfasdf session:随机字符串4:q23asifaksdfkajsdfasdf session:随机字符串5:q23asifaksdfkajsdfasdf - 随机字符串返回给用户。 随机字符串 源码: from flask_session import RedisSessionInterface
25、判断是否是函数和方法
def func(): pass class Foo(object): def func(self): pass # 执行方式一 # obj = Foo() # obj.func() # 方法 # 执行方式二 # Foo.func(123) # 函数 from types import FunctionType,MethodType # obj = Foo() # print(isinstance(obj.func,FunctionType)) # False # print(isinstance(obj.func,MethodType)) # True print(isinstance(Foo.func,FunctionType)) # True print(isinstance(Foo.func,MethodType)) # False
26、redis安装下载地址:https://github.com/MicrosoftArchive/redis/releases
redis-desktop manager 0.9.3.817.exe
27、code management system
1、注意设计表的时候,自增和外键的id类型要一致
2、work
内容详细: - 代码统计 - 数据库连接池: pip3 install DBUtils 注意: - 使用数据库连接池 - 封装SQLHelper 作业: 1. 功能完善 2. BootStrap 模板 3. 详细页面: http://127.0.0.1:5000/detail/1 -> 折线图 4. 用户列表: - 柱状图 - 表格 PS: select user_id,sum(line) from record group by user_id + 连表查询到用户姓名
28、review
第一部分:Flask 1. 谈谈你对django和flask的认识? 2. Flask基础: - 配置文件:反射+importlib - 路由系统: - 装饰器 @app.route() - 参数: - url - endpoint - methods - 加装饰器 - endpoint默认是函数名 - functools.wraps(func) + functools.partial - 写路由两种方式: - 装饰器 - add_url_rule - 自定义支持正则的URL - session - 蓝图 - 目录结构划分 - 前缀 - 特殊装饰器 3. 上下文管理 - threading.local - 为每个线程开辟空间,使得线程之间进行数据隔离。 - 应用:DBUtils中为每个线程创建一个数据库连接时使用。 - 面向对象特殊方法: - getattr - setattr - delattr - 偏函数 - 单例模式 - 请求上下文流程: - 班级示例: - 源码流程: - __call__ - wsgi_app - ctx = RequestContext(): 封装= 请求数据+空session - ctx.push() : 将ctx传给LocalStack对象,LocalStack再将数据传给Local存储起来。 问题:Local中是如何存储? __storage__ = { 1231:{} } 问题:LocalStack作用? __storage__ = { 1231:{stack:[ctx] } } - 视图函数:再次去获取 - 关闭 4. 第三方组件: 1. flask-session 2. DBUtils
29、数据库&前端
1. 什么是响应式布局? @media属性 2. MySQL数据库 - 引擎: - innodb - 支持事务 - 锁 - 行锁 - 表锁 - 示例: - 终端: begin; select xx from xx for update; commit; - pymysql cursor.execute('select * from xx for update') - django with trancation.automic(): models.User.objects.all().for_update() - mysaim - 不支持事务 - 锁 - 表锁 - 快
30、上下文管理
知识点
# by luffycity.com class Foo(object): def __str__(self): return 'asdf' def __getattr__(self, item): return "999" def __getitem__(self, item): return '87' def __add__(self, other): return other + 1 obj = Foo() print(obj) print(obj.x) print(obj['x1']) print(obj + 7)
上下文
1. 上下文管理:LocalProxy对象 2. 上下文管理: - 请求上下文:request/session - App上下文: app/g
from flask import Flask,request,session app = Flask(__name__) @app.route('/index') def index(): # 1. request是LocalProxy对象 # 2. 对象中有method、执行__getattr__ print(request.method) # request['method'] # request + 1 # 1. session是LocalProxy对象 # 2. LocalProxy对象的__setitem__ session['x'] = 123 return "Index" if __name__ == '__main__': app.run() # app.__call__ # app.wsgi_app """ 第一阶段:请求到来 将request和Session相关数据封装到ctx=RequestContext对象中。 再通过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 import Flask,request print(request.method) """
详细
容详细: 1. 上下文管理:LocalProxy对象 2. 上下文管理: - 请求上下文(ctx=RequestContext()):request/session - App上下文(app_ctx=AppContext()): app/g - 程序启动: 两个Local: local1 = { } local2 = { } 两个LocalStack: _request_ctx_stack _app_ctx_stack - 请求到来 对数据进行封装: ctx = RequestContext(request,session) app_ctx = AppContext(app,g) 保存数据: 将包含了(app,g)数据的app_ctx对象,利用 _app_ctx_stack(贝贝,LocalStack())将app_ctx添加到Local中 storage = { 1231:{stack:[app_ctx(app,g),]} } 将包含了request,session数据的ctx对象,利用_request_ctx_stack(刘淞,LocalStack()),将ctx添加到Local中 storage = { 1231:{stack:[ctx(request,session),]} } - 视图函数处理: from flask import Flask,request,session,current_app,g app = Flask(__name__) @app.route('/index') def index(): # 去请求上下文中获取值 _request_ctx_stack request.method # 找小东北获取值 session['xxx'] # 找龙泰获取值 # 去app上下文中获取值:_app_ctx_stack print(current_app) print(g) return "Index" if __name__ == '__main__': app.run() app.wsgi_app - 结束 _app_ctx_stack.pop() _request_ctx_stack.pop() 问题: 1. Flask中g的生命周期? 2. g和session一样吗? 3. g和全局变量一样吗?
31、wtforms
基本使用
from flask import Flask,request,render_template,session,current_app,g,redirect
from wtforms import Form
from wtforms.fields import simple
from wtforms.fields import html5
from wtforms.fields import core
from wtforms import widgets
from wtforms import validators
app = Flask(__name__)
class LoginForm(Form):
name = simple.StringField(
validators=[
validators.DataRequired(message='用户名不能为空.'),
# validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(),
render_kw={'placeholder':'请输入用户名'}
)
pwd = simple.PasswordField(
validators=[
validators.DataRequired(message='密码不能为空.'),
# validators.Length(min=8, message='用户名长度必须大于%(min)d'),
# validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[$@$!%*?&])[A-Za-zd$@$!%*?&]{8,}",
# message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
],
render_kw={'placeholder':'请输入密码'}
)
@app.route('/login',methods=['GET','POST'])
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.pwd是PasswordField()对象,PasswordField().__str__
return render_template('login.html',form=form)
form = LoginForm(formdata=request.form)
if form.validate():
print(form.data)
return redirect('https://www.luffycity.com/home')
else:
# print(form.errors)
return render_template('login.html', form=form)
import helper
class UserForm(Form):
city = core.SelectField(
label='城市',
choices=(),
coerce=int
)
name = simple.StringField(label='姓名')
def __init__(self,*args,**kwargs):
super(UserForm,self).__init__(*args,**kwargs)
self.city.choices=helper.fetch_all('select id,name from tb1',[],type=None)
@app.route('/user')
def user():
if request.method == "GET":
#form = UserForm(data={'name':'alex','city':3}) #默认传入值,在编辑用户界面适合用a
form = UserForm()
return render_template('user.html',form=form)
if __name__ == '__main__':
app.run()
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 method="post" novalidate> <p>用户名:{{form.name}} {{form.name.errors[0]}}</p> <p>密码:{{form.pwd}} {{form.pwd.errors[0]}} </p> <p><input type="submit" value="提交" ></p> </form> </body> </html>
32、wtforms的注册实例,数据库实时更新
出现数据库未实时更新的原因是如下,类的静态字段在实例化时只会导入一次,之后不作处理
若想实时更新,需要将静态字段放在__init__里,每次实例化的时候都导入一次
class Foo(object): country = helper.fetch_all('select id,name from tb1',[],type=None) def __init__(self): self.name = '东北' print(Foo.country) obj = Foo() obj = Foo() obj = Foo() obj = Foo() obj = Foo() obj = Foo()
views.py
class RegisterForm(Form): name = simple.StringField( label='用户名', validators=[ validators.DataRequired() ], widget=widgets.TextInput(), render_kw={'class': 'form-control'}, default='alex' ) 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 # int("1") ) 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, default=[1, ] ) @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('https://www.luffycity.com/home') return render_template('register.html', form=form) import helper class UserForm(Form): city = core.SelectField( label='城市', choices=(), coerce=int ) name = simple.StringField(label='姓名') def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) #数据库实时更新
self.city.choices=helper.fetch_all('select id,name from tb1',[],type=None) @app.route('/user') def user(): if request.method == "GET": #form = UserForm(data={'name':'alex','city':3}) form = UserForm() return render_template('user.html',form=form) if __name__ == '__main__': app.run()
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 method="post" novalidate> {% for field in form %} <p>{{field.label}}: {{field}} {{field.errors[0]}}</p> {% endfor %} <input type="submit" value="提交"> </form> </body> </html>
33、review
第一部分:Flask 1. flask和django比较? 2. wsgi? 3. flask上下文理解? 两类: 请求上下文管理 应用上下文管理 流程: 请求到来: 将请求和session相关封装到ctx = RequestContext对象中。 将app和g封装到app_ctx = AppContext对象中。 再通过LocalStack对象将ctx、app_ctx封装到Local对象中。 问题: Local是什么?作用? LocalStack是什么?作用? 获取数据 通过LocalProxy对象+偏函数,调用LocalStack去Local中获取响应ctx、app_ctx中封装的值。 问题: 为什么要把 ctx=request/session app_ctx = app/g ? 答:因为离线脚本需要使用app_ctx。 请求结束: 调用LocalStack的pop方法,将ctx和app_ctx移除。 4. threading.local 5. 偏函数 6. 单例模式 7. 问题: before_request的执行时机(源码实现),在local之后,因为local需要request
34、review - database Mysql
1. 数据库引擎 2. 数据库授权 3. 表结构设计:代码统计(教育机构,班级表结构设计) 4. SQL语句 https://www.cnblogs.com/wupeiqi/articles/5729934.html 5. 了解:https://www.cnblogs.com/wupeiqi/articles/5713323.html - 视图 - 存储过程 - 触发器 - 函数 select max(id) from tb group by xid; 6. 索引 索引作用:加速查找+约束。 索引种类: - 主键索引:加速查找、不重复、非空 - 唯一索引:加速查找、不重复 - 普通索引:加速查找 - 联合索引:加速查找 - 联合唯一索引:加速查找、不重复 PS:联合索引遵循最左前缀原则。 id name pwd email select * from tb where name='x' select * from tb where name='x' and pwd='123' select * from tb where name='x' and pwd='123' and email='xs' 名词: - 覆盖索引:在索引文件中就可以把想要的数据得到。 select name from tb1; - 索引合并:使用多个单列索引去查找数据。
35、类的知识点储备
1、对象可以被for循环
- form对象为什么可以被for循环? 答:变为可迭代对象。 class Foo(object): # def __iter__(self): # return iter([11,22,33]) #iter()为生成器 def __iter__(self): yield 1 yield 2 #生成器也是迭代器的一种 yield 3 obj = Foo() for item in obj: print(item)
2、 new方法的返回值决定对象到底是什么?
class Bar(object): pass class Foo(object): def __new__(cls, *args, **kwargs): # return super(Foo,cls).__new__(cls,*args, **kwargs) return Bar() obj = Foo() print(obj)
3、 metaclass
# 1. 类创建的两种方式 # class Foo(object): # a1 = 123 # def func(self): # return 666 # Foo = type("Foo",(object,),{'a1':123,'func':lambda self:666}) # 2. 自定义type # class MyType(type): # pass # # class Foo(object,metaclass=MyType): # a1 = 123 # def func(self): # return 666 # # Foo = MyType("Foo",(object,),{'a1':123,'func':lambda self:666}) # 注意:metaclass作用是指定当前类由谁来创建。
分析
- 创建类时,先执行type的__init__。 - 类的实例化时,执行type的__call__,__call__方法的的返回值就是实例化的对象。 __call__内部调用: - 类.__new__,创建对象 - 类.__init__,对象的初始化 class MyType(type): def __init__(self,*args,**kwargs): super(MyType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): obj = cls.__new__(cls) cls.__init__(obj,*args, **kwargs) 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()
36、SQLAlchemy
SQLAlchemy,ORM框架。 作用:帮助我们使用类和对象快速实现数据库操作。 数据库: - 原生: - MySQLdb:py2 - pymysql:py2/py3 http://www.cnblogs.com/wupeiqi/articles/5095821.html - ORM框架 - SQLAlchemy
使用
SQLAlchemy使用: 参考:https://www.cnblogs.com/wupeiqi/articles/8259356.html 1. 单表操作 表: from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column from sqlalchemy import Integer,String,Text,Date,DateTime from sqlalchemy import 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() 行: 示例: from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import Users 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 # 多久之后对线程池中的线程进行一次连接的回收(重置) ) SessionFactory = sessionmaker(bind=engine) # 根据Users类对users表进行增删改查 session = SessionFactory() # 1. 增加 # obj = Users(name='alex') # session.add(obj) # session.commit() # session.add_all([ # Users(name='小东北'), # Users(name='龙泰') # ]) # session.commit() # 2. 查 # result = session.query(Users).all() # for row in result: # print(row.id,row.name) # result = session.query(Users).filter(Users.id >= 2) # for row in result: # print(row.id,row.name) # result = session.query(Users).filter(Users.id >= 2).first() # print(result) # 3.删 # session.query(Users).filter(Users.id >= 2).delete() # session.commit() # 4.改 # session.query(Users).filter(Users.id == 4).update({Users.name:'东北'}) # session.query(Users).filter(Users.id == 4).update({'name':'小东北'}) # session.query(Users).filter(Users.id == 4).update({'name':Users.name+"DSB"},synchronize_session=False) # session.commit() session.close()
常用
# ############################## 其他常用 ############################### # 1. 指定列 # select id,name as cname from users; # result = session.query(Users.id,Users.name.label('cname')).all() # for item in result: # print(item[0],item.id,item.cname) # 2. 默认条件and # session.query(Users).filter(Users.id > 1, Users.name == 'eric').all() # 3. between # session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all() # 4. in # session.query(Users).filter(Users.id.in_([1,3,4])).all() # session.query(Users).filter(~Users.id.in_([1,3,4])).all() # 5. 子查询 # 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(Users.id > 3, Users.name == 'eric').all() # 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 # session.query(Users).filter_by(name='alex').all() # 8. 通配符 # ret = session.query(Users).filter(Users.name.like('e%')).all() # ret = session.query(Users).filter(~Users.name.like('e%')).all() # 9. 切片 # result = 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. group by from sqlalchemy.sql import func # ret = session.query( # Users.depart_id, # func.count(Users.id), # ).group_by(Users.depart_id).all() # for item in ret: # print(item) # # 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.union 和 union all """ select id,name from users UNION select id,name from users; """ # 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()