一、认识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-z\d$@$!%*?&]{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()

 37、SQLAlchemy