(二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

2024-03-22 1499阅读

温馨提示:这篇文章已超过367天没有更新,请注意相关的内容是否还可用!

目录:

  • 每篇前言:
  • MTV&MVC
  • 构建一个基于MTV模式的Demo项目:
    • 蹦出一个问题:

      每篇前言:

      • 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者

      • 🔥🔥本文已收录于Flask框架从入门到实战专栏:《Flask框架从入门到实战》
      • 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
      • 📝​📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
      • 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
      • 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!
        (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

        MTV&MVC

        先来讲一下两种常见的软件架构模式——MTV和MVC~

        它俩是用于组织和管理应用程序的不同组件和逻辑。通常用于开发Web应用程序和其他软件项目。

        1. MTV(Model-Template-View):
          • Model(模型):表示应用程序的数据和业务逻辑。它负责管理数据的存储、检索和处理,以及处理与数据相关的操作。
          • Template(模板):定义了应用程序用户界面的外观和结构,通常使用模板语言编写,将动态数据插入静态页面中。
          • View(视图):表示用户界面的逻辑部分,它从模型中获取数据并将其呈现到模板中,然后将结果发送给用户的浏览器。视图还可以处理用户的输入和用户界面的交互。

        MTV模式是Django Web框架的一部分(但是这个模式也非常适合Flask),其中Model对应于数据模型,Template对应于模板文件,View对应于视图函数,这些组合在一起用于构建Web应用程序。

        1. MVC(Model-View-Controller):
          • Model(模型):与MTV中的模型类似,负责应用程序的数据和业务逻辑。它处理数据的存储、检索和处理。
          • View(视图):与MTV中的视图有些不同,它表示应用程序的用户界面,但不直接与模板相关。视图接收用户输入,处理它并与模型进行通信以更新数据。
          • Controller(控制器):控制器是MVC的核心,它负责接收用户输入并根据输入调度视图和模型的操作。它决定如何响应用户的请求,并确保模型和视图之间的协同工作。

        MVC模式通常在框架如Spring、Ruby on Rails等中使用,它的核心思想是分离数据处理、用户界面和控制流,以提高代码的可维护性和可扩展性。控制器充当用户输入的处理中心,调度模型和视图以完成所需的操作。

        总的来说,MTV和MVC都是用于组织和管理应用程序代码的模式,它们有不同的实现方式,但目标都是分离关注点,以提高代码的可读性和可维护性。

        一一对应:

        (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

        Flask如果采用MVC架构的话,项目结构demo:

        (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

        但是推荐在Flask中使用MTV架构模式,而且Flask中主流也是用这个:

        (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

        构建一个基于MTV模式的Demo项目:

        (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

        • login.html:

          
          
              
              用户登录
          
          
          
              
              
              {{msg}}
          
          
          
          
        • account.py:

          from flask import Blueprint, render_template, request, session, redirect
          from uuid import uuid4
          account = Blueprint('account', __name__)
          @account.route('/login', methods=['GET', 'POST'])
          def login():
              if request.method == 'GET':
                  return render_template('login.html')
              user = request.form.get('user')
              pwd = request.form.get('pwd')
              if user == 'GuHanZhe' and pwd == '123':
                  uid = str(uuid4())
                  session['user_info'] = {'id': uid, 'name': user}
                  return redirect('/index')
              else:
                  return render_template('login.html', msg='用户名获或密码错误')
          
        • home.py:

          from flask import Blueprint, session
          home = Blueprint('home', __name__)
          @home.route('/index')
          def index():
              user_info = session.get('user_info')
              print(user_info)
              return 'Index'
          @home.route('/test')
          def test():
              return 'Test'
          
        • __init__.py:

          from flask import Flask
          from .views import account
          from .views import home
          def create_app():
              app = Flask(__name__)
              app.config.from_object('settings.DevelopmentConfig')
              app.register_blueprint(account.account)
              app.register_blueprint(home.home)
              return app
          
        • manage.py:

          from flask_stru import create_app
          app = create_app()
          if __name__ == '__main__':
              app.run()
          
        • settings.py:

          class Config(object):
              DEBUG = True
              SECRET_KEY = 'GuHanZheIsCool'
          class ProductionConfig(Config):
              pass
          class DevelopmentConfig(Config):
              pass
          class TestingConfig(Config):
              pass
          

          运行manage.py文件,访问login登录(登录成功的话):

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          来看下源码,看看默认cookie过期时间是多久:

          from flask.sessions import SecureCookieSessionInterface
          

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          可以看到注释中表示这个值默认是31天~而且我们可以设置这个值:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          而这个参数生效的前提是session.permanent为True(默认就是为True);

          如果设置为False,则关闭浏览器cookie就失效。

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          再看看源码设置cookie的(save_session)上面这一部分:

          如果if成立就直接返回,客户端就不会存cookie了!

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          蹦出一个问题:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          访问index接口是正常的,name修改成功:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          但是如果访问test接口的话,就不正常了:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          很容易想到,这个问题的原因是因为cookie修改后没有保存!

          抛出答案,这是因为修改cookie的话,默认只有当第一层修改才会保存,而第二层、第三层等…都不会保存。

          {
          	user_info: {k1: 1, k2: 2}
          }
          如果是第一层被修改会保存:
          {
          	user_info: {k1: 1, k2: 2},
          	xxx :{'A': 5}
          }
          但是如果是第二层,第三层...修改不会保存:
          {
          	user_info: {k1: 1, k2: 2, k3: 3}
          }
          

          看源码分析这种问题的点源于何处:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          斗胆翻译一下第一个箭头所指变量对应的注释部分:

          “当数据发生变化时,将此标志设置为True。仅跟踪会话字典本身;如果会话包含可变数据(例如嵌套的字典),则在修改该数据时必须手动将此标志设置为True。仅当此标志为True时,会话cookie才会写入响应中。”

          进第一个父类:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          调用on_update函数了就会给modified设为True。

          所以:

          session['user_info'] = 'abc'     # session.__setitem__
          session['user_info'][('name')]
          

          第一行会修改,但是第二行是调用的__getitem__,而源码(上图)没有相关处理,所以不会保存。

          解决方法:

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          再来看下源码(上面没有看完的部分):

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          进去看,因为设置了modified为True,所以这个should_set_cookie就为True,所以这个if就不成立,就继续往下执行。

          不过这部分源码给了我们另一个方法,如果设置SESSION_REFRESH_EACH_REQUEST这个参数为True,每次请求session也都会修改(用这个方法解决):

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

          而且推荐使用这个方法,推荐用这个的原因:

          想象一个使用场景,如果用户登录一个网站,cookie设置的二十分钟失效,如果这个过程中用户一直没有做cookie相关的操作,那么20分钟后登录就失效了,但是如果这二十分钟用户一直在访问这个网站,是不应该让用户失效的。

          如果设置了上述这个参数为True,就不会出现这种问题,

          SESSION_REFRESH_EACH_REQUEST 的行为是在用户请求时将会话标记为“新的”(fresh),以确保它在每个请求之后都会被刷新。这意味着,无论用户是否刷新浏览器或进行其他操作,都会重置会话的过期时间。这是为了确保用户在与应用交互期间会话不会过期(过期时间会从用户刷新的时间重新往后计算)

          Flask中使用的话,要在登录成功后设置一下permanent也为True(但是如果用flask-session就不用了,因为这个默认是为True):

          (二十五)Flask之MTV&MVC架构模式Demo【重点:原生session使用及易错点!】

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]