| |
| - 第一步:导入 |
| - 第二步:实例化得到对象,可以指定static和templates |
| - 第三步:app中注册蓝图,注册蓝图时,可以指定前缀 |
| - 第四步:使用蓝图,注册路由,注册请求扩展 |
| |
| |
| |
| -当次请求的全局对象,在当次请求中可以放值和取值 |
| -跟session的区别是 |
| |
| |
| |
| -pymysql,在一个视图函数中,创建一个连接对象,操作,操作完就关闭连接 |
| -连接对象不要用单例,可能会出现数据错乱问题 |
| |
| |
| |
| -dbutils |
| -使用步骤: |
| 1 第一步:导入,实例化得到一个对象, pool |
| 2 第二步:pool做成单例,以模块导入 |
| 3 第三步:从pool中拿到一个连接 pool.connection() |
| 3 第四步:使用连接获得游标,使用游标操作数据库 |
| |
| |
| |
| |
今日内容
1 请求上下文分析(源码:request原理)
1.1 导出项目的依赖
| |
| |
| |
| 第一步:安装 pip3 install pipreqs |
| 第二步:使用命令,导出项目依赖 pipreqs ./ |
| -win由于编码问题会出错:pipreqs ./ --encoding=utf8 |
| -mac,linx没有问题 |
| |
| 第三步:就会在项目根路径下生成:requirements.txt |
1.2 函数和方法
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| from types import MethodType, FunctionType |
| |
| |
| class Foo(object): |
| def fetch(self): |
| pass |
| |
| @classmethod |
| def test(cls): |
| pass |
| |
| @staticmethod |
| def test1(): |
| pass |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def add(): |
| pass |
| |
| |
| |
| print(isinstance(Foo.fetch, MethodType)) |
| obj = Foo() |
| print(isinstance(obj.fetch, MethodType)) |
| print(isinstance(Foo.fetch, FunctionType)) |
| |
| print(isinstance(add, FunctionType)) |
| print(isinstance(add, MethodType)) |
| |
| |
| print(isinstance(Foo.test, MethodType)) |
| |
| print(isinstance(obj.test, MethodType)) |
| |
| print(isinstance(Foo.test1, MethodType)) |
| print(isinstance(obj.test1, MethodType)) |
| print(isinstance(obj.test1, FunctionType)) |
| |
| |
1.3 threading.local对象
| |
| |
| |
| |
| |
| |
| 多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储 |
| 每个线程操作自己的那部分数据 |
1.4 偏函数
| |
| |
| |
| |
| |
| from functools import partial |
| def add(a,b,c): |
| return a+b+c |
| |
| |
| |
| |
| |
| |
| |
| add=partial(add,2) |
| |
| |
| |
| print(add(3,4)) |
| |
| |
1.5 flask 整个生命执行流程(1.1.4版本为例)
| |
| 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) |
| |
| |
| |
| |
| |
| |
| def push(self): |
| |
| _request_ctx_stack.push(self) |
| |
| 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) |
| |
| if self.url_adapter is not None: |
| self.match_request() |
| |
| |
| |
| |
| def push(self, obj): |
| |
| rv = getattr(self._local, "stack", None) |
| |
| if rv is None: |
| rv = [] |
| self._local.stack = rv |
| rv.append(obj) |
| |
| return rv |
| |
| |
| |
| |
| |
| |
| 视图函数中:print(request.method) |
| |
| |
| |
| |
| def __getattr__(self, name): |
| |
| return getattr(self._get_current_object(), name) |
| |
| |
| |
| def _get_current_object(self): |
| if not hasattr(self.__local, "__release_local__"): |
| return self.__local() |
| try: |
| return getattr(self.__local, self.__name__) |
| except AttributeError: |
| raise RuntimeError("no object bound to %s" % self.__name__) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def _lookup_req_object(name): |
| top = _request_ctx_stack.top |
| if top is None: |
| raise RuntimeError(_request_ctx_err_msg) |
| return getattr(top, name) |
| 请求上下文执行流程(ctx): |
| -0 flask项目一启动,有6个全局变量 |
| -_request_ctx_stack:LocalStack对象 |
| -_app_ctx_stack :LocalStack对象 |
| -request : LocalProxy对象 |
| -session : LocalProxy对象 |
| -1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response) |
| -2 wsgi_app() |
| -2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session,flash,当前app对象 |
| -2.2 执行: ctx.push():RequestContext对象的push方法 |
| -2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),self是ctx对象 |
| -2.2.2 去_request_ctx_stack对象的类中找push方法(LocalStack中找push方法) |
| -2.2.3 push方法源码: |
| def push(self, obj): |
| |
| |
| |
| rv = getattr(self._local, "stack", None) |
| if rv is None: |
| |
| self._local.stack = rv = [] |
| |
| |
| rv.append(obj) |
| return rv |
| -3 如果在视图函数中使用request对象,比如:print(request) |
| -3.1 会调用request对象的__str__方法,request类是:LocalProxy |
| -3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object()) |
| -3.2.1 内部执行self._get_current_object() |
| -3.2.2 _get_current_object()方法的源码如下: |
| def _get_current_object(self): |
| if not hasattr(self.__local, "__release_local__"): |
| |
| |
| |
| |
| |
| return self.__local() |
| try: |
| return getattr(self.__local, self.__name__) |
| except AttributeError: |
| raise RuntimeError("no object bound to %s" % self.__name__) |
| -3.2.3 _lookup_req_object函数源码如下: |
| def _lookup_req_object(name): |
| |
| |
| top = _request_ctx_stack.top |
| if top is None: |
| raise RuntimeError(_request_ctx_err_msg) |
| |
| return getattr(top, name) |
| -3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__ |
| -4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性 |
| |
| -5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉 |
| |
| 其他的东西: |
| -session: |
| -请求来了opensession |
| -ctx.push()---->也就是RequestContext类的push方法的最后的地方: |
| 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) |
| -请求走了savesession |
| -response = self.full_dispatch_request() 方法内部:执行了before_first_request,before_request,视图函数,after_request,savesession |
| -self.full_dispatch_request()---->执行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response) |
| -请求扩展相关 |
| before_first_request,before_request,after_request依次执行 |
| -flask有一个请求上下文,一个应用上下文 |
| -ctx: |
| -是:RequestContext对象:封装了request和session |
| -调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置 |
| -app_ctx: |
| -是:AppContext(self) 对象:封装了当前的app和g |
| -调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置 |
| -g是个什么鬼? |
| 专门用来存储用户信息的g对象,g的全称的为global |
| g对象在一次请求中的所有的代码的地方,都是可以使用的 |
| |
| |
| -代理模式 |
| -request和session就是代理对象,用的就是代理模式 |
| |
| - 生成前端模板 |
| - 校验数据 |
| - 渲染错误信息 |
| |
| |
| - 第一步:导入,定义一个类,继承forms |
| -第二步:模板中, for循环生成模板 |
| -第三步:视图函数中,使用form校验数据 |
| |
| |
| |
| |
| from flask import Flask, render_template, request, redirect |
| from wtforms import Form |
| from wtforms.fields import simple |
| from wtforms import validators |
| from wtforms import widgets |
| |
| app = Flask(__name__, template_folder='templates') |
| |
| app.debug = True |
| |
| |
| class LoginForm(Form): |
| |
| name = simple.StringField( |
| label='用户名', |
| validators=[ |
| validators.DataRequired(message='用户名不能为空.'), |
| validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d') |
| ], |
| widget=widgets.TextInput(), |
| render_kw={'class': 'form-control'} |
| |
| ) |
| |
| pwd = simple.PasswordField( |
| label='密码', |
| 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个特殊字符') |
| |
| ], |
| widget=widgets.PasswordInput(), |
| render_kw={'class': 'form-control'} |
| ) |
| |
| |
| |
| @app.route('/login', methods=['GET', 'POST']) |
| def login(): |
| if request.method == 'GET': |
| form = LoginForm() |
| return render_template('login.html', form=form) |
| else: |
| form = LoginForm(formdata=request.form) |
| if form.validate(): |
| print('用户提交数据通过格式验证,提交的值为:', form.data) |
| else: |
| print(form.errors) |
| return render_template('login.html', form=form) |
| |
| if __name__ == '__main__': |
| app.run() |
| |
| |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| </head> |
| <body> |
| <h1>登录</h1> |
| <form 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> |
| <input type="submit" value="提交"> |
| </form> |
| </body> |
| </html> |
| |
补充
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现