yyz notes yyz notes
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

益章

可乐鸡翅
首页
  • RBAC权限设计
  • 架构图标设计
  • 账号体系
  • python基础
  • python高级
  • python模块
  • python设计模式
  • python数据结构与算法
  • django
  • django-DRF
  • flask
  • 直接设计开源pip包
  • 直接设计开源项目
  • python示例题/脚本
  • python面试题
  • golang基础
  • golang高级
  • golang常用组件
  • gin框架
  • es6
  • javascript
  • react
  • vue
  • TypeScript
  • mysql
  • redis
  • minio
  • elasticsearch
  • mongodb
  • 消息队列
  • 自动化测试
  • 操作系统

    • linux
    • windows
  • nginx
  • docker
  • k8s
  • git
  • ldap
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • python基础

  • python高级

  • python模块

  • python设计模式

  • python数据结构与算法

  • django

  • django-DRF

  • flask

    • flask基础

      • flask基础(1)
      • flask基础(2)
        • day03
          • 内容回顾
          • 今日内容
          • 1、上下文管理
          • 1.1 `__call__`
          • 1.2 `__getitem__`
          • 1.3 `__getattr__`
          • 1.4 偏函数
          • 1.5 threading Local()
          • a. 使用local方法
          • b.!自定义实现Local(),重点看一下
          • c. 仿照flask用栈来实现自定义threading.local的存取
          • 1.6 flask的上下文管理机制
          • 1. 请求上下文管理
          • a. 请求进来时
          • b. 执行视图函数时
          • c. 请求结束前
          • 2 应用上下文
          • a.应用g
        • day04
          • 内容回顾
          • 今日内容
          • 1、flask_session
          • 2、数据库连接池
          • 3、wtforms
        • day05
          • 内容回顾
          • 今日内容
          • 1、面向对象相关
          • 1.1 `__mro__`
          • 1.2 `__dict__`
          • 1.3 metaclass
          • 1. 创建类的二种方式
          • 2.创建自定义的type类
        • dayo6
          • 内容回顾
          • 今日内容
          • 1、flask_script
          • 2、flask-sqlalchemy
          • 3、 SQLAlchemy两种创建session的方式
          • 4、flask-migrate
          • 5、多app应用(url进行处理和分发)
          • 6、信号:blinker
    • flask项目

  • 自己设计开源pip包

  • 自己设计开源项目

  • python小示例

  • python面试题

  • python
  • flask
  • flask基础
YiZhang-You
2023-05-20
目录

flask基础(2)

# day03

# 内容回顾

# 1. 面向对象:特殊方法
   obj['x'] = 123
   obj.x = 123
   obj + 123
# 2. functools
   def func(a1,a2,a3):
      return a1 + a2 + a3

   v1 = func(1,2,3)

   new_func = functools.partial(func,111,2)  # partial帮助我们传递参数原来func需要传递三个参数,现在只用传递一个 a3=3
   new_func(3)
1
2
3
4
5
6
7
8
9
10
11
12

# 今日内容

# 1、上下文管理

我看的这个:<https://www.cnblogs.com/zhaopanpan/articles/9457343.html>
<https://cloud.tencent.com/developer/article/1794125>
1
2

django、tornado是通过传参的形式传递数据,而flask是通过其特有的上下文管理机制来管理数据的。

在flask中,上下文管理机制分为两个大的部分:请求上下文和应用上下文。

在了解flask上下文管理机制之前,先来一波必知必会的知识点

# 1.1 __call__

创建对象的

这个方法相信大家并不陌生,在单例模式中,我们可能用到过,除此之外,还想就没有在什么特殊场景中用到了。我们往往忽视了它一个很特殊的用法:对象object+()或者类Foo()+()这种很特殊的用法。在Flask上下文管理中,入口就是使用了这种方式。

# 1.2 __getitem__

使用这个系列的方法时,我们最大的印象就是调用对象的属性可以像字典取值一样使用中括号[]。使用中括号对对象中的属性进行取值、赋值或者删除时,会自动触发对应的getitem、setitem、delitem方法。

class Foo(object):

    def __init__(self):
        self.name = "boo"

    def __getitem__(self, item):
        print("调用__getitem__了", item)
        if item in self.__dict__:
            return self.__dict__[item]

    def __setitem__(self, key, value):
        print("调用__setitem__方法了", key, value)
        self.__dict__[key] = value

    def __delitem__(self, key):
        print("调用__delitem__", key)
        del self.__dict__[key]

foo = Foo()
ret = foo["name"]
# print(ret)     # 输出     调用__getitem__了      boo
foo["age"] = 18
# print(foo["age"])   # 输出   调用__setitem__方法了   调用__getitem__了    18
del foo["age"]  # 输出  调用__delitem__

"""
在类中实现了 __getitem__ 方法可以使用对象[属性] 拿到对应的值,
在类中实现了 __setitem__ 方法可以直接对象[属性] 添加值
在类中实现了 __delitem__ 方法  foo["age"]
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 1.3 __getattr__

使用对象取值、赋值或者删除时,会默认的调用对应的getattr、setattr、delattr方法。

对象取值时,取值的顺序为:先从getattribute中找,第二步从对象的属性中找,第三步从当前类中找,第四步从父类中找,第五步从getattr中找,如果没有,直接抛出异常。

class Foo(object):

    def __init__(self):
        self.name = "boo"

    def __getattr__(self, item):
        print("调用__getattr__了", item)

    def __setattr__(self, key, value):
        print("调用__setattr__方法了", key, value)

    def __delattr__(self, item):
        print("调用__delattr__", item)

foo = Foo()
ret = foo.xxx  # 输出     调用__getattr__了
foo.age = 18  # 调用__setattr__方法了
del foo.age  # 输出  调用__delattr__
"""
调用__setattr__方法了 name boo
调用__getattr__了 xxx
调用__setattr__方法了 age 18
调用__delattr__ age
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 1.4 偏函数

python中有一个小工具包functools,这个包中包含了几个在很有用的小功能,比如:

  • wraps:在使用装饰器时,使用这个方法可以保护函数的元信息。
  • reduce:一个合并序列项为一个单一值的小方法。
  • 还有一个就是偏函数: partial

一句话来总结partial的作用,固定函数中的一些参数,返回一个新的函数,方便调用

from functools import partial

class Foo(object):

    def __init__(self):
        self.request = "request"
        self.session = "session"

foo = Foo()

def func(args):
    return getattr(foo, args)

re_func = partial(func, 'request')  # 帮助源函数传递值
se_func = partial(func, 'session')

print(re_func())
print(se_func())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 1.5 threading Local()

详细资料:<http://c.biancheng.net/view/2628.html>
1

在多线程中,同一个进程中的多个线程是共享一个内存地址的,多个线程操作数据时,就会造成数据的不安全,所以我们就要加锁。但是,对于一些变量,如果仅仅只在本线程中使用,怎么办?

方法一,可以通过全局的字典,key为当前线程的线程ID,value为具体的值。(自定义方法)

方法二,使用threading.local方法

作用:为每一个线程开辟一块空间进行数据存储,空间和空间是互相隔离的

# a. 使用local方法

import time
import threading

local = threading.local()

def func(n):
    local.val = n
    time.sleep(5)
    print(n)

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

# 结果输出    0--9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# b.!自定义实现Local(),重点看一下

定制粒度更细的

try:
    from greenlet import getcurrent as get_ident
except Exception as e:
    from threading import get_ident
from threading import Thread
import time

class Local(object):

    def __init__(self):
        # self.storage = {}
        # 为什么不能直接这样,因为当进来Local对象中的self.storage是调用内部的__setattr__方法创建的,但是这里被我们重写了,所以不会去创建实例变量
        # 我们就要去调用父类的__setattr__方法进行创建
        object.__setattr__(self, 'storage', {})  # self.storage = object.__setattr__(self, 'storage', {})

    def __setattr__(self, k, v):
        ident = get_ident()  # 得到线程或者协程的id
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}

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

obj = Local()

def task(arg):
    obj.val = arg  # 在类中实现了__setattr__(k,v) 就可以通过 对象.key =值 直接存
    # print(obj.storage)
    # obj.xxx = arg
    time.sleep(2)
    print(obj.val)  # 在类中实现了__getattr__(k,) 就可以通过 对象.key 直接获取字典的值

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

"""
1.可以通过协程的方式或者线程的方式创建
{24244: {'val': 0}}
{24244: {'val': 0}, 19356: {'val': 1}}
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# c. 仿照flask用栈来实现自定义threading.local的存取

from greenlet import getcurrent

class Local(object):

    def __init__(self):
        object.__setattr__(self, "_storage", {})

    def __setattr__(self, key, value):

        # ident = threading.get_ident()
        ident = getcurrent()  # 得到协程的id 定制粒度更细的
        if ident in self._storage:
            self._storage[ident][key] = value
        else:
            self._storage[ident] = {key: value}

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

class LocalStack(object):

    def __init__(self):
        self.local = Local()

    def push(self, item):
        self.local.stack = []
        self.local.stack.append(item)

    def pop(self):
        return self.local.stack.pop()

    def top(self):
        return self.local.stack[-1]

_local_stack = LocalStack()
_local_stack.push(55)
_local_stack.push(32)
_local_stack.push(21)
print(_local_stack.top())  # 取栈顶元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 1.6 flask的上下文管理机制

在flask中,上下文管理机制分为两个大的部分:请求上下文和应用上下文。

  • 请求上下文:request/session
  • 应用上下文:app/g

从以下三个大的方面分别探讨flask的两大上下文管理机制。

  1. 方面一:请求进来时
  2. 方面二:视图函数
  3. 方面三:请求结束前

# 1. 请求上下文管理

先来一个最简单的flask版的Hello World

from flask import Flask

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run()
1
2
3
4
5
6
7
8
9
10

启动一个flask项目时,会先执行app.run()方法,这是整个项目的入口,执行run方法时,接着执行werkzeug模块中的run_simple

https://images2018.cnblogs.com/blog/1316789/201808/1316789-20180810225921512-294502634.png

werkzeug中触发调用了Flask的call方法

# a. 请求进来时

触发执行call方法,call方法的逻辑很简单,直接执行wsgi_app方法,将包含所有请求相关数据和一个响应函数传进去。

https://images2018.cnblogs.com/blog/1316789/201808/1316789-20180810231226918-1233584610.png

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

    environ:一个包含所有HTTP请求信息的dict对象;

    start_response:一个发送HTTP响应的函数。

在application()函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])
1
2
3
4
5
6
7
8
9
10
11
12
13

备注:call是一个符合wsgi标准的函数

执行wsgi_app方法

def wsgi_app(self, environ, start_response):

        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第一步先执行了一个request_context的方法,将environ传进去,最后返回一个RequestContext类的对象,被ctx的变量接收(ctx=request_context(environ))

 def request_context(self, environ):
        """Create a :class:`~flask.ctx.RequestContext` representing a
        WSGI environment. Use a ``with`` block to push the context,
        which will make :data:`request` point at this request.

        See :doc:`/reqcontext`.

        Typically you should not call this from your own code. A request
        context is automatically pushed by the :meth:`wsgi_app` when
        handling a request. Use :meth:`test_request_context` to create
        an environment and context instead of this method.

        :param environ: a WSGI environment
        """
        return RequestContext(self, environ)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这个ctx对象在初始化时,赋了两个非常有用的属性,一个是request,一个是session

def __init__(self, app, environ, request=None):
    self.app = app
    if request is None:
        request = app.request_class(environ)
    self.request = request
    self.url_adapter = app.create_url_adapter(self.request)
    self.flashes = None
    self.session = None
1
2
3
4
5
6
7
8

这两个属性中request是一个Request()对象,这个对象就是我们在flask中使用的request对象,为我们提供了很多便捷的属性和方法,比如:request.method、request.form、request.args等等,另一个属性是session,初始为None。

紧接着执行ctx.push()方法,这个方法中,在执行请求上下文对象ctx之前先实例化了一个app_context对象,先执行了app_context的push方法,然后才执行request_ctx_stack对象中的top和request_ctx_stack.push(self),最后对ctx中的session进行处理。

所以,flask中的应用上下文发生在请求上下文之前。

def push(self):

        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        # 在执行request_context请求上下文的push方法时,先执行了app_context应用上下文的push方法
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()

        # 然后执行请求上下文对象中LocalStack对象的push方法
        _request_ctx_stack.push(self)

        # 最后处理session
        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(
                self.app, self.request
            )

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

但是我们先说请求上下文,在处理完应用上下文的push方法后,紧接着执行了_request_ctx_stack对象的两个方法。

而这个*request_ctx_stack是LocalStack这个类的对象。*request_ctx_stack = LocalStack()

LocalStack有没有很眼熟,没错,flask内部使用的机制就是类似于我们上文中自定义的LocalStack的机制,实例化过程中使用了面向对象中的组合概念,self._local = Local(),然后在自身又实现了push、pop、top方法,这三个方法中都是通过反射从Local类的实例化对象中对一个stack属性进行append、pop、[-1]的操作,所以,Local对象中的stack属性对应的值一定是一个类似于列表的东西。通过对列表的操作,实现一个类似于栈的存取。

接着聊聊这个Local类,在实例化时,会对每个对象生成一个storage的空字典。我们翻遍整个Local类的源码,发现内部并没有实现一个叫stack的方法或者属性,但是上面我们提到了LocalStack对象会对Local对象中的一个叫stack的东西进行一系列操作。找不到不会报错吗?

这就是flask的巧妙之处,通过类的一些魔法方法巧妙的实现了相应的处理。在前引中,提到如果对象中没有某个属性,取值时,最终会执行类中的getattr方法,然后再做后续的异常处理,flask将所有的对应逻辑都实现在了类的getattr方法中,将每一个线程存储到字典中,在请求进来时,将每一个对应的请求ctx存在一个列表中,使用时直接调用,而不是通过传参的形式,更体现出了flask框架的轻量级。

处理完_request_ctx_stack后,就该处理session了。

在flask中,处理session时,非常的巧妙,完美的遵循了开闭原则,会先执行session_interface对象的open_session方法,在这个方法中,会先从用户请求的cookie中获取sessionid,获取该用户之前设置的session值,然后将值赋值到ctx.session中。

处理完session后,ctx.push方法就执行完了,返回到最开始的app.wsgi_app方法中,执行完push方法后,接着执行full_dispatch_request方法,从这个名字中我们也能猜到,这个方法只要是负责请求的分发。

def full_dispatch_request(self):

            self.try_trigger_before_first_request_functions()
            try:
                request_started.send(self)
                rv = self.preprocess_request()
                if rv is None:
                    rv = self.dispatch_request()
            except Exception as e:
                rv = self.handle_user_exception(e)
            return self.finalize_request(rv)
1
2
3
4
5
6
7
8
9
10
11

在full_dispatch_request方法中先执行preprocess_request方法,这个方法,会先执行所有被before_request装饰器装饰的函数,然后就通过路由的分发执行视图函数了(dispatch_request)

# b. 执行视图函数时

在执行视图函数之前,先执行了before_request,在执行我们的视图函数。

视图函数主要处理业务逻辑。在视图函数中可以调用request对象,进行取值,也可以调用session对象对session的存取。

在整个request的请求生命周期中,获取请求的数据直接调用request即可,对session进行操作直接调用session即可。request和session都是LocalProxy对象,借助偏函数的概念将对应的值传入lookup_req_object函数。先从request_ctx_stack(LocalStack)对象中获取ctx(请求上下文对象),再通过反射分别获取request和session属性。整个过程中LocalStack扮演了一个全局仓库的角色,请求进来将数据存取,需要时即去即用。所以,flask实现了在整个请求的生命周期中哪儿需要就直接调用的特色。

request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
1
2

# c. 请求结束前

视图函数执行完后,dispatch_request执行结束,执行full_dispatch_request方法的返回值finalize_request方法。这个方法中,同样的,在返回响应之前,先执行所有被after_request装饰器装饰的函数。

---->finalize_request ------> process_response
 def process_response(self, response):

        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
        for handler in funcs:
            response = handler(response)
        if not self.session_interface.is_null_session(ctx.session):
            self.session_interface.save_session(self, ctx.session, response)
        return response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

执行process_response过程中,执行完after_request后,然后,执行session的save_session方法。将内存中保存在ctx.session的值取到后,json.dumps()序列化后,写入响应的cookie中(set_cookie),最后返回响应。

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name,
                    domain=domain,
                    path=path
                )

            return

        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add('Cookie')

        if not self.should_set_cookie(app, session):
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))
        # set_cookie将session写入响应的cookie中
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite
        )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

返回响应后,自动的调用ctx.auto_pop(error),将Local中存储的ctx对象pop掉,整个请求结束。

请求上下文的执行流程:

https://images2018.cnblogs.com/blog/1316789/201808/1316789-20180813170846967-1549165844.png

# 2 应用上下文

与请求上下文类似,当请求进来时,先实例化一个AppContext对象app_ctx,在实例化的过程中,提供了两个有用的属性,一个是app,一个是g。self.app就是传入的全局的app对象,self.g是一个全局的存储值的对象。接着将这个app_ctx存放到LocalStack()。

class AppContext(object):
	def __init__(self, app):
		self.app = app
		self.url_adapter = app.create_url_adapter(None)
		self.g = app.app_ctx_globals_class()
1
2
3
4
5

视图函数中,我们就可以调用app对象和g对象,如果我们使用蓝图构建我们的项目时,在每一个直接引用app就会造成循环引用的异常,这时,应用上下文就会显得非常有用,我们可以直接调用current_app就可以在整个生命周期中使用我们的app对象了。比如使用我们的配置项:current_app.config

current_app = LocalProxy(_find_app)
1

最后,当视图函数执行结束后,从storage中pop掉app_ctx对象。

from flask import Flask,request,session,g

app = Flask(__name__)  # type:Flask

@app.before_request
def auth_demo():
    g.val = 123

@app.route('/')
def index():
    print(g.val)
    return "Hello World"

if __name__ == '__main__':
    app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

总结:flask中的上下文管理机制分为请求上下文和应用上下文两大方面,通过上下文的管理机制,实现了即去即用的一个特色。

# a.应用g

from flask import Flask, g, request

app = Flask(__name__)

@app.before_request
def bf():
    g.x = 666

@app.route('/n1')
def n1():
    print(g.x)  # 这样就可以共享全局的值了,都可以存在g中
    return 'n1'

@app.route('/n2')
def n2():
    print(g.x)
    return 'n2'

if __name__ == '__main__':
    app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# day04

# 内容回顾

# 今日内容

# 1、flask_session

详细:<https://blog.csdn.net/weixin_43976393/article/details/87983656?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-87983656-blog-83781020.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-87983656-blog-83781020.pc_relevant_antiscanv2&utm_relevant_index=1>
1

作用:将默认保存的签名cookie中的值,保存到redis/memcached/file/mangodb/SQLAlchemy中

注意:session中存储的是字典,修改字典内部元素时,会造成数据不更新,解决方法:

  • motified = True
  • SESSION_REFRESH_EACH_REQUEST = True and session.permanent = True(redis中默认)

存入session的方式,将session存入数据库中,不使用默认的了

"""
1. flask-session
    作用:将默认保存的签名cookie中的值 保存到 redis/memcached/file/Mongodb/SQLAlchemy
"""
from flask import Flask, request, session

app = Flask(__name__)
app.secret_key = 'abasd'

# 默认的session存储方式
from flask.sessions import SecureCookieSessionInterface
app.session_interface = SecureCookieSessionInterface()

# 其他
# 方式一:保存到redis中
from redis import Redis
from flask_session import RedisSessionInterface

app.session_interface = RedisSessionInterface(
    redis=Redis(host='127.0.0.1', port=6379),
    key_prefix='rediszz'  # session的前缀
)
# 方式二 保存在redis中
from flask_session import Session
from redis import Redis

app.config['SESSION_TYPE'] = 'REDIS'
app.config['SESSION_REDIS'] = Redis(host='', db='', port='', password='', encoding='')
Session(app)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return 'login'
    user = request.form.get('user')
    pwd = request.form.get('pwd')
    session['user_info'] = {'user': user, 'pwd': pwd}
    return 'login'

@app.route('/index', methods=['GET'])
def index():
    user_info = session.get('user_info')
    return 'index'

if __name__ == '__main__':
    app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 2、数据库连接池

pip install DBUtils

模式:
  - 每个线程创建一个连接,关闭(默认不关闭),线程终止时,才关闭连接。
  - 创建共享连接池
应用:只要写原生SQL,用户数据框连接池
1
2
3
4
5
6

# 3、wtforms

基本使用:<http://www.cnblogs.com/wupeiqi/articles/8202357.html>

作用:用于对python web框架做表单验证。

	使用:
		class MyForm(Form):
			user = 类(正则,插件)
			字段 = 类(正则,插件)
			字段 = 类(正则,插件)
			字段 = 类(正则,插件)
			字段 = 类(正则,插件)
			字段 = 类(正则,插件)

		form = MyForm()
		# 生成HTML标签
		print(form.user) 类.__str__ ==> 插件.xx方法

		# 验证
		form = MyForm(formdata=request.form)
		if form.validate():
			# 内部找到所有的字段:user + 用户发过来的数据 =》 正则校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# day05

# 内容回顾

# 今日内容

# 1、面向对象相关

# 1.1 __mro__

得到类继承的执行顺序

class A(object):
    pass

class B(A):
    pass

class C(object):
    pass

class D(B, C):
    pass

print(D.__mro__)
# 得到类继承的执行顺序
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 1.2 __dict__

以字典的形式查看对象或者字典的属性

class Foo(object):
    CITY = 'bj'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def func(self):
        pass

# print(Foo.CITY)
# print(Foo.func)

# 类的__ dict __ 属性
print(Foo.__dict__)

# 类对象 的__ dict __ 属性
obj1 = Foo('小明', 54)
print(obj1.__dict__)

# __ dict __  查看对象其属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1.3 metaclass

# 1. 创建类的二种方式

# 方式一:
class Foo(object):
    CITY = "bj"

    def func(self, x):
        return x + 1
foo = Foo()
print(foo.func(10))

# 方式二:
Foo2 = type('Foo2', (object,), {'CITY': 'bj1', 'func': lambda self, x: x + 1})
foo2 = Foo2
print(foo2.CITY)
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.创建自定义的type类

因为类都是继承type去创建的,我们可以继承type,在其前后执行一些操作

	1. 默认类由type实例化创建。
    2. 某个类指定metaclass=MyType,那么当前类的所有派生类都由于MyType创建。
    3. 实例化对象对象顺序
      - type.__init__
      - type.__call__
      - 类.__new__
      - 类.__init__
class MyType(type):
    def __init__(self,*args,**kwargs):
        print('创建类之前')
        super(MyType,self).__init__(*args,**kwargs)  # 创建FOO类
        print('创建类之后')

class Foo(object,metaclass=MyType): # 当前类,由type类创建。
    CITY = "bj"
    def func(self, x):
        return x + 1
# ############# 创建类 ###########
# 1. 创建类的两种方式
"""
class Foo(object,metaclass=type):
    CITY = "bj"

    def func(self,x):
        return x + 1

Foo = type('Foo',(object,),{'CITY':'bj','func':lambda self,x:x+1})
"""

# 2. 类由自定义type创建
#    类由type创建,通过metaclass可以指定当前类由那一个type创建。
"""
class MyType(type):
    def __init__(self,*args,**kwargs):
        print('创建类之前')
        super(MyType,self).__init__(*args,**kwargs)
        print('创建类之后')

class Foo(object,metaclass=MyType): # 当前类,由type类创建。
    CITY = "bj"
    def func(self, x):
        return x + 1
"""
# 3. 类的继承
#    类的基类中指定了metaclass,那么当前类也是由metaclass指定的类来创建当前类。
"""
class MyType(type):
    def __init__(self,*args,**kwargs):
        print('创建类之前')
        super(MyType,self).__init__(*args,**kwargs)
        print('创建类之后')

class Foo(object,metaclass=MyType): # 当前类,由type类创建。
    CITY = "bj"
    def func(self, x):
        return x + 1

class Bar(Foo):
    pass
"""

# ################### 变 ##################
"""
class MyType(type):
    def __init__(self,*args,**kwargs):
        print('创建类之前')
        super(MyType,self).__init__(*args,**kwargs)
        print('创建类之后')

Base = MyType('Base',(object,),{})
# class Base(object,metaclass=MyType):
#     pass

class Foo(Base):
    CITY = "bj"
    def func(self, x):
        return x + 1
"""
# ################ 变 #################
"""
class MyType(type):
    def __init__(self,*args,**kwargs):
        print('创建类之前')
        super(MyType,self).__init__(*args,**kwargs)
        print('创建类之后')

def with_metaclass(arg):
    return MyType('Base',(arg,),{}) # class Base(object,metaclass=MyType): pass

class Foo(with_metaclass(object)):
    CITY = "bj"
    def func(self, x):
        return x + 1
"""

# ########################## 实例化 ######################
class MyType(type):
    def __init__(self, *args, **kwargs):
        super(MyType, self).__init__(*args, **kwargs)

class Foo(object, metaclass=MyType):  # 当前类,由type类创建。
    pass

"""
内部执行顺序
0. Mytype的__init__
obj = Foo()
1. MyType的__call__
2. Foo的__new__
3. Foo的__init__
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

# dayo6

# 内容回顾

# 今日内容

# 1、flask_script

  • python manage.py (opens new window) runserver
  • python manage.py (opens new window) 自定义命令

自定义脚本命令

a. pip install flask_script

b.manager = Manager(app) # 传递app

c. manager.run() # 改了就可以使用 python manage.py (opens new window) runserver运行

d.自定义命令脚本

from flask_migrate import Migrate

from s8day130_pro import create_app,db
from flask_script import Manager

app = create_app()
manager = Manager(app)  # 传递app

@manager.command
def custom(arg):  # 自定义脚本命令
    """
    自定义命令
    python manage.py custom 123
    :param arg:
    :return:
    """
    print(arg)

@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
    """
    自定义命令
    执行: python manage.py  cmd -n wupeiqi -u <http://www.oldboyedu.com>
    执行: python manage.py  cmd --name wupeiqi --url <http://www.oldboyedu.com>
    :param name:
    :param url:
    :return:
    """
    print(name, url)

if __name__ == '__main__':
    # app.run()
    manager.run()  # 改了就可以使用 python manage.py runserver运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 2、flask-sqlalchemy

作用:将SQLAlchemy相关的所有功能都封装到db=flask_sqlalchemy.SQLAlchemy()对象中
    - 创建表
        class User(db.Model):
        pass

    - 操作表
        db.session

   扩展:离线脚本编写
      from s8day130_pro import db
      from s8day130_pro import create_app
      from s8day130_pro import models

      app = create_app()
      with app.app_context():
         # db.drop_all()
         # db.create_all()
         data = db.session.query(models.Users).all()
         print(data)
   步骤:
      1. 在 __init__.py中创建db对象
         from flask_sqlalchemy import SQLAlchemy

         # 包含了SQLAlchemy相关的所有操作
         db = SQLAlchemy()

      2. 在 __init__.py中create_app函数中让将app传入到db中

         def create_app():
            app = Flask(__name__)
            app.config.from_object('settings.DevelopmentConfig')

            from .views.account import ac
            app.register_blueprint(ac)

            # 看这里看这里
            db.init_app(app)

            return app

      3. 写配置文件,将连接字符串定义在配置文件中
             SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:123456@127.0.0.1:3306/s8day130db?charset=utf8"
            SQLALCHEMY_POOL_SIZE = 5
            SQLALCHEMY_POOL_TIMEOUT = 30
            SQLALCHEMY_POOL_RECYCLE = -1

      4. 定义 s8day130_pro/models.py

            #!/usr/bin/env python
            # -*- coding:utf-8 -*-
            from sqlalchemy.ext.declarative import declarative_base
            from sqlalchemy import Column, Integer, String, UniqueConstraint, Index,DateTime,ForeignKey
            from s8day130_pro import db

            class Users(db.Model):
               __tablename__ = 'users'
               id = Column(Integer, primary_key=True,autoincrement=True)
               name = Column(String(32),nullable=False,unique=True)

      5. 创建数据库表,编写离线脚本:drop_create_table.py
            from s8day130_pro import db
            from s8day130_pro import create_app
            from s8day130_pro import models

            app = create_app()
            with app.app_context():
               db.drop_all()
               db.create_all()
               #data = db.session.query(models.Users).all()
               #print(data)

      6. 在视图函数中使用SQLAlchemy操作数据库
            from s8day130_pro import models
            from s8day130_pro import db
            ac = blueprints.Blueprint('ac',__name__)

            @ac.route('/login',methods=['GET','POST'])
            def login():
               data = db.session.query(models.Users).all()
               print(data)
               db.session.remove()
               return 'Login'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

# 3、 SQLAlchemy两种创建session的方式

	方式一:
         import models
         from threading import Thread
         from sqlalchemy.orm import sessionmaker
         from sqlalchemy import create_engine

         engine =create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8day128db?charset=utf8",pool_size=2,max_overflow=0)
         XXXXXX = sessionmaker(bind=engine)

         def task():
            from sqlalchemy.orm.session import Session
            session = XXXXXX()

            data = session.query(models.Classes).all()
            print(data)

            session.close()

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

      方式二(推荐):
         import models
         from threading import Thread
         from sqlalchemy.orm import sessionmaker
         from sqlalchemy import create_engine
         from sqlalchemy.orm import scoped_session

         engine =create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/s8day128db?charset=utf8",pool_size=2,max_overflow=0)
         XXXXXX = sessionmaker(bind=engine)

         session = scoped_session(XXXXXX)

         def task():
            # 1. 原来的session对象 = 执行session.registry()
            # 2. 原来session对象.query
            data = session.query(models.Classes).all()
            print(data)
            session.remove()

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

   flask-session默认也是使用的第二种方式:scoped_session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 4、flask-migrate

帮助我们像django一样可以生成,迁移数据库

	作用:做数据库迁移
    依赖:
      flask-script
      flask-sqlalchemy

   Migrate(app, db)
   """
   # 数据库迁移命名
      python manage.py db init
      python manage.py db migrate # makemigrations
      python manage.py db upgrade # migrate
   """
   manager.add_command('db', MigrateCommand)
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5、多app应用(url进行处理和分发)

多个app一起启动通过前面加app名称去访问对应的app

- 多app应用(url进行处理和分发)
   from flask import Flask
   from werkzeug.wsgi import DispatcherMiddleware
   from werkzeug.serving import run_simple

   app01 = Flask('app01')
   app02 = Flask('app02')

   @app01.route('/login')
   def login():
      return 'app01.login'

   @app02.route('/index')
   def index():
      return 'app02.index'

   dm = DispatcherMiddleware(app01,{
      '/app02':        app02,
   })

   if __name__ == '__main__':
      run_simple('localhost', 5000,dm)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 6、信号:blinker

请求前,后,中执行信号

更多详细:<https://www.cnblogs.com/wupeiqi/articles/8249576.html>
使用方式:<https://www.cnblogs.com/WiseAdministrator/articles/11217681.html>
- 信号:blinker

   appcontext_pushed = _signals.signal('appcontext-pushed')
   request_started = _signals.signal('request-started')

   如果有render:
      before_render_template = _signals.signal('before-render-template')
      template_rendered = _signals.signal('template-rendered')

   request_finished = _signals.signal('request-finished')

   如果视图函数有异常:
      got_request_exception = _signals.signal('got-request-exception')

   request_tearing_down = _signals.signal('request-tearing-down')
   appcontext_tearing_down = _signals.signal('appcontext-tearing-down')

   appcontext_popped = _signals.signal('appcontext-popped')

   如果使用信号:
      message_flashed = _signals.signal('message-flashed')

   使用:
      from flask import Flask,signals,render_template,flash

      app = Flask(__name__)

      def func1(*args,**kwargs):
         print('触发信号:request_started')

      def func2(*args,**kwargs):
         print('触发信号:appcontext_pushed')

      signals.request_started.connect(func1)
      signals.appcontext_pushed.connect(func2)

      @app.route('/login')
      def login():
         return render_template('index.html')

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

   问题:信号和before_request区别?

        before_request,可以控制请求是否可以继续往后执行。
        信号,在原来的基础增加额外的操作和值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

1

编辑 (opens new window)
flask基础(1)
flask项目

← flask基础(1) flask项目→

最近更新
01
配置yun源
05-24
02
linux-配置python虚拟环境
05-24
03
linux文件目录管理
05-24
更多文章>
Theme by Vdoing | Copyright © 2023-2023 yizhang | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式