Bootstrap

Flask框架(下)

Flask框架(下)

 

 

一、Flask-Script扩展命令使用

安装

    pip install Flask-Script

导入Manager

    from flask_script import Manager

创建Manager对象

    manager = Manger(app)

使用manager对象的run()方法(而不使用app.run())

    manager.run()

接下来就可以像django的manage.py一样,使用命令来操作

1、查看有哪些命令

        python xxx.py --help

2、常用命令

启动服务器

        python xx.py runserver

进入该环境(模块)下命令模式

        python xx.py shell

注:flask2.0版本会报错

处理方法:在flask_script/__init__.py文件里面把from ._compat import text_type 改成 from flask_script._compat import text_type 

二、模板(Jinja2模板) 

1、Flask模板渲染函数

from flask import render_template

  • render_template("模板文件",参数1= 值1, 参数2 = 值2, ...)方法
  • return render_template("index.html", name = "fy", age = 18)

data = {参数1:值1, 参数2:值2, ...}

#通过拆包的方式也能使用字典

render_template("模板文件",**data)方法

2、模板变量的基本使用(与django模板变量使用类似) 

 3、过滤器

一、使用语法
 {{模板变量或字符串 | 过滤器}}
 {{模板变量或字符串 | 过滤器1 | 过滤器2 |...}}
从左到右执行过滤器

二、字符串过滤器

safe:禁用转义

        {{"<em>hello</em>" | safe}}

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

lower:把值转换成小写

upper:把值转换成大写

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

trim:把值的首尾空格去掉

reverse:字符串反转

format:格式化输出

        <p>{{"<%s is %d> | format("name", 17)"}}

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

三、列表过滤器

first:取第一个元素

last:取最后一个元素

length:获取列表长度

sum:列表求和

sort:列表排序

四、自定义过滤器

方式一:

通过app.add_template_filter(过滤器函数, 模板中使用的过滤器名字)方法

例:

def filter_double_sort(ls):

    return ls[::2]

app.add_template_filter(filter_double_sort, "double_2")

方式二:

通过装饰器app.template_filter(模板中使用的装饰器名字)

例:

@app.template_filter("double_3")

def filter_double_sort(ls):

    return ls[::-3]

过滤器原理:模板中把参数值传递给过滤器函数,函数的返回值就是过滤后该参数的值

4、Flask-WTF表单扩展 

帮助进行csrf验证,帮助我们快速定义表单模板,而且帮助我们在视图中验证表单数据

安装:pip install Flask-WTF

使用步骤:

1、通过类抽象出表单

from flask_wtf import FlaskForm
#定义表单模型类
class RegisterForm(FlaskForm):
    """自定义注册表单模型类"""
    pass

2、类属性定义表单的字段

from flask_wtf import FlaskForm
from wtfroms import StringField, PasswordField, SubmitField #导入表单字段-flask_wtf依赖包
from wtforms.validators import DataRequird, EqualTo #导入验证器-flask_wtf依赖包
#定义表单模型类
class RegisterForm(FlaskForm):
	"""自定义注册表单模型类"""
	# label说明标签   validators验证器,可以有多个。
	user_name = StringField(label="用户名", validators=[DataRequired("用户名不能为空")])
	password = PasswordField(label="用户名", validators=[DataRequired("密码不能为空")])
	rpassword = PasswordField(label="用户名", validators=[DataRequired("确认密码不能为空")],EqualTo("password", "两次密码不一致"))
	submit = SubmitField(label = "提交")

wtf支持的表单字段

 wtf常用验证函数

验证失败提示错误,在模板中使用对应字段errors属性 

3、定义视图函数,创建表单对象

@app.route("/register", methods=["GET", "POST"])
def register():
	#创建表单对象,如果是post请求,前端发送的数据,flask会把数据在构造from对象的时候,存放到对象中
	form = RegisterForm()
	#validate_on_submit()判断数据是否合理,如果from中的数据完全满足验证器返回真,否则返回假
	if form.validate_on_submit():
		#表示验证合格
		#提取数据
		uname = form.user_name.data
		pwd = form.user_name.data
		print("账号:%s\n密码:%s"%(uname, pwd))
		return "注册成功"
	return render_template("register.html", form=form)

4、创建表单模板

<form method="post">
	{{form.csrf_token}}
	{{form.user_name.label}}
	<p>{{form.user_name}}
	{%for error in form.user_name.errors%}
		<p>{{error}}</p>
	{%endfor%}

	{{form.password.label}}
	<p>{{form.password}}
	{%for error in form.user_name.errors%}
		<p>{{error}}</p>
	{%endfor%}

	{{form.rpassword.label}}
	<p>{{form.rpassword}}
	{%for error in form.user_name.errors%}
		<p>{{error}}</p>
	{%endfor%}

	{{form.submit}}
</form>

注:

防止csrf攻击,需要添加{{form.csrf_token}}

防止CSRF攻击:配置SECRET_KEY

        app.config["SECRET_KEY"] = "adadsdf12e5trtytyr"

5、控制/循环语句

和django里面类似
{%for item in samples%}{%endfor%}
{%if %}{%endif%}

6、宏的定义与使用

什么是宏?:相当于函数,提高代码复用

1、不带参数宏

定义

{%macro 宏名()%}

    ...

{%endmacro%}

{%macro input()%}
    <input type="text", name="username", value="", size="30">
{%endmacro%}

使用

 {{ 宏名() }}

 {{input()}}

2、带参数的宏

定义

{%macro 宏名(参数1,参数2,...)%}

    ...

{%endmacro%}

{%macro input(type, value, size)%}
    <input type="{{type}}", name="{{value}}", value="", size="{{size}}">
{%endmacro%}

带默认的值的宏

{%macro input(type="text", value="", size="30")%}

    <input type="{{type}}", name="{{value}}", value="", size="{{size}}">

{%endmacro%}

使用

{{ 宏名(参数1,参数2,...) }}

{{input("password", "", "50")}}

3、宏定义在外部使用
(1)、创建一个HTML文件,结构可以全删掉,只写宏
        xx.html
(2)、模板文件中导入xx.html
        {%import "xx.html" as func%}
(3)、调用(使用)宏
        {{func.input()}}

7、模板的继承 

1、块定义
{%block 块名%}
    块
{%endblock 块名%}
2、继承
{%extends "父模板文件名"%}

例:
父模板:base.html

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

子模板:child.html

{%extends "base.html"%}
{%block top%}
    需要填充内容
{%endblock top%}

注:
        
不支持多继承
        便于阅读extends尽量写在第一行
        不能在一个模板文件中定义多个相同名字的block标签
        页面中使用多个block标签时,建议给结束标签起名字,便于阅读

8、包含

作用:提高代码重用,将另一个模板整个加载到当前模板中,并直接渲染
使用:{\%include "xx.html"%}
模板文件不存在会抛出异常,加上ignore missing关键字,如果模板文件不存在,会忽略这条include语句
例:{\%include "xx.html" ignore missing%}

9、Flask中特殊变量和方法

 实现闪现信息(第一次进入可见)

 三、数据库操作

SQLAlchemy是一个关系型数据库框架,flask-sqlalchemy是一个简化了SQLAlchenmy操作的flask扩展(与django操作模型类类似)

数据库操作示意图:

 

安装:
        pip  install flask-sqlalchemy
需要连接数据库,还需要安装
        pip install pymysql
启动服务器前执行
        pymysql.install_as_mysqldb()

Flask数据库配置 :

SQLALCHEMY_DATABASE_URI:配置连接数据库相关参数
    SQLALCHEMY_DATABASE_URI = "mysql://root:[email protected]:3306/flask_db"
SQLALCHEMY_COMMIT_ON_TEARDOWN:每次请求结束自动提交数据库中改动,一般不使用自动提交这个参数,默认不开启
SQLALCHEMY_TRACK_MODIFICATIONS:如果数据库表数据修改了那么模型类数据也应该修改,开启后,自动跟踪,同步修改。这个参数需要开启
SQLALCHEMY_ECHO:查询时会显示SQL语句,用于调式使用

SQLAlchemy工具使用 :
一、导入SQLAlchemy
    
from flask_sqlalchemy import SQLAlchemy

二、创建SQLalchemy工具对象
    db = SQLAlchemy(app)

三、创建模型类
        表名命令规范
        数据库名缩写_表名:#ihome -> ih_user
        tbl_表名:#tbl_user

class Role(db.Model):
    """用户角色/身份表"""
    __tablename__ = "tbl_roles" #指明数据库表名
    id = db.Column(db.Integer, primary_key = True)
    name_id = db.Column(db.String,unique=True)
    #数据库中不存在的字段,用于查询关联的数据(模型类层面考虑),backref作用:User模型类里面,通过role属性可以拿到关联的模型类
    users = db.relationship("User", backref="role")

class User(db.Model):
    """用户表"""
    __tablename__ = "tbl_users" #指明数据库表名
    #定义数据表字段及字段类型约束
    id = db.Column(db.Integer, primary_key = True)#定义整型的主键,会默认设置为自增主键
    name = db.Column(db.string(64), unique=True)
    email = db.Column(db.string(128), unique=True)
    password = db.Column(db.string(128))
    role_id = db.Column(db.Integer, db.ForeignKey("tbl_roles.id") )#注:与关联字段的类型保持一致(数据库底层考虑)

模型类实现__repr__(self)方法 :改变print函数输出,打印时使这个对象看起来更加方便

四、创建数据表

通过db对象(db = SQLAlchemy(app))

if __name__ == "__main__":

#清除数据库里面所有数据(谨慎使用,一般第一次执行时使用,清除一些垃圾数据)

db.drop_all()

#创建所有数据表

db.create_all()

五、数据的增删改查

1、添加数据
添加一条

#创建对象
role1 = Role(name="admin")
#session记录对象任务
db.session.add(role1)
#提交任务到数据库中
db.session.commit() 

添加多条

#创建对象
role1 = Role(name="admin")
user1 = User(name="枫叶1", email="[email protected]", password="123456", role_id =role1.id )
user1 = User(name="枫叶2", email="[email protected]", password="123456", role_id =role1.id )
user1 = User(name="枫叶3", email="[email protected]", password="123456", role_id =role1.id )
#session记录对象任务
db.session.add_all([user1, user2, user3])
#提交任务到数据库中
db.session.commit() 

2、查询数据
(1)取所有数据

#类名.query.all(),返回所有数据对象的列表
li = Role.query.all()
r = li[0] #取第一条数据对象
print(r.name)

(2)取第一条数据

#类名.query.first()
Role.query.first
#类名.query.get(主键的值)
Role.query.get(2)

(3)过滤器
过滤器使用:类名.query.过滤器(过滤条件).all()
filter():

#把过滤器添加到原查询上,返回一个新的查询
User.query.filter(User.name=="wang", User.role_id==1).first()
#模糊查询
#查询以163.com结尾的数据
User.query.filter(User.email.endwith(163.com)).all()

filter_by():
把等值过滤器添加到原查询上,返回一个新查询

User.query.filter_by(name="wang", role_id=1).first()

limit():

使用指定值限定原查询返回的结果

#取查询结果的前两条
User.query.offset(1).limit(2).all()

offset():

偏移原查询返回的结果(相当于跳过前几条数据),返回一个新查询             

#从第三个数据开始查询(包括第三)
li = User.query.offset(2).all()

order_by():

根据指定条件对原查询结果进行排序,返回一个新查询

#把查询结果倒序排序
    #方式一
User.query.order_by(User.id.desc()).all()#推荐使用
    #方式二
User.query.order_by("-id").all()
#把查询结果正序排序
User.query.order_by(User.id.asc()).all()#推荐使用

group_by():
根据指定条件对原查询结果进行分组,返回一个新查询
需要使用db.session.query查询

#把User表根据role_id进行分组
#分组后的数据 User.role_id 和统计分组个数 func.count(User.role_id)
from sqlalchemy import func
db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all()
#返回结果列表元组, [(1, 2), (2, 2)]

or_()函数:

from sqlalchemy import or_
#各条件之间或的关系
类名.query.过滤器(or_(条件1, 条件2,...)).all()       

关联查询:使用db.relationship()这个定义的类属性

ro = Role.query.get(1)
user_liset = ro.users  #获取Role对象关联的User对象,一个列表
user = User.query.get(1)
role = user.role #获取User对象关联的Role对象

3、修改数据

方式一:
(1)获取到数据对象
        user = User.query.get(1)
(2)修改对象
        user.name = "风信子"
(3)重新保存该对象
        db.session.add(user)
        db.session.commit() 

方式二:
查询集的updata()方法,接收一个字典{"字段名":"值",...}

User.query.filter_by(name = "枫叶").update("name":"风信子", "email":"[email protected]")
db.session.commit() 

4、删除数据
db.session的delete()方法

user = User.query.get(1)
db.session.delete(user)
db.session.commit()

数据库migrate扩展命令简介:

作用:向Django一样使用命令的形式实现数据迁移

使用步骤:
(1)安装flask-migrate模块
        pip install flask-migrate
        注:依赖flask-script包
(2)导入
        from flask_migrate import Migrate, MigrateCommand
        from flask_script import Shell,Manager
(3)创建migrate对象
        manager = Manager(app)
        db = SQLAlchemy(app)
        migrate = Migrate(app, db) #可以不用变量接受
        manager.add_command("db", MigrateCommand)

命令使用:
创建迁移仓库
        python xx.py db init
        这个命令会创建migrations文件夹,所有迁移文件都放在里面
创建迁移脚本
        python xx.py db migrate -m "initial migration"
更新数据库
        python xx.py db upgrade
查看历史版本的状态码
        python xx.py db history
        每次更新数据库都有个状态码(随机字符串),可以用于回退
回退到某个历史版本
        python xx.py db downgrade 状态码

四、Flask-Mail 

Flask扩展包,用于在Flask程序中发送邮件

使用步骤:
(1)安装flask-mail模块

        pip install flask-mail

(2)导入Mail, Message
        form flask_mail import Mail, Message
(3)配置应用
        app.config.update(
            MAIL_SERVER = "smtp.qq.com",
            MAIL_PORT = 456,
            MAIL_USE_TLS = True,
            MAIL_USERNAME = "[email protected]",
            MAIL_PASSWORD = "153856fsw",
                )
注:密码可能不是真实的密码,而是授权码

(4)创建mail对象

        mail = Mail(app)

(5)视图函数中发送邮件

        #sender发送方, recipients接收方列表
        msg = Message("this is a test", sender="[email protected]", recipients=[])
        #邮件内容
        msg.body = "Flask test mail"
        #发送邮件
        mail.send(msg)

五、功能模块的划分 

(1)不使用蓝图

(1)使用循环导入:A模块导入B,B模块导入A
        模块开头采用form xx import xx形式导入会锁死,报错;

        解决办法,一方让步。在函数里面导入
(2)使用装饰器:app.route("/xx")(func)

例:

#goods.py

def g_func():
    from main import request
    print(request.url)
    return " goods index page"
#main.py

from flask import Flask,request
from goods import g_func
app = Flask(__name__)
app.route("/goods_index")(g_func)
if __name__ == "__main__":
    print(app.url_map)
    app.run()

(2)使用蓝图Blueprint

蓝图使用步骤

1、创建蓝图对象

        form flask import Blueprint

        #Blueprint("蓝图名称",  蓝图所在模块)

        admin = Blueprint("admin", __name__)

2、使用蓝图对象路由

        @admin.route("/")

        def admin_index():

                return "admin_index_page"

3、程序实例中注册蓝图

        #register_blueprint(蓝图对象, url_fix="访问蓝图路由前缀")

        app.register_blueprint(admin, url_fix="/admin")

例:

#good.py

from flask import Blueprint

goods = Blueprint("goods", __name__)

@goods.route("/index")
def goods_index():
    return "blueprint goods index page"
#main.py

from flask import Flask, Blueprint
from goods import goods


app = Flask(__name__)
app.register_blueprint(goods, url_prefix="/goods")


if __name__ == "__main__":
    print(app.url_map)
    app.run()

(3)以目录形式定义蓝图 

作用:实现单个应用的视图、模板、静态文件的集合

定义步骤
1、创建一个单个应用的包
包结构
        --static文件夹
        --templates文件夹
        -- __init__.py

(1)可以把包理解成一个类,__init__.py文件就是初始化方法,导入包后就可以导入__init__.py文件里面初始化出的对象
(2)__init__.py文件内容
#在文件夹里面的__init__.py文件里面定义蓝图
        from flask import Blueprint
#创建一个蓝图static_folder-指定蓝图静态文件目录,templates_folder-指定静态文件目录
#__main__作用:指定模块所在目录为根目录,蓝图没有默认静态文件目录,静态文件目录
        app_cart = Blueprint("app_cart", __name__, static_folder="static",         template_folder="templates")
#在__init__.py文件被执行的时候,把视图加载进来,让蓝图与应用程序知道有视图的存在
        from .views import get_cart
         --views.py
                from . import app_cart
                @app_cart.route("/get_cart")
                def get_cart():
                        return "get cart page"

2、程序实例中注册蓝图
        form cart import app_cart
        #register_blueprint(蓝图对象, url_fix="访问蓝图路由前缀")
        app.register_blueprint(app_cart, url_fix="/cart")
注:模板文件查找顺序,app对象模板文件目录  》 蓝图对应模板文件目录

六、单元测试 

概念:记录测试的过程,把测试的逻辑写成一段代码,便于修改代码后测试是否修改正确

断言:assert的使用
后面接一个表达式,如果表达式返回真,则断言成功,程序能够继续往下执行,如果表达式为假,则断言失败,抛出一个异常AssertionError,终止程序继续往下执行

例:断言一个数是整数
assert isinstance(num, int)

单元测试的使用:

(1)创建一个.py文件一般命名为test用于写测试代码

(2)写测试类,继承unittest.TestCase

import unittest
class TestClass(unittest.TestCase):
#该方法会首先执行,相当于测试前的准备工作
    def setUp(self):
        pass
#该方法会测试代码完成后执行,相当于测试后的扫尾工作
    def tearDown(self):
        pass
#测试代码
    def test_xx():
        pass

unittest.TestCase类中自带测试的方法(不必使用断言) 

注:写测试代码的方法,必须以test_开头,运行后python会自动帮我们执行测试代码

七、部署 

概念区分:
    WSGI

        是一种规范,web服务器和web应用程序之间的接口
    uwsgi
        是一种传输协议,用于定义传输信息的类型
    uWSGI
        实现了uwsgi协议WSGI服务器

部署方式:
        nginx + gunicorn + flask
        采用Gunicorn做wsgi容器,来部署flask程序
    图示:

        

;