zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Representation Data in OpenCascade BRep
    Render OpenCascade Geometry Surfaces in OpenSceneGraph
    Render OpenCascade Geometry Curves in OpenSceneGraph
    OpenCascade Shape Representation in OpenSceneGraph
    Geometry Surface of OpenCascade BRep
    Geometry Curve of OpenCascade BRep
    Tyvj2017清北冬令营入学测试
    Spfa算法模板
    洛谷1016 旅行家的预算
    洛谷1290 欧几里得的游戏
  • 原文地址:https://www.cnblogs.com/serpent/p/9614628.html
Copyright © 2011-2022 走看看