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')
Question

 

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
1.配置文件
反向解析  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()
2.路由系统
FBV
3.视图
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))
4.请求相关
响应体:
    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
5.响应
- 给前端传数据的方式:
    @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">')
             
6.模板渲染
-- 存在加密的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中。        
7.session
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'
8. 闪现
- 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()
9. 中间件
目标:给开发者提供目录
其他:
    自定义模板,静态文件
    某一类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()
10.蓝图
示例程序:学生管理加认证功能 **
版本一:直接给每个函数加认证
    @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
11.特殊装饰器
作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)
    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,))
12.threading.local

上下文管理:

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()
super和执行类的区别
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
        - 不支持事务
        -- 表锁
        - 查询速度快
Mysql数据库引擎
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()

wtforms

作用:

  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 为每个线程开辟独立的空间

3. SQL了解   SQL扩展   SQL无法命中索引

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__方法的返回值就是实例化的对象
Question

SQLAlchemy

  1. 是一个ORM框架

  2. 作用:帮助我们使用类和对象快速实现数据库操作

  原生:

    - MySQLdb:python2

    - pymysql:pymysql2 / pymysql3

    详细

SQLAlchemy参考

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'            
Flask-SQLAlchemy
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
flask-script
 pip insatll pipreqs

在终端输入:
pipreqs ./ --encoding=utf-8
找到项目使用的所有组件和版本

 

 

  

 

posted @ 2020-04-27 15:30  royal天  阅读(184)  评论(0)    收藏  举报