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是不能互相访问的

应用场景:广告推送;商城结账提取商品信息

  • 获取与设置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

  • 记录请求中的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

    flask汇总的更多相关文章

    1. Flask技术问题汇总

      1:Flask 使用 request对象代理了当前请求的上下文.这么做什么好处和坏处? 好处:flask封装了C端发起request对象,这样就可以使用上下文临时把某些对象变为全局可访问:如果不封装, ...

    2. 2013流行Python项目汇总

      2013流行Python项目汇总 转自:http://www.kankanews.com/ICkengine/archives/102963.shtml Python作为程序员的宠儿,越来越得到人们的 ...

    3. 2013年最好的Python开源项目汇总

      2013年Python社区诞生了很多实用的开发工具,这些工具 在一定程度上 可以帮助你节省更多的时间.本文为你汇总了这些工具,它们大部分都是开源的,你还可以通过源码来学习更多的Python开发知识. ...

    4. 流行的Python项目汇总

      年有哪些流行的Python项目呢?下面,我们一起来看下. 一.测试和调试 python_koans :Python Koans 算 “Ruby Koans” 的一部分,作为交互式教程,可以学习 TDD ...

    5. Python资源汇总

      Python 目录: 管理面板 算法和设计模式 反垃圾邮件 资产管理 音频 验证 构建工具 缓存 ChatOps工具 CMS 代码分析和Linter 命令行工具 兼容性 计算机视觉 并发和并行性 组态 ...

    6. Python框架学习之Flask中的常用扩展包

      Flask框架是一个扩展性非常强的框架,所以导致它有非常多的扩展包.这些扩展包的功能都很强大.本节主要汇总一些常用的扩展包. 一. Flask-Script pip install flask-scr ...

    7. nginx+supervisor+gunicorn+flask

      一. 更新系统 #yum -y install epel-release #yum clean all && yum makecache #yum -y update 二.安装pyth ...

    8. Flask的Context(上下文)学习笔记

      上下文是一种属性的有序序列,为驻留在环境内的对象定义环境.在对象的激活过程中创建上下文,对象被配置为要求某些自动服务,如同步.事务.实时激活.安全性等等. 比如在计算机中,相对于进程而言,上下文就是进 ...

    9. Python学习笔记第二十三周(Flask架构)

      目录: 一.变量引用 内容: 备注:PyCharm小技巧,comm+alt+l  自动修改格式,comm+alt+return  向上添加新行 一.变量引用 1.url生成 from flask im ...

    随机推荐

    1. delphi dll编写与调用

      dll代码: mydll.dpr library mydll; uses System.SysUtils, System.Classes, uFunction in 'uFunction.pas'; ...

    2. Egret Engine 2D - 显示容器

        DisplayObjectContainer 所有容器的父类 1 添加 删除 子对象 2 访问子对象 3 检测子对象 4 设置叠放次序 Sprite 继承自DisplayObjectContain ...

    3. 自学Java第五章——《面向对象的基本特征》

      面向对象的基本特征: 1.封装 2.继承 3.多态 6.1 封装 1.好处: (1)隐藏实现细节,方便使用者使用 (2)安全,可以控制可见范围 2.如何实现封装? 通过权限修饰符 面试题:请按照可见范 ...

    4. Linux - 安装 dotnet core 环境

      Linux -  安装 dotnet core 环境 系统环境:CentOS7 官方安装指导 https://www.microsoft.com/net/learn/get-started/linux ...

    5. 吴裕雄--天生自然 PHP开发学习:echo 和 print 语句

      <?php echo "<h2>PHP 很有趣!</h2>"; echo "Hello world!<br>"; ec ...

    6. Day 10:浅谈正则表达式

      正则表达式 以检验扣扣号是否合法为例引入正则表达式 要求:校验QQ号,要求:必须是5~15位数字,0不能开头. 1.没有正则表达式 public class Demo1 { public static ...

    7. JavaEE--JNDI(下,实现)

      参考:https://blog.csdn.net/ouyida3/article/details/46699023  https://www.landui.com/help/show-6158.htm ...

    8. Chrome在新版MacOS上报错 NET::ERR_CERT_WEAK_KEY 解决方法

      现象 原文链接 证书详情: 原因 参考苹果官网给出的提示(https://support.apple.com/en-us/HT210176): RSA秘钥长度必须>=2048,小于这个长度的将不 ...

    9. Linux(CENTOS7) NodeJs安装

      1.下载NodeJs 官网下载地址:http://nodejs.cn/download/ ​ 2.上传到linux系统 我这里上传到/disk/nodejs目录下面的,上传工具使用的xftp. ​ 3 ...

    10. xlua 原理

      基于版本 104 可以直接在lua访问c#函数原理: CS 是一个table,设置了一个__index函数,如果访问不存在的成员的时候,会走__index函数,调用import_type从C#中找到具 ...