• flask汇总


    flask框架

    蓝图

    随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理

    Blueprint概念

    • 简单来说,Blueprint 是一个存储操作方法的容器,
      这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,
      Flask 可以通过Blueprint来组织URL以及处理请求。

    在Flask中Blueprint具有的属性

    • Flask使用Blueprint让应用实现模块化
    • 一个应用可以具有多个Blueprint
    • 在一个应用中,一个模块可以注册多次
    • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法
    • 在一个应用初始化时,就应该要注册需要使用的Blueprint
    • 一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

    使用蓝图的步骤

    蓝图/Blueprint对象用起来和一个应用/Flask对象差不多,最大的区别在于一个 蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效

    • 1,创建一个蓝图对象
      admin=Blueprint('admin',name)
      • 蓝图对象中的参数
        • 蓝图的url前缀
          • 当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)
          • 在应用最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可
          • url_for

    url_for('admin.index') # /admin/
    * 注册静态路由
    * 和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。
    * 下面的示例将蓝图所在目录下的static_admin目录设置为静态目录

    admin = Blueprint("admin",name,static_folder='static_admin')
    app.register_blueprint(admin,url_prefix='/admin')
    * 现在就可以使用/admin/static_admin/ 访问static_admin目录下的静态文件了
    * 定制静态目录URL规则 :可以在创建蓝图对象时使用 static_url_path 来改变静态目录的路由。下面的示例将为 static_admin 文件夹的路由设置为 /lib
    * admin = Blueprint("admin",name,static_folder='static_admin',static_url_path='/lib')
    app.register_blueprint(admin,url_prefix='/admin')
    * 设置模版目录
    * 蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录

    admin = Blueprint('admin',name,template_folder='my_templates')
    * 如果在 templates 中存在和 my_templates 同名文件,则系统会优先使用 templates 中的文件

    • 2,在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
      @admin.route('/')
      def admin_home():
      return 'admin_home'
    • 3,在应用对象上注册这个蓝图对象
      app.register_blueprint(admin,url_prefix='/admin')
      • 这里的前缀和蓝图对象的前缀一样的作用
    • 当这个应用启动后,通过/admin/可以访问到蓝图中定义的视图函数

    虚拟环境的

    为甚麽搭建虚拟环境

    在实际开发中,多个程序的运行可以能需要调试各种版本的不同环境,比如不同的python解释器,不同的flask版本
    问题描述:如果直接进行多个版本安装,会导致后安装的内容覆盖先安装的.
    解决办法:使用虚拟环境,不同的程序选择不同的环境,互不影响

    搭建步骤

    • 1.先查看当前电脑中是否有虚拟环境命令
      virtualenv --version

    • 2.[可选]安装虚拟环境的命令:
      sudo pip install virtualenv
      sudo pip install virtualenvwrapper

    • 3.查看是否有mkvirtualenv创建虚拟环境指令
      mkvirtualenv --version

    • 4.[可选]安装完虚拟环境后,如果提示找不到mkvirtualenv命令,须配置环境变量

      • 4.1、创建目录用来存放虚拟环境
        mkdir $HOME/.virtualenvs
      • 4.2、打开~/.bashrc文件,并添加如下:
        export WORKON_HOME=$HOME/.virtualenvs
        source /usr/local/bin/virtualenvwrapper.sh
      • 4.3、运行
        source ~/.bashrc
    • 5.创建虚拟环境的命令 :
      mkvirtualenv 虚拟环境名称(默认python2.x)
      例: mkvirtualenv py_flask

      • mkvirtualenv -p python3 虚拟环境名称(指定python3.x)
        例 :mkvirtualenv -p python3 py3_flask

    如何使用虚拟环境

    • 1.查看虚拟环境的命令 :

      workon 两次tab键 或者 workon 回车

    • 2.使用虚拟环境的命令 :

      workon 虚拟环境名称
      例 :workon py_flask
      例 :workon py3_flask

    • 3.退出虚拟环境的命令 :

      deactivate

    • 4.删除虚拟环境的命令(需要先退出):

      rmvirtualenv 虚拟环境名称

      例 :删除虚拟环境py3_flask

      先退出:deactivate

      再删除:rmvirtualenv py3_flask

    如何在虚拟环境中安装工具包

    • 1.安装flask-0.10.1的包:
      pip install 包名称
      例 : 安装flask-0.10.1的包
      pip install flask==0.10.1
    • 2.查看虚拟环境中安装的包 :
      pip freeze
    • 工具包安装的位置
      • python2版本下:
        ~/.virtualenvs/py_flask/lib/python2.7/site-packages/
      • python3版本下:
        ~/.virtualenvs/py3_flask/lib/python3.5/site-packages

    web应用程序交互流程

    flask

    核心

    • jinja2
      • 模板
    • 拓展包
      • flask_script

        • 命令行方式启动
          python hello.py runserver -host ip地址
        • from flask import Flask

        • 1.从flask_script中导入Manager类

        • from flask_script import Manager

        • app = Flask(name)

        • 2.使用Manager管理app对象

        • manager = Manager(app)

        • @app.route('/')

        • def hello_world():

        • return "helloworld"
          
        • if name == 'main':

        • manager.run()
          
      • flask_wtf

        • 后台CSRF校验机制

        • from flask import Flask,render_template

        • from flask_wtf import CSRFProtect

        • app = Flask(name)

        • 设置SECRET_KEY

        • app.config["SECRET_KEY"] = "fjkdjfkdfjdk"

        • 保护应用程序

        • CSRFProtect(app)

        • @app.route('/')

        • def show_page():

        • return render_template('file01csrf.html')
          
        • @app.route('/add_data',methods=["POST"])

        • def add_data():

        • return "登陆成功"
          
        • if name == 'main':

        • app.run(debug=True)
          
        • 前端隐藏表单

        • {#设置隐藏的csrf_token,使用了CSRFProtect保护app之后,即可使用csrf_token()方法#}
          
        • <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
          
        • <label>用户名:</label> <input type="text" name="username"><br>
          
        • <label>密码:</label> <input type="text" name="username"><br>
          
        • <input type="submit" value="登陆">
          
      • flask_migrate

        • 数据库迁移
      • flask_sqlalchemy

        • flask操作数据库
    • werkzeug
      • 路由模块
        • from werkzeug.route import Converter
          自定义转换器转换器

    最简单的flaskweb

    • 新建文件helloworld.py文件

    1.导入Flask类

    from flask import Flask

    2.创建Flask对象接收一个参数__name__,它会指向程序所在的包

    app = Flask(name)

    3.装饰器的作用是将路由映射到视图函数index

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

    4.Flask应用程序实例的run方法,启动WEB服务器

    if name == 'main':
    app.run()

    加载应用程序的配置方式

    • from_pyfile

      • 从配置文件中添加
        在同级创建配置文件.ini
        配置内容每行一个
      • 从配置文件中加载配置

      • app.config.from_pyfile('config.ini')

    • from_object

      • 从配置对象中添加
      • 配置对象,里面定义需要给 APP 添加的一系列配置

      • class Config(object):

      • DEBUG = True
        
      • 从配置对象中加载配置

      • app.config.from_object(Config)

    • from_enver

      • 从环境变量中添加
      • 加载指定环境变量名称所对应的相关配置

      • app.config.from_envvar('FLASKCONFIG')

    Flask()创建时的参数

    • name

      • 当前程序运行__main__
      • Flask程序所在的包(模块),传 name 就可以

      • 其可以决定 Flask 在访问静态文件时查找的路径

    • static_folder

      • 静态文件存储的文件夹,默认为static
    • static_url_path

      • 静态文件访问路径,默认为 /static
    • template_folder

      • 模板文件存储的文件夹,默认为 template

    app.run()传递的参数

    • host
    • port
    • debug

    路由的定义

    指定请求方式

    • methods

    • @app.route('/demo2', methods=['GET', 'POST'])

    • def demo2():

    • # 直接从请求中取到请求方式并返回
      
    • return request.method
      
    • 配合postman软件开发

    • Postman功能(https://www.getpostman.com/features)

    • 主要用于模拟网络请求包

    • 快速创建请求

    • 回放、管理请求

    • 快速设置网络代理

    指定请求参数

    路由传递参数,整数

    @app.route('/user/int:user_id')
    def user_info(user_id):
    return 'the num is %d' % user_id

    • 不填转换器名称默认是字符串

      • 默认的就是UnicodeConverter, 所以不填转换器名称, 或者使用default、string都是一样的. - 默认将参数当做普通字符串对待. DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
    • 自定义转换器

    • from flask import Flask

    • 导入基类转换器

    • from werkzeug.routing import BaseConverter

    • app = Flask(name)

    • 1.自定义类,继承自BaseConverter

    • class MyRegexConverter(BaseConverter):

    • # 2.编写初始化方法, init方法, 接收两个参数, url_map, regex, 并初始化父类空间和子类空间
      
    • def __init__(self,url_map,regex):
      
    •     super(MyRegexConverter, self).__init__(url_map)
      
    •     self.regex = regex
      
    • 3.将自定义转换器类,添加到默认的转换列表中

    • app.url_map.converters['re'] = MyRegexConverter

    • 使用自定义转换器

    • 接收3位整数

    • @app.route('/<re("d{3}"):num>')

    • def hello_world(num):

    • print("num = %s"%num)
      
    • return "the num is %s"%num
      
    • 接收一个手机号

    • @app.route('/<re("1[345678]d{9}"):mobile>')

    • def get_phone_number(mobile):

    • return "the mobile is %s"%mobile
      
    • if name == 'main':

    • app.run()
      
      • 继承与BaseCoverter

      • 可以直接指定regex值,重写init方法,传递正则规则

      • class BaseConverter(object):

      • """Base class for all converters."""
        
      • regex = '[^/]+'
        
      • weight = 100
        
      • def __init__(self, map):
        
      •     self.map = map
        
      • 也可以不直接指定regex值,在使用自定义转换器的时候指定正则规则

      • class MyConverter(BaseConverter):

      • def __init__(self,url_map,regex):
        
      •     super(MyConverter, self).__init__(url_map)
        
      •     self.regex = regex
        
      • app.url_map.converters['re'] = MyConverter

      • @app.route('/<re("d{3}"):num>')

      • def index(num):

      • return num
        
      • 自定义转换器类中的其他方法

      • class MyConverter(BaseConverter):

      • def __init__(self,url_map,regex):
        
      •     super(MyConverter, self).__init__(url_map)
        
      •     self.regex = regex
        
      • def to_python(self, value):  
        
      •     return value
        
      • def to_url(self, value):  
        
      •     return value
        
      • app.url_map.converters['re'] = MyConverter

        • to_python
          • 匹配成功后被调用,可以对匹配到的参数进行处理,比如转换匹配到的数据的类型,在正则匹配完成之后,调用视图函数之前,可以对数据进行最后的处理
        • to_url
          • 在正则匹配之前调用执行,并传入参数,调用完成之后才开始真正的路由匹配,不关心是否匹配成功,可以通过此函数修正要传入路由中的参数,方便更好的正则匹配

    重定向

    重定向

    @app.route('/demo5')
    def demo5():
    # 使用 url_for 生成指定视图函数所对应的 url
    return redirect(url_for('user_info', user_id=100))

    • redirect('路径')
      • 通过指定路径重定向到函数
    • url_for('函数名',key=value)
      • 通过指定函数,返回路径,可以附带附带请求参数

    返回json

    • 注意contentType

    • contentType: 告诉服务器,我要发什么类型的数据

    • dataType:告诉服务器,我要想什么类型的数据,如果没有指定,那么会自动推断是返回 XML,还是JSON,还是script,还是String。

    • json和dict转换

      • dict->json
        • json = json.dumps(dict)
        • json = jsonity(key=value)
      • json->dict
        • dict = json.loads(json)
    • jsonify

    • 自定义状态码

      • 手动指定(响应体——状态码+响应头)

      • make_response()

      • @app.route('/')

      • def hello_world():

      • # 1.直接返回, 响应体 + 状态码
        
      • # return "hello",666
        
      • # return "hello","666 BIGERROR"
        
      • # 2.手动创建响应体对象
        
      • response = make_response("hello")
        
      • response.status = "888 XIAGAO"
        
      • return response
        
        • status
        • headers

    上下文

    相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

    请求上下文

    保存了客户端和服务器交互的数据

    • request
    • session

    应用上下文

    flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等

    • current_app

    • 应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

    • 应用的启动脚本是哪个文件,启动时指定了哪些参数

    • 加载了哪些配置文件,导入了哪些配置

    • 连了哪个数据库

    • 有哪些public的工具类、常量

    • 应用跑再哪个机器上,IP多少,内存多大

    • current_app.name

    • current_app.test_value='value'

    • g

    • g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别

    • g.name='abc'

    • 注意:不同的请求,会有不同的全局变量

    状态保持

    http是一种无状态协议,浏览器请求服务器是无状态的。
    无状态:指一次用户请求时,浏览器、服务器不知道之前这个用户做过什么,每次请求都是一次新的请求。
    无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。
    有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
    实现状态保持主要有两种方式:
    在客户端存储信息使用Cookie
    在服务器端存储信息使用Session
    二.生活状态举例:

    Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
    Cookie基于域名安全,不同域名的Cookie是不能互相访问的
    应用场景:广告推送;商城结账提取商品信息

    • 获取与设置cookie
    • from flask import Flask, make_response, request

    • app = Flask(name)

    • 设置cookie值

    • @app.route('/set_cookie')

    • def set_cookie():

    • response = make_response("set cookie")
      
    • response.set_cookie("name","zhangsan")
      
    • response.set_cookie("age","13",10) #10秒有效期
      
    • return response
      
    • 获取cookie

    • @app.route('/get_cookie')

    • def get_cookie():

    • #获取cookie,可以根据cookie的内容来推荐商品信息
      
    • # name = request.cookies['haha']
      
    • name = request.cookies.get('name')
      
    • age = request.cookies.get('age')
      
    • return "获取cookie,name is %s, age is %s"%(name,age)
      
    • if name == 'main':

    • app.run(debug=True)
      

    Session

    对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息,所以可以使用session进行保存
    在服务器端进行状态保持的方案就是Session

    • 依赖cookie

    • 获取和设置session

    • from flask import Flask,session

    • app = Flask(name)

    • 设置SECRET_KEY

    • app.config["SECRET_KEY"] = "fhdk^fk#djefkj&&&"

    • 设置session

    • @app.route('/set_session/path:name')

    • def set_session(name):

    • session["name"] = name
      
    • session["age"] = "13"
      
    • return "set session"
      
    • 获取session内容

    • @app.route('/get_session')

    • def get_session():

    • name = session.get('name')
      
    • age = session.get('age')
      
    • return "name is %s, age is %s"%(name,age)
      
    • if name == 'main':

    • app.run(debug=True)
      
    • SECRET_KEY

    请求钩子

    在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

    在请求开始时,建立数据库连接;
    在请求开始时,根据需求进行权限校验;
    在请求结束时,指定数据的交互格式;
    为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

    before_first_request

    在第一次请求之前调用,可以在此方法内部做一些初始化操作
    如:数据库的连接

    before_request

    在每次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验

    如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数

    @app.before_request
    def before_request():
    print("before_request")
    # if 请求不符合条件:
    # return "laowang"

    after_request

    在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理

    @app.after_request
    def after_request(response):
    print("after_request")
    response.headers["Content-Type"] = "application/json"
    return response

    teardown_request

    请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息

    @app.teardown_request
    def teardown_request(e):
    print("teardown_request")

    当前Request对象

    request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)

    args

    • 地址栏数据,问号后面
      www.baidu.com?key=value&key=value

    data

    • json/xml,post提交的请求

    form

    • 表单
    • 记录请求中的cookie信息

    files

    • 文件内容

    headers

    • 记录请求中的报文头

    method

    • 记录请求中使用的HTTP方法

    url

    • 记录请求的url地址

    路由和视图函数在定义和匹配过程中关键类

    BaseConverter

    • 记录匹配规则

    Rule

    • 记录视图函数与路由映射关系

    Map

    • 路由与视图函数关系列表

    MapAdapter

    • 做具体匹配工作

    Flask_script

    属于flask的扩展包,通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过:

    继承脚本扩展,动态运行程序,指定IP,PORT

    python hello.py runserver -host ip地址

    Manager(app)

    from flask import Flask

    1.从flask_script中导入Manager类

    from flask_script import Manager

    app = Flask(name)

    2.使用Manager管理app对象

    manager = Manager(app)

    @app.route('/')
    def hello_world():
    return "helloworld"

    if name == 'main':
    manager.run()

    数据库

    • ORM

    • ORM 全拼Object-Relation Mapping. 称为对象-关系映射

    • 主要实现模型对象到关系数据库数据的映射.

      • 优点
        • 对数据库的操作都转化成对类,属性和方法的操作.
          不用编写各种数据库的sql语句.
          不在关注,使用的是mysql、oracle...等数据库
      • 缺点
        • 相比较直接使用SQL语句操作数据库,有性能损失.
    • Flask-SQLAlchemy

    • SQLALchemy 实际上是对数据库的抽象,让开发者不用直接和 SQL 语句打交道,而是通过 Python 对象来操作数据库,在舍弃一些性能开销的同时,换来的是开发效率的较大提升

    • SQLAlchemy是一个关系型数据库框架,它提供了高层的 ORM 和底层的原生数据库的操作。flask-sqlalchemy 是一个简化了 SQLAlchemy 操作的flask扩展。

      • 安装
        • pip install flask-sqlalchemy
        • 如果连接的是 mysql 数据库,需要安装 mysqldb
          pip install flask-mysqldb
        • 提示:如果flask-mysqldb安装不上,安装, pip install pymysql
      • 数据库连接设置
        • 数据库链接地址
          app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test'
          • 格式:mysql://<用户名>:<密码>@:<端口>/数据库名称
        • 动态追踪修改设置,如未设置只会提示警告
          app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
        • 有时候需要查看映射的sql语句
          • app.config['SQLALCHEMY_ECHO'] = True
        • 配置完成需要去 MySQL 中创建项目所使用的数据库
          $ mysql -uroot -pmysql
          $ create database test charset utf8;
        • 配置信息汇总
          • SQLALCHEMY_BINDS 一个映射 binds 到连接 URI 的字典。更多 binds 的信息见用 Binds 操作多个数据库。
          • SQLALCHEMY_ECHO 如果设置为Ture, SQLAlchemy 会记录所有 发给 stderr 的语句,这对调试有用。(打印sql语句)
          • SQLALCHEMY_RECORD_QUERIES 可以用于显式地禁用或启用查询记录。查询记录 在调试或测试模式自动启用。更多信息见get_debug_queries()。
          • SQLALCHEMY_NATIVE_UNICODE 可以用于显式禁用原生 unicode 支持。当使用 不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上 某些版本的 PostgreSQL )。
          • SQLALCHEMY_POOL_SIZE 数据库连接池的大小。默认是引擎默认值(通常 是 5 )
          • SQLALCHEMY_POOL_TIMEOUT 设定连接池的连接超时时间。默认是 10 。
          • SQLALCHEMY_POOL_RECYCLE 多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定 这个值为 2 小时。
        • 连接其他数据库
          • Postgres:
            postgresql://scott:tiger@localhost/mydatabase
          • MySQL:
            mysql://scott:tiger@localhost/mydatabase
          • Oracle:
    • oracle://scott:tiger@127.0.0.1:1521/sidname
      * SQLite (注意开头的四个斜线):
      sqlite:////absolute/path/to/foo.db
      • 创建sqlalchemy对象
        • 创建SQLAlchemy对象

    db = SQLAlchemy(app)

    * 使用模型类创建数据表
        * 一对多关系
            * 一方
                * 创建模型类(继承自db.Model)
                 * > class Student(db.Model):
                 * >     __tablename__ = "students"
                 * >     id = db.Column(db.Integer,primary_key=True)
                 * >     name = db.Column(db.String(64),unique=True,nullable=False)
                 * > 
                 * >     #关系属性
                 * >     courses = db.relationship("Course",backref="students",secondary=tb_student_course)
    
                    * 默认表名为模型类的名字
    

    __tablename__指定表名
    * class Student(db.Model):
    tablename = "students"
    * 类属性名表示的字段名
    * > 定义列对象
    * > 变量作为字段名
    * > 括号内是字段类型和字段约束

                        *  id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(64),unique=True,nullable=False)
                    * 关系属性
                     * > 关系属性方便查询使用
                     * > 通过 table_name.courses可以访问到‘Course’模型类定义的数据表
                     * > ‘Course’模型类定义的数据表,通过Course.students可以访问到本数据表
    
                        *  courses = db.relationship("Course",backref="students")
                        * lazy='dynamic'
                         * > 第三个参数lazy决定了什么时候SQLALchemy从数据库中加载数据
                         * > 如果设置为子查询方式(subquery),则会在加载完Role对象后,就立即加载与其关联的对象,这样会让总查询数量减少,但如果返回的条目数量很多,就会比较慢
                         * > 设置为 subquery 的话,role.users 返回所有数据列表
                         * > 另外,也可以设置为动态方式(dynamic),这样关联对象会在被使用的时候再进行加载,并且在返回前进行过滤,如果返回的对象数很多,或者未来会变得很多,那最好采用这种方式
    
                        * 关系属性在任何一方设置都行
            * 多方
                * 设置外键
                    * role_id = db.Column(db.Integer,db.ForeignKey(Role.id))
        * 多对多关系
            * 创建一张关联表
    

    关联表数据分别和两张表设置外键
    * tb_student_course = db.Table('tb_student_course',
    db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
    db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
    )
    * 关系属性设置在任何一方都行
    * courses = db.relationship('Course', secondary=tb_student_course,
    backref='student',
    lazy='dynamic')
    * 向数据表中添加数据
    * 在
    if name == main:
    之后
    * 一对多
    * 创建模型类对象
    * stu1 = Student(name='张三')
    * cou1 = Course(name='物理')
    * 将模型类对象添加进会话
    * db.session.add(obj) 添加对象
    db.session.add_all([obj1,obj2,..]) 添加多个对象
    * db.session.delete(obj) 删除对象
    * 提交会话
    * db.session.commit() 提交会话
    * db.session.rollback() 回滚
    * db.session.remove() 移除会话
    * 多对多
    * 创建模型类对象
    * stu1 = Student(name='张三')
    stu2 = Student(name='李四')
    stu3 = Student(name='王五')
    * cou1 = Course(name='物理')
    cou2 = Course(name='化学')
    cou3 = Course(name='生物')
    * 关联表添加关联数据
    * stu1.courses = [cou2, cou3]
    stu2.courses = [cou2]
    stu3.courses = [cou1, cou2, cou3]
    * 将模型类对象添加进会话
    * db.session.add(obj) 添加对象
    db.session.add_all([obj1,obj2,..]) 添加多个对象
    * db.session.delete(obj) 删除对象
    * 提交会话
    * db.session.commit() 提交会话
    * db.session.rollback() 回滚
    * db.session.remove() 移除会话
    * app.run()

    • 数据库的迁移

    • 在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。

    • 为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。

      • 虚拟环境中安装Flask-Migrate
        • pip install flask-migrate
      • 导入相应的包
        • from flask_sqlalchemy import SQLAlchemy
          from flask_migrate import Migrate,MigrateCommand
          from flask_script import Shell,Manager
      • 创建Flask对象
        • app = Flask(name)
      • 使用flask-script的manager接管app对象
        • manager = Manager(app)
      • 设置app连接数据库的配置信息
      • 创建SQLAlchemy对象,读取app中的配置信息
        • db = SQLAlchemy(app)
      • 使用数据库迁移框架
        • migrate = Migrate(app,db)
          第一个参数是Flask实例,第二个参数是sqlalchemy数据库实例
      • 使用Flask_script中的manager动态运行程序
        • manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令

    manager.add_command('db',MigrateCommand)

    数据库迁移

    • 配合flask_migrate
      pip install flask-migrate

    • from flask import Flask

    • from flask_sqlalchemy import SQLAlchemy

    • from flask_migrate import Migrate,MigrateCommand

    • from flask_script import Shell,Manager

    • app = Flask(name)

    • manager = Manager(app)

    • app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test'

    • app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

    • app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

    • db = SQLAlchemy(app)

    • 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例

    • migrate = Migrate(app,db)

    • manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令

    • manager.add_command('db',MigrateCommand)

    • 定义模型Role

    • class Role(db.Model):

    • # 定义表名
      
    • __tablename__ = 'roles'
      
    • # 定义列对象
      
    • id = db.Column(db.Integer, primary_key=True)
      
    • name = db.Column(db.String(64), unique=True)
      
    • user = db.relationship('User', backref='role')
      
    • #repr()方法显示一个可读字符串,
      
    • def __repr__(self):
      
    •     return 'Role:'.format(self.name)
      
    • 定义用户

    • class User(db.Model):

    • __talbe__ = 'users'
      
    • id = db.Column(db.Integer, primary_key=True)
      
    • username = db.Column(db.String(64), unique=True, index=True)
      
    • #设置外键
      
    • role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
      
    • def __repr__(self):
      
    •     return 'User:'.format(self.username)
      
    • if name == 'main':

    • manager.run()
      
      • app = Flask(name)

      • manager = Manager(app)

      • db = SQLAlchemy(app)

      • migrate = Migrate(app,db)

      • manager.add_command('db',MigrateCommand)

      • manager.run()

    • 创建迁移仓库

      • 这个命令会创建migrations文件夹,所有迁移文件都放在里面。

    python database.py db init

    • 创建迁移脚本
      • 自动创建迁移脚本有两个函数
        upgrade():函数把迁移中的改动应用到数据库中。
        downgrade():函数则将改动删除。
        自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。
        对比不一定完全正确,有可能会遗漏一些细节,需要进行检查
        python database.py db migrate -m 'initial migration'
    • 更新数据库
      • python database.py db upgrade
    • 返回以前的版本
      • 可以根据history命令找到版本号,然后传给downgrade命令:

    python app.py db history

    输出格式: -> 版本号 (head), initial migration
    * 回滚到指定版本
    python app.py db downgrade 版本号

    • 实际操作顺序
      • 实际操作顺序:
        1.python 文件 db init
        2.python 文件 db migrate -m"版本名(注释)"
        3.python 文件 db upgrade 然后观察表结构
        4.根据需求修改模型
        5.python 文件 db migrate -m"新版本名(注释)"
        6.python 文件 db upgrade 然后观察表结构
        7.若返回版本,则利用 python 文件 db history查看版本号
        8.python 文件 db downgrade(upgrade) 版本号

    模板

    Jinja2模板

    1.获取各种变量的值

    整数: {{ my_num + 20}}

    字符串: {{ my_str + " python" }}

    元组: {{ my_tuple }}, 分开获取:{{ my_tuple[0] }}, {{ my_tuple[1] }}

    列表: {{ my_list }}, 分开获取:{{ my_list[0] }}, {{ my_list[1] }}

    字典: {{ my_dict }},分开获取:{{ my_dict.name }}, {{ my_dict[age] }}

    2.遍历元祖中所有的元素

    {% for item in my_tuple %}
  • {{ item }}
  • {% endfor %}
    <h2>3.取出列表中所有偶数</h2>
    {% for item in my_list %}
        {% if item %2 == 0 %}
            {{ item }}
        {% endif %}
    {% endfor %}
    
    <h2>4.遍历字典内容</h2>
    {% for key in my_dict %}
        {# 如果直接是mydict.key ,那么这个key是一个字符串, 如果是 mydict[key], 那么key当成变量 #}
        <li>{{ key }} = {{ my_dict[key] }}</li>
    {% endfor %}
    
    • Jinja2模板概述

    • 用来展示数据的html页面,这个过程也通常称为渲染,属于Jinja2的功能 使用模板的好处:

    • 视图函数只负责业务逻辑和数据处理(业务逻辑方面)

    • 而模板则取到视图函数的数据结果进行展示(视图展示方面)

    • 代码结构清晰,耦合度低

    • Jinja2特点

    • Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。

    • 模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。

    • 使用render_template函数封装模板引擎

      • python代码实现
      • 仿照django实现模板语言
      • 过滤器支持链式调用{{
        str|lower|reverse }}
      • 直接在模板中支持运算
    • Jinja2模板语法

      • 获取变量值
        • 整数:{ {number} }

      元祖:{ {tuple[0]} }

      列表:{ { list[0] } }

      字典:{ { dict['key'] } }

      * 分支语句 * { % if 条件 % } 语句1

    { % else % }
    语句2
    { % endif % }
    * for循环
    * {% for 变量 in 容器 %}
    语句
    {% endfor%}
    * for循环中可以访问i的特殊变量
    * loop.index 当前循环迭代的次数(从 1 开始)
    * loop.index0 当前循环迭代的次数(从 0 开始)
    * loop.revindex 到循环结束需要迭代的次数(从 1 开始)
    * loop.revindex0 到循环结束需要迭代的次数(从 0 开始)
    * loop.first 如果是第一次迭代,为 True 。
    * loop.last 如果是最后一次迭代,为 True 。
    * loop.length 序列中的项目数。
    * loop.cycle 在一串序列间期取值的辅助函数。见下面示例程序。
    * 注释
    * {# 注释内容 #}

    • Jinja2模板过滤器
      • Jinja2模板自带的两种过滤器

      • 过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。

        • 字符串过滤器
    • 使用格式:{{ 字符串 | 字符串过滤器 }}
      * safe:禁用转义

    {{ 'hello' | safe }}

    * capitalize:把变量值的首字母转成大写,其余字母转小写

    {{ 'hello' | capitalize }}

    * lower:把值转成小写

    {{ 'HELLO' | lower }}

    * upper:把值转成大写

    {{ 'hello' | upper }}

    * title:把值中的每个单词的首字母都转成大写

    {{ 'hello' | title }}

    * reverse:字符串反转

    {{ 'olleh' | reverse }}

    * format:格式化输出

    {{ '%s is %d' | format('name',17) }}

    * striptags:渲染之前把值中所有的HTML标签都删掉

    {{ 'hello' | striptags }}

    * 列表过滤器 * 使用格式:{{ 列表 | 列表过滤器 }} * first:取第一个元素

    {{ [1,2,3,4,5,6] | first }}

    * last:取最后一个元素

    {{ [1,2,3,4,5,6] | last }}

    * length:获取列表长度

    {{ [1,2,3,4,5,6] | length }}

    * sum:列表求和

    {{ [1,2,3,4,5,6] | sum }}

    * sort:列表排序

    {{ [6,2,3,1,5,4] | sort }}

    * 使用过滤器的其他操作语句 * 语句块操作 {% filter upper %} #一大堆文字# {% endfilter %} * 链式调用 {{ "hello world" | reverse | upper }} * Jinja2自定义过滤器的两种方式 * 先定义函数(一个形参接受原值) * 添加到过滤器列表,app.add_template_filter('函数名',‘过滤器名称’) * > def do_listreverse(li): * > # 通过原列表创建一个新列表 * > temp_li = list(li) * > # 将新列表进行返转 * > temp_li.reverse() * > return temp_li * > * > app.add_template_filter(do_listreverse,'lireverse')
        * 定义函数,直接使用过滤器列表装饰@app.teemplate_filter('过滤器名称')
         * > @app.template_filter('lireverse')
         * > def do_listreverse(li):
         * >   # 通过原列表创建一个新列表
         * >   temp_li = list(li)
         * >   # 将新列表进行返转
         * >   temp_li.reverse()
         * >   return temp_li
    
    • 模板代码的复用

    • 在模板中,可能会遇到以下情况:

    • 多个模板具有完全相同的顶部和底部内容

    • 多个模板中具有相同的模板代码内容,但是内容中部分值不一样

    • 多个模板中具有完全相同的 html 代码块内容

    • 像遇到这种情况,可以使用 JinJa2 模板中的 宏、继承、包含来进行实现

      • 宏是Jinja2中的函数,调用后,直接返回一个模板,或者字符串

      • 当模板中出现大量重复功能代码的时候,可以使用宏来进行封装

        • 定义宏
          可以在当前文件,也可以在其他文件

        • {% macro input(name,value='',type='text') %}

        • <input type="{{type}}" name="{{name}}" value="{{value}}">
          
        • {% endmacro %}

          • 使用当前文件的宏

          • {{ input('name' value='zs')}}

          • 使用其他文件的宏

          • {%import 'filename.html' as 别名%}

          • {%别名.函数名(参数)%}
            
      • 继承

      • 将公共的内容抽取到父类模板,共子类使用的形式称为继承.

      • 一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。

        • 父模板

        • 父模板中使用多个block组成

          • {% block top %}
            顶部菜单
            {% endblock top %}

    {% block content %}
    正文内容
    {% endblock content %}

    {% block bottom %}
    底部
    {% endblock bottom %}
    * 子模版
    * 继承后,子类完全拥有父类内容,并且子类可以进行重写,如果写保留父类内容使用: super()

            * {% extends 'base.html' %}
    

    {% block content %}
    需要填充的内容
    {% endblock content %}
    * 模板继承的注意点
    * 不支持多继承
    为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
    不能在一个模板文件中定义多个相同名字的block标签。
    定义block模板的时候,一定要加上endblock结束标记
    * 包含
    * > Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。

        * 格式:
    

    {% include 'hello.html' %}
    或者
    {% include 'hello.html' ignore missing %}
    提示: ignore missing 加上后如果文件不存在,不会报错
    * 总结
    * 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
    继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
    宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
    包含(include)是直接将目标模板文件整个渲染出来。

    • 模板特有的变量和函数

    • 你可以在自己的模板中访问一些 Flask 默认内置的函数和对象

      • config
        • 你可以从模板中直接访问Flask当前的config对象:

    {{config.DEBUG}}
    输出:True
    * request
    * 就是flask中代表当前请求的request对象:

    {{request.url}}
    输出:http://127.0.0.1
    * g变量
    * 在视图函数中设置g变量的 name 属性的值,然后在模板中直接可以取出

    {{ g.name }}
    * url_for()
    * url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接:

    {{url_for('home')}}
    * 如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中:

    {{ url_for('post', post_id=1)}}
    /post/1
    * get_flashed_messages()
    * 这个函数会返回之前在flask中通过flask()传入的消息的列表,flash函数的作用很简单,可以把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:

    {%for message in get_flashed_messages()%}
    {{message}}
    {%endfor%}

    • Flask_WTF表单
      • 使用代码定义表单
      • 在模板中进行表单渲染
      • 提供一系列的验证器,对表单提交的数据进行验证
      • 流程
        • 定义类继承子FlaskForm
        • 在类中编写字段内容,验证函数
        • 创建,渲染到页面
    • csrf
      • flask_wtf模块提供了csrf攻击的保护
      • 使用流程:

    from flask_wtf.csrf import CSRFProtect
    CSRFProtect(app)
    * CSRFProtect(app)保护原理:
    * > 对应用程序app中的post,put,dispatch,delete, 4种类型的请求做保护,因为这些类型的请求是用于更改服务器的资源
    * > 当以上面4种类型的请求,操作服务器资源的时候,会校验cookie中的csrf_token, 表单中的csrf_token信息
    * > 只有上面二者的值相等的时候,那么校验则通过,可以操作服务器资源

    * csrf_token值的生成需要加密, 所以设置SECRET_KEY
    * 前后端代码
        * 后端代码
         * > from flask_wtf import CSRFProtect
         * > app.config["SECRET_KEY"] = "fjkdjfkdfjdk"
         * > CSRFProtect(app)
    
        * 前端代码
         * > {#设置隐藏的csrf_token,使用了CSRFProtect保护app之后,即可使用csrf_token()方法#}
         * >     <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
    

    render_template()函数

    from flask import Flask,render_template
    app = Flask(name) #默认省略了三个参数,static_url_path, static_folder, template_folders

    @app.route('/')
    def hello_world():
    #定义数据,整数,字符串,元祖,列表,字典
    num = 10
    str = "hello"
    tuple = (1,2,3,4)
    list = [5,6,7,8]
    dict = {
    "name":"张三",
    "age":13
    }

    return render_template('file01.html',my_num=num,my_str=str,my_tuple=tuple,my_list=list,my_dict=dict)
    

    if name == 'main':
    app.run(debug=True)

    • 将数据传递给模板
      return render_template('模板文件名',key=value)

    XMind: ZEN - Trial Version

  • 相关阅读:
    P2704 [NOI2001]炮兵阵地[状压dp]
    【CRT】中国剩余定理简介
    乘法逆元
    P2921 [USACO08DEC]在农场万圣节[SCC缩点]
    P1967 货车运输[生成树+LCA]
    P2746 P2812 [USACO5.3]校园网Network of Schools[SCC缩点]
    Debian初始化配置
    Linux网桥配置
    Jenkins与Gitlab集成
    Gitlab搭建
  • 原文地址:https://www.cnblogs.com/serpent/p/9614628.html
Copyright © 2020-2023  润新知