Flask框架(三)

一 flask_wtf与flask_bootstrap

  1. flask_wtf表单处理
    上一篇博客中提出flask_wtf是用来处理表单数据的,而其中
    表单类型(StringField、 PasswordField、 SubmitField、 FileField 等)
    用来验证的表单有(DataRequired、Length、 Email、 Regexp、 EqualTo),这些在上一篇博客中有详细的解释说明,但是只有一个简单的应用,这里会具体的讲解应用
    在创建一个新的项目的时候,通常会有一些行业内通俗的模块与包的名字,比如:static(放置静态文件处:css样式,js样式等)、templatems(放置html文件处)等
    而整个项目中除了主函数(run.py)外,还会有一些其他文件,forms.py 就是放置 flask_wtf 表单处理的文件,关于表单处理的要求,会被写在这个文件下,当然还会有其它的文件,下面讲到了,会提及的。

首先,要做的就是安装 flask_wtf 这个库

pip install flask-WTF

注意:这里在安装的时候,看好库名,是 -WTF ,并不是 _wtf ,但是在应用的时候,却是后者

from flask_wtf import FlaskForm

导入这个库里面的 FlaskForm这个类
这里图简单一点,会一次性导入许多的东西,当然都是下面的例子会用到的

from wtforms import StringField, PasswordField, SubmitField, FileField

导入一些常用的表单类型,相当于input标签中type的作用

from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo

导入一些常用的验证表单,用来确保表单内容是否合法
这里写一个用户登陆和注册时的案例,flask_wtf的作用就是用来限制登陆和注册时的信息

from flask_wtf import FlaskForm
# 确定表单的类型, 相当于input标签中type的作用
from wtforms import StringField, PasswordField, SubmitField, FileField
# 确保表单内容是否合法;
from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo



# Regexp == regular experssion (用于编写正则表达式的)
class LoginForm(FlaskForm):
    user = StringField(
        label="用户名/手机/邮箱",
        validators=[
            DataRequired(message="用户名不能为空"),
            Length(5, 12),
        ]
    )
    passwd = PasswordField(
        label="密码",
        validators=[
            Length(6)
        ]
    )
    submit = SubmitField(
        label="登录"

    )


class RegisterForm(FlaskForm):
    user = StringField(
        label="用户名/手机/邮箱",
        validators=[
            Length(5, 12),
        ]
    )
    email = StringField(
        label="邮箱",
        validators=[
            Email("邮箱格式不正确!")
        ]
    )
    phone = StringField(
        label="电话",
        validators=[
            Regexp(r'1\d{10}', message="手机格式不正确!")
        ]
    )
    passwd = PasswordField(
        label="密码",
        validators=[
            Length(6)
        ]
    )

    repasswd =  PasswordField(
        label="确认密码",
        validators=[
          EqualTo('passwd', "两次密码不一致!")
        ]

    )

    submit = SubmitField(
        label="注册"

    )

上面的代码,需要注意的一点就是:

lable:(是用来对要填写内容的提示)
validators:(是用来对要填写内容的限制),一些具体的限制内容祥解,在上一篇博客中写过

这里还有一点,被我藏起来了(没想到吧),没写到上面的代码中,下面来提起
在用户注册的时候,有时候会被要求,上传图像,这里就有不同的地方了
来,划重点:

face = FileField(
        label="上传头像",
        validators=[
            FileAllowed(['png', 'jpg'], message="文件非图片")
        ]
    )

validators 中的 FileAllowed 是从from flask_wtf.file import FileAllowed这里导出的,还有 FileRequired(必填文件) 也一样,并不同于其它的要求

  1. flask_bootstrap网页编写
    flask_bootstrap 是用来编写html文件的基模版
    其中bootstrap下面有 base.html 这样的基模版,当然,你也可以自己编写模版

下面是需要注意的几点:
1). 如何在flask中使用Boostrap?
要想在程序中集成Bootstrap,显然要对模板做所有必要的改动。不过,更简单的方法是使用一个名为Flask-Bootstrap 的Flask 扩展,简化集成的过程。Flask-Bootstrap 使用pip安装:

pip install flask_bootstrap

2). Flask 扩展一般都在创建程序实例时初始化,下面是Flask_Bootstrap的初始化方法

from flask_bootstrap import  Bootstrap

app = Flask(__name__)
bootstrap = Bootstrap(app)

初始化Flask-Bootstrap 之后,就可以在程序中使用一个包含所有Bootstrap 文件的基模板。这个模板利用Jinja2 的模板继承机制,让程序扩展一个具有基本页面结构的基模板,其中就有用来引入Bootstrap 的元素。

3). 如何引用bootatrap的基模板?

{%extends "bootstrap/base.html"%}

{%block title %}Flask{% endblock %}

这两个块分别表示页面中的导航条和主体内容。在这个模板中,navbar 块使用Bootstrap 组件定义了一个简单的导航条。content 块中有个

容器,其中包含一个页面头部。

4). Flask-Bootstrap定义的其他可用块
参考链接:https://pythonhosted.org/Flask-Bootstrap/basic-usage.html#available-blocks

Flask框架(三)_第1张图片

恩~,还是翻译一下:

块名 说明
doc 整个html文档
html_attribs html标签属性
html html标签中的内容
head head标签中的内容
title title标签中的内容
metas 一组meta标签
styles 层叠样式表定义
body_attribs body标签的属性
body body标签中的内容
navbar 用户定义的导航条
content 用户定义的页面内容
scripts 文档底部的JavaScript 声明

5). 如何继承原有内容
上表中的很多块都是Flask-Bootstrap 自用的,如果直接重定义可能会导致一些问题。例如,Bootstrap 所需的文件在styles 和scripts 块中声明。如果程序需要向已经有内容的块中添加新内容,必须使用Jinja2 提供的super() 函数。例如,如果要在衍生模板中添加新的JavaScript 文件,需要这么定义scripts 块:

{% block scripts %}
{{ super() }}

{% endblock %}

二 flask_sqlalchemy

flask_sqlalchemy为 Flask 提供了一个 SQLAlchemy 扩展,用来处理数据库的操作

  1. 快速入门
    常见情况下对于只有一个 Flask 应用,所有您需要做的事情就是创建 Flask 应用,选择加载配置接着创建 SQLAlchemy 对象时候把 Flask 应用传递给它作为参数
    一旦创建,这个对象就包含 sqlalchemy 和 sqlalchemy.orm 中的所有函数和助手。此外它还提供一个名为 Model 的类,用于作为声明模型时的 delarative 基类
from flask_sqlalchemy import  SQLAlchemy
from flask import  Flask
app = Flask(__name__)

#Flask-SQLAlchemy 中存在的配置值
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:westos@localhost/User'

# SQLAlchemy 将会追踪对象的修改并且发送信号。
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

db = SQLAlchemy(app)

具体的配置键说明:

配置键 说明
SQLALCHEMY_DATABASE_URI 用于连接数据的数据库,例如:sqlite:////tmp/test.db; mysql://username:password@server/db
SQLALCHEMY_TRACK_MODIFICATIONS 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。

当然 mysql://username:password@server/db 在连接的时候,默认的是 Python2 中的 MySQLdb 。要想使用 Python3 中的 pymysql ,需要在mysql+pymysql://username:password@server/db 后面加上 mysql+pymysql
这样才是完整连接 URI 格式,才会连接上Python3中使用的数据库

  1. 简单示例
    通常下,Flask-SQLAlchemy 的行为就像一个来自 declarative 扩展配置正确的 declarative 基类;您的所有模型的基类叫做 db.Model。它存储在您必须创建的 SQLAlchemy 实例上。

一个简单的示例:

class User(db.Model):
   #  __tablename__ = "students"	
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    
    def __repr__(self):
        return '' % self.username

其中:
1). 默认情况下创建一个表, 表名为类名; 如果指定了__tablename__, 那么表名为你指定的名称
2). 用 Column 来定义一列。列名就是您赋值给那个变量的名称。如果您想要在表中使用不同的名称,您可以提供一个想要的列名的字符串作为可选第一个参数
3). 主键用 primary_key=True 标记。可以把多个键标记为主键,此时它们作为复合主键
4). 列的类型是 Column 的第一个参数。您可以直接提供它们或进一步规定(比如提供一个长度)。下面的类型是最常用的:

类型 解释说明
Integer 一个整数
String (size) 有长度限制的字符串
Text 一些较长的 unicode 文本
DateTime 表示为 Python datetime 对象的 时间和日期
Float 存储浮点值
Boolean 存储布尔值
PickleType 存储为一个持久化的 Python 对象
LargeBinary 存储一个任意大的二进制数据
  1. 一对多关系
    最为常见的关系就是一对多的关系。因为关系在它们建立之前就已经声明,您可以使用 字符串来指代还没有创建的类。

一对多的简单示例:

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    addresses = db.relationship('Address', backref='person')
                               

class Address(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(50))
    person_id = db.Column(db.Integer, db.ForeignKey('person.id'))

其中:
1). 一的一端需要写反向引用:关系使用 relationship() 函数表示
2). 多的一端需要写外键:必须用类 sqlalchemy.schema.ForeignKey 来单独声明

还有一点需要注意:

db.drop_all()

创建定义的表结构

db.create_all()

删除定义的表结构

  1. 数据库的增删改查
    这是我刚才运用上面提到的点,创建了一个数据库:
    当然在创建之前,需要先创建一个,中文编码格式的库,
CREATE DATABASE User CHARACTER SET utf8 COLLATE utf8_general_ci;

然后再在库上建立数据。

from datetime import datetime
from flask_sqlalchemy import  SQLAlchemy
from flask import  Flask
from sqlalchemy import desc
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:redhat@localhost/User'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

# 用户表
class User(db.Model):
    # autoincrement=True自增
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    #  unique=True, name的值不能重复, 是唯一的;
    name = db.Column(db.String(50), unique=True)
    # 长度为100, 是因为网站密码一般会加密;
    passwd = db.Column(db.String(100))
    # 指定用户注册/创建的时间,
    # default, 指定默认值, datetime.now()获取当前时间;
    # 用户注册时间为当前时间;
    add_time = db.Column(db.DateTime, default=datetime.now())
    # 用户的角色id,不能随便写, 必须要关联其他的数据库表(role) --- 外键
    role_id = db.Column(db.INTEGER, db.ForeignKey('role.id'))

    def __repr__(self):
        return '' %(self.name)

# 用户角色表
class Role(db.Model):
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(50), unique=True)
    #  Role表中的users属性与User表关联, 并且User这个表中可以由role这个对象属性;
    users = db.relationship('User',backref='role')

    def __repr__(self):
        return  "" %(self.name)

if __name__ == '__main__':
    db.create_all()

执行结果:
Flask框架(三)_第2张图片

1). 增加数据
在查询数据之前我们必须先插入数据。您的所有模型都应该有一个构造函数,如果您 忘记了,请确保加上一个。只有您自己使用这些构造函数而 SQLAlchemy 在内部不会使用它, 所以如何定义这些构造函数完全取决与您。
向数据库插入数据分为三个步骤:

1.	创建 Python 对象
2.	把它添加到会话
3.	提交会话

这里的会话不是 Flask 的会话,而是 Flask-SQLAlchemy 的会话。它本质上是一个 数据库事务的加强版本。它是这样工作的:

创建用户角色:

role1 = Role(name="超级会员")
role2 = Role(name="会员")

db.session.add(role1)
db.session.add(role2)

db.session.commit()

执行结果:
Flask框架(三)_第3张图片

添加100个用户,其中50个为超级会员, 50个为会员:

    for i in range(50):
        u = User(name='westos%s' %(i), passwd='westos', role_id=1)
        db.session.add(u)
    db.session.commit()


    for i in range(50):
        u = User(name='redhat%s' % (i), passwd='redhat', role_id=2)
        db.session.add(u)
    db.session.commit()

执行结果:
Flask框架(三)_第4张图片

2). 查询数据
Flask-SQLAlchemy 在您的 Model 类上提供了 query 属性。当您访问它时,您会得到一个新的所有记录的查询对象。在使用 all() 或者 first() 发起查询之前可以使用方法 filter() 来过滤记录。

(1)查询所有数据

print(Role.query.all())
print(User.query.all())

在这里插入图片描述

(2)根据条件查询数据(筛选数据(filter_by)); 相当于slect * from table where xxx=xxx;

    print(User.query.filter_by(role_id=1).all())
    print(User.query.filter_by(role_id=2).all())

在这里插入图片描述

(3)对于找到的内容进行更新

    u = User.query.filter_by(name='westos0').first()
    print(u)
    u.passwd = '123456'
    db.session.add(u)
    db.session.commit()

Flask框架(三)_第5张图片

(4)筛选数据方法2(filter),通过这种方式可以查看原生的sql语句

    user = User.query.filter(User.role_id==1)
    print(user)

Flask框架(三)_第6张图片

(5)对于查询的信息进行显示限制

    users = User.query.filter_by(role_id=1).limit(5).all()
    print(users, len(users), end='\n')

在这里插入图片描述

(6)对于查询的信息进行排序输出(默认情况由小到大进行排序), 如果想要由大到小: desc(User.add_time)

    users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).all()
    print(users)

在这里插入图片描述

(7)多个过滤函数加一个显示函数

    users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).limit(5).all()
    print(users)
    #  offset指定偏移量, limit 指定输出数量, 类似于切片操作;
    #  1 2 3 4 5 6 7 8 9
    #  limit(5): 1 2 3 4 5
    #  limit(5).offset(2): 3 4 5 6 7
    users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).limit(5).offset(2).all()
    print(users)
    # 切片操作li[2:7]
    users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).slice(2,7).all()
    print(users)

    count = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).slice(2, 7).count()
    print(count)

Flask框架(三)_第7张图片

(8)分页:第一个参数代表显示第几页的数据, 第二个参数代表每页显示多少条数据

    users = User.query.paginate(1, 5)
    print(users.items)
    users = User.query.paginate(2, 5)
    print(users.items)

Flask框架(三)_第8张图片

(9)反向引用的使用

    u = User.query.filter_by(name='westos0').first()
    print(u.name, u.passwd, u.add_time, u.role_id, u.role, u.role.id, u.role.name)

Flask框架(三)_第9张图片

3). 删除数据
删除数据与增加数据是十分类似的,使用 delete() 代替 add()

    user=User.query.filter_by(name='redhat0').first()
    db.session.delete(user)
    db.session.commit()

Flask框架(三)_第10张图片

这里有可以参考的两点:
1-查询过滤器总结
Flask框架(三)_第11张图片

2-执行函数总结
Flask框架(三)_第12张图片

三 flask_migrate与flask_script

  1. flask_migrate
    Flask-Migrate是用于处理SQLAlchemy 数据库迁移的扩展工具。当Model出现变更的时候,通过migrate去管理数据库变更
    一般分为三步(init、migrate、upgrade)

  2. flask_script
    Flask_script扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器,一个定制的Python shell,设置数据库的脚本,cronjobs,及其他运行在web应用之外的命令行任务;使得脚本和系统分开;Flask_script和Flask本身的工作方式类似,只需定义和添加从命令行中被Manager实例调用的命令

  3. 结合的简单使用
    需求:现在,在 Role 数据表中需要添加一个新的属性(grace:级别),需要该怎么做,总不能将数据库删了重新建吧。
    这里就需要用到这两个库了,做数据库的扩展

可以将代码写在 manage 文件下,与 models 文件同级
manage文件就是存放 关于数据库扩展的文件

from flask_migrate import  Migrate, MigrateCommand
from flask_script import  Shell, Manager
from models import app, db, User, Role

# 用来管理命令的对象, Manager用来跟踪所有名林不过并处理他们如何从命令行调用;
manager = Manager(app)
migrate = Migrate(app, db)

# 添加一条数据库管理的命令
manager.add_command('db', MigrateCommand)

#  实现添加用户的命令
if __name__ == "__main__":
    # 准备
    manager.run()

首先,需要在models文件中填写需要添加的属性
Flask框架(三)_第13张图片

不用运行,manager文件读取该文件就行了

执行manager文件,可得到:这些位置参数
Flask框架(三)_第14张图片

执行 db 参数后,可得到下列的命令,用来对 models 中建立的数据库操作

(2048) [kiosk@foundation69 day30]$ python manage.py db
usage: Perform database migrations

Perform database migrations

positional arguments:
  {init,revision,migrate,edit,merge,upgrade,downgrade,show,history,heads,branches,current,stamp}
    init                Creates a new migration repository
    revision            Create a new revision file.
    migrate             Alias for 'revision --autogenerate'
    edit                Edit current revision.
    merge               Merge two revisions together. Creates a new migration
                        file
    upgrade             Upgrade to a later version
    downgrade           Revert to a previous version
    show                Show the revision denoted by the given symbol.
    history             List changeset scripts in chronological order.
    heads               Show current available heads in the script directory
    branches            Show current branch points
    current             Display the current revision for each database.
    stamp               'stamp' the revision table with the given revision;
                        don't run any migrations

optional arguments:
  -?, --help            show this help message and exit

1).下来就是操作的三部曲了

(1) init : 初始化数据,创建迁移仓库

python manage.py db init

Flask框架(三)_第15张图片

可以看到,创建了一个 migrations 目录,其中 versions 就记录着数据库的变迁
models 文件中,不是给 Role 数据表添加了一个 grace 属性
(2)migrate :增加属性

python manage.py db migrate -m "Role中添加属性grace"

其中: -m 可以指定此次操作的名称
Flask框架(三)_第16张图片
可以看到 versions 文件中记录了此次的变化
(3)upgrade :数据库更新此次变化

python manage.py db upgrade

Flask框架(三)_第17张图片
可以看到,数据库的更改

2). 当然,此库还有一个强大的功能就在于,可以返回原先的操作
在这里插入图片描述
从历史中可以查看到,对原先的数据库(base),做了更改

退回base状态:
在这里插入图片描述
从数据库中查看结果:
Flask框架(三)_第18张图片
可以明显的看到更改前后的不同之处

3). 当然你也可以自定义一些操作
使用装饰器来装饰你的函数
(1)manager.command

@manager.command
def showUser():
    """显示所有的用户"""
    users = User.query.all()
    print(users[:5])
# 由于用户太多了,这里只显示前5个

Flask框架(三)_第19张图片
可以看到这里多个一个位置参数,用来显示所有的用户
在这里插入图片描述
调用这个参数,就可以显示所有的用户了

(2)manager.option
同 command 不同的点在于,此方法可以传递参数
比如说添加一个角色:

@manager.option('-n', '--name', help="角色名称")
def add_role(name):
    try:
        role1 = Role(name=name)
        db.session.add(role1)
        db.session.commit()
    except Exception:
        print("创建用户角色失败!")
    else:
        print("创建用户角色%s成功!" %(name))

第一行括号里面可以写成帮助文档,用来执行命令
就像 ls 命令一样Flask框架(三)_第20张图片

第一个为缩写,第二个为全称,第三个为帮助文档
在这里插入图片描述
Flask框架(三)_第21张图片
创建白金会员成功,当然假如创建已经存在的,当然会失败了

关于Flask框架的一些基本内容就说到这了。

你可能感兴趣的:(Flask框架(三))