Flask框架的学习——10—(Flask-SQLAlchemy插件、Flask-Script命令行传参、Flask-Migrate数据迁移、WTForms表单验证)

1、Flask-SQLAlchemy插件

另外一个框架,叫做Flask-SQLAlchemy,Flask-SQLAlchemy是对SQLAlchemy进行了一个简单的封装,使得我们在flask中使用sqlalchemy更加的简单。可以通过pip install flask-sqlalchemy
在这里插入图片描述

  • 数据库初始化:数据库初始化不再是通过create_engine
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from constants import DB_URI
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
db = SQLAlchemy(app)
  • ORM类:之前都是通过Base = declarative_base()来初始化一个基类,然后再继承,在Flask-SQLAlchemy中更加简单了
class User(db.Model):
  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 '<User %s>' % self.username
  • 映射模型到数据库表:使用Flask-SQLAlchemy所有的类都是继承自db.Model,并且所有的Column和数据类型也都成为db的一个属性。但是有个好处是不用写表名了,Flask-SQLAlchemy会自动将类名小写化,然后映射成表名。
    写完类模型后,要将模型映射到数据库的表中,使用以下代码创建所有的表:
db.create_all()
  • 添加数据:这时候就可以在数据库中看到已经生成了一个user表了。
admin = User('admin','admin@example.com')
guest = User('guest','guest@example.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
  • 添加数据和之前的没有区别,只是session成为了一个db的属性。
  • 查询数据:查询数据不再是之前的session.query了,而是将query属性放在了db.Model上,所以查询就是通过Model.query的方式进行查询了
users = User.query.all()
  • 删除数据:删除数据跟添加数据类似,只不过session是db的一个属性而已:
db.session.delete(admin)
db.session.commit()

实例整体代码flask_sqlalchemy_demo.py如下:

# -*- encoding: utf-8 -*-
"""
@File    : flask_sqlalchemy_demo.py
@Time    : 2020/4/28 9:38
@Author  : chen

"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy


# 127.0.0.1
HOSTNAME = "localhost"
DATABASE = "demo0425"
PORT = 3306
USERNAME = "root"
PASSWORD = "root"
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)


app = Flask(__name__)

'''
# 创建引擎并生成Base类
engine = create_engine(DB_URL)
Base = declarative_base(engine)
'''
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URL           # 数据库连接成功


# FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.
# Set it to True or False to suppress this warning.'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
# 这里是为了解决上面的警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)               # 创建SQLAlchemy


class User(db.Model):
    __tablename__ = "user2"            # 如果没定义__tablename__,会默认以User模型的小写user定义表名
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50))
    
    # articles = db.relationship('author')             # 下面使用backref='articles',这里就不用定义了
    
    # 输出信息     __str__方法打印不够详细
    def __repr__(self):
        return "User(name:%s)" % self.name


class Article(db.Model):
    __tablename__ = "article2"
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50))
    
    uid = db.Column(db.Integer, db.ForeignKey('user2.id'))    # 定义外键
    
    author = db.relationship('User', backref='articles')      # 反向引用articles属性


'''
Base.metadata.drop_all()
Base.metadata.create_all()         下面是替代简化语句
'''
db.drop_all()
db.create_all()

# 添加数据
user = User(name='U1')
article = Article(title='A1')
article.author = user    # 反向引用

db.session.add(article)         # 数据添加到数据库中
db.session.commit()

# articles = db.session.query(Article).order_by(Article.id).all()
# 查询数据
user = User.query.all()                     # 注意和之前的写法不一样,简化写法
print(user)
# users = db.session.query(User).all()      # 打印数据和上面相同,书写方式不一样

# 排序
users = User.query.order_by(User.id.desc()).all()
print(users)

# 删除数据
user = User.query.filter(User.name == 'U1').first()
db.session.delete(user)
db.session.commit()


@app.route("/")
def index():
    return "首页"


if __name__ == '__main__':
    app.run(debug=True)

2、Flask-Script 命令行传参

Flask-Script的作用是可以通过命令行的形式来操作Flask。例如通过命令跑一个开发版本的服务器、设置数据库,定时任务等。要使用Flask-Script,可以通过pip install flask-script安装最新版本。

from flask_script import Manager
from flask_sqlalchemy_demo import app     # 需要将当前文件夹设置为当前根目录,才不会报错

manage = Manager(app)


@manage.command
def index():
    print("hello world")

 
'''
@option('-n', '--name', dest='name')
@option('-u', '--url', dest='url')
def hello(name, url):
    print("hello", name, url)
'''


# 命令行传参
@manage.option('-n', '--name', dest='name')
@manage.option('-u', '--url', dest='url')
def hello(name, url):
    print("hello", name, url)


if __name__ == '__main__':
    manage.run()

在这里插入图片描述
在这里插入图片描述
我们把脚本命令代码放在一个叫做manage.py文件中,然后在终端运行python manage.py hello命令,就可以看到输出hello了。

用命令行创建超级用户,并向数据库中添加数据,实例如下:
在这里插入图片描述
flask_script_demo.py

# -*- encoding: utf-8 -*-
"""
@File    : flask_script_demo.py
@Time    : 2020/4/28 15:35
@Author  : chen

"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config     # 导入config.py文件

app = Flask(__name__)
app.config.from_object(config)       # 加载配置文件中的选项

db = SQLAlchemy(app)


class AdminUser(db.Model):
    __tablename__ = "admin_users"  # 如果没定义__tablename__,会默认以User模型的小写adminuser定义表名
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50))
    email = db.Column(db.String(50))
    
    # 输出信息     __str__方法打印不够详细
    def __repr__(self):
        return "User(name:%s)" % self.name


db.create_all()
db.session.commit()

manage.py

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/4/28 15:45
@Author  : chen

"""
from flask_script_demo import app, AdminUser, db      # 引用flask_script_demo.py中的app,AdminUser,db模块
from flask_script import Manager

manage = Manager(app)


'''
@option('-n', '--name', dest='name')
@option('-u', '--url', dest='url')
def hello(name, url):
    print("hello", name, url)
'''


# 命令行传参
@manage.option('-n', '--name', dest='name')
@manage.option('-e', '--email', dest='email')
def add_user(name, email):
    user = AdminUser(name=name, email=email)         # 前面的name=是字段,后面的name是参数值
    
    db.session.add(user)
    db.session.commit()
    

if __name__ == '__main__':
    manage.run()

定义命令的三种方法
  1. 使用@command装饰器
  2. 使用类继承自Command类
from flask_script import Command,Manager
from your_app import app

manager = Manager(app)

class Hello(Command):
    "prints hello world"

    def run(self):
        print("hello world")

manager.add_command('hello',Hello())

使用类的方式,有三点需要注意:

  • 必须继承自Command基类。
  • 必须实现run方法。
  • 必须通过add_command方法添加命令。
  1. 使用option装饰器:如果想要在使用命令的时候还传递参数进去,那么使用@option装饰器更加的方便
@manager.option('-n','--name',dest='name')
def hello(name):
    print('hello ',name)

调用hello命令

python manage.py -n xxx
python manage.py --name xxx
添加参数到命令中
  • option装饰器:以上三种创建命令的方式都可以添加参数
@manager.option('-n', '--name', dest='name', default='joe')
@manager.option('-u', '--url', dest='url', default=None) 
def hello(name, url): 
  if url is None: 
     print("hello", name)
  else: 
      print("hello", name, "from", url)
  • command装饰器:command装饰器也可以添加参数,但是不能那么的灵活
@manager.command 
def hello(name="Fred") 
  print("hello", name)
  • 类继承:类继承也可以添加参数
from flask_Flask import Comman,Manager,Option

class Hello(Command):
  option_list = (
      Option('--name','-n',dest='name'),
  )

  def run(self,name):
      print("hello %s" % name)

如果要在指定参数的时候,动态的做一些事情,可以使用get_options方法:

class Hello(Command):
  def __init__(self,default_name='Joe'):
self.default_name = default_name

  def get_options(self):
      return [
          Option('-n','--name',dest='name',default=self.default_name),
      ]

  def run(self,name):
      print('hello',name)

3、Flask-Migrate

在实际的开发环境中,经常会发生数据库修改的行为。一般我们修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。这时候如果有一个工具能专门做这种事情,就显得非常有用了,而flask-migrate就是做这个事情的。flask-migrate是基于Alembic进行的一个封装,并集成到Flask中,而所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并将变化映射到数据库中

使用Flask-Migrate需要安装

pip install flask-migrate

Win10系统安装使用Flask-Migrate需要注意的是:
在这里插入图片描述
alembic 1.4版本下载

Flask-Migrate数据迁移步骤:

要让Flask-Migrate能够管理app中的数据库,需要使用Migrate(app,db)来绑定app和数据库

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from constants import DB_URI
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
# 绑定app和数据库
migrate = Migrate(app,db)

class User(db.Model):
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20))

    addresses = db.relationship('Address',backref='user')

class Address(db.Model):
    id = db.Column(db.Integer,primary_key=True)
    email_address = db.Column(db.String(50))
    user_id = db.Column(db.Integer,db.ForeignKey('user.id'))

db.create_all()

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

if __name__ == '__main__':
    app.run()

实例代码文件如下

正常我们使用flask-migrate时,步骤是:1、主文件flask_app.py

# -*- encoding: utf-8 -*-
"""
@File    : flask_app.py
@Time    : 2020/4/28 21:10
@Author  : chen

"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config                             # 引用config文件
from model import User                    # 引用model中的User类

from exts import db                      # 解决互相引用的问题,用exts.py当做第三方文件来引用

'''
ImportError: cannot import name 'User'     互相引用报错

需要用第三个文件来间接导入
'''
app = Flask(__name__)

app.config.from_object(config)

# 需要解决互相引用的问题 传app参数到db中
db.init_app(app)

# db = SQLAlchemy(app)   # 互相引用的问题导致不能直接传参到db


'''
name = db.Column(db.String(50))
email = db.Column(db.String(50))
password = db.Column(db.String(50))
'''
# user = User(name='x1', email='123@cc.com', password='123')
# db.session.add(user)
# db.session.commit()


@app.route("/")
def index():
    return "首页"


if __name__ == '__main__':
    app.run()

2、模型文件model.py

# -*- encoding: utf-8 -*-
"""
@File    : model.py
@Time    : 2020/4/28 22:03
@Author  : chen

"""
# 这个model.py文件放app的模型

# from flask_app import db

# 解决互相引用的问题,用exts.py当做第三方文件来引用
from exts import db


class User(db.Model):
    __tablename__ = "user_migrate"
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50))
    email = db.Column(db.String(50))
    password = db.Column(db.String(50))
    
    # migrate测试添加字段
    age = db.Column(db.Integer)
    gender = db.Column(db.Enum('1', '2'))
    
    # 输出信息     __str__方法打印不够详细
    def __repr__(self):
        return "User(name:%s)" % self.name

3、映射数据库文件manage.py

manage.py文件
这个文件用来存放映射数据库的命令,MigrateCommand是flask-migrate集成的一个命令,因此想要添加到脚本命令中,需要采用manager.add_command(‘db’,MigrateCommand)的方式,以后运行python manage.py db xxx的命令,其实就是执行MigrateCommand。

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/4/29 9:21
@Author  : chen

"""
# 通过这个文件去映射数据库
# 写法固定
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from exts import db                    # exts是为了解决循环引用,根据自己建立的py文件名称修改
from flask_app import app

# 映射哪个模型,就导入哪个模型
from model import User                # 这个部分会在不同项目中不一样

manage = Manager(app)

# init  migrate upgrade
# 模型 -> 迁移文件 -> 表
# 1.要使用flask_migrate,必须绑定app和DB
Migrate(app, db)
# 2.把migrateCommand命令添加到manager中。
manage.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manage.run()

4、配置文件config.py

# -*- encoding: utf-8 -*-
"""
@File    : config.py
@Time    : 2020/4/28 22:01
@Author  : chen

"""
# 这里写的是flask_app.py配置选项,固定写法,方便导入其他的模块

# 127.0.0.1
HOSTNAME = "localhost"
DATABASE = "demo0429"
PORT = 3306
USERNAME = "root"
PASSWORD = "root"
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

'''
# 创建引擎并生成Base类
engine = create_engine(DB_URL)
Base = declarative_base(engine)
'''
SQLALCHEMY_DATABASE_URI = DB_URL           # 数据库连接成功

# FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.
# Set it to True or False to suppress this warning.'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
# 这里是为了解决上面的警告
SQLALCHEMY_TRACK_MODIFICATIONS = False

5、exts是为了解决循环引用问题

# -*- encoding: utf-8 -*-
"""
@File    : exts.py
@Time    : 2020/4/28 22:13
@Author  : chen

"""
# 这个文件是充当第三方引用文件的作用
# exts是为了解决循环引用
from flask_sqlalchemy import SQLAlchemy

'''
# 需要解决互相引用的问题 传app参数db中
db.init_app(app)
'''
# 这里没有导入app参数 ,在flask_app.py中传app参数到db中
db = SQLAlchemy()

6、初始化一个迁移文件夹

python manage.py db init     每个项目执行一次 他负责生成迁移包  #初始化,工程目录下生成一个migrations文件夹

python manage.py db migrate  生成迁移文件
python manage.py db upgrade  执行上升操作(执行迁移信息)  相当于之前的creat_all()操作

Win10系统这里出现一个问题

E:\ENV\flask项目-cBMOsSmb\项目代码10\project_demo
(flask项目-cBMOsSmb) λ python manage.py db migrate
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'user_migrate'
Generating E:\ENV\flask项目-cBMOsSmb\项目代码10\project_demo\migrations\version
s\e96f8a3e6220_.py ...  done

E:\ENV\flask项目-cBMOsSmb\项目代码10\project_demo
(flask项目-cBMOsSmb) λ python manage.py db upgrade
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade 424277a9e799 -> e96f8a3e6220, empty message
Traceback (most recent call last):
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\sqlalchemy\engine\base.py",
 line 1248, in _execute_context
    cursor, statement, parameters, context
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\sqlalchemy\engine\default.p
y", line 590, in do_execute
    cursor.execute(statement, parameters)
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\mysql\connector\cursor.py",
 line 551, in execute
    self._handle_result(self._connection.cmd_query(stmt))
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\mysql\connector\connection.
py", line 490, in cmd_query
    result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query))
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\mysql\connector\connection.
py", line 395, in _handle_result
    raise errors.get_exception(packet)
mysql.connector.errors.ProgrammingError: 1050 (42S01): Table 'user_migrate' already exists

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "manage.py", line 25, in <module>
    manage.run()
  File "E:\ENV\flask项目-cBMOsSmb\lib\site-packages\flask_script\__init__.py",
line 417, in run
    result = self.handle(argv[0], argv[1:])

在这里插入图片描述
注意上面这个bug原因是数据库的版本问题,我在MySQL版本8.0时候会出现这个问题,把MySQL版本换成5.5的时候BUG不会出现。
在这里插入图片描述

4、WTForms表单验证

Flask-WTF
Flask-WTF是简化了WTForms操作的一个第三方库。WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。当然还包括一些其他的功能:CSRF保护,文件上传等。安装Flask-WTF默认也会安装WTForms,因此使用以下命令来安装Flask-WTF。

pip install flask-wtf

表单验证
安装完Flask-WTF后。来看下第一个功能,就是用表单来做数据验证,现在有一个forms.py文件,然后在里面创建一个RegistForm的注册验证表单。

在这里插入图片描述
程序主文件demo.py

# -*- encoding: utf-8 -*-
"""
@File    : demo.py
@Time    : 2020/4/29 15:17
@Author  : chen

"""
from flask import Flask, request, render_template
from forms import RegistForm   # 设置为根目录之后,导入forms.py中的RegistForm进行表单验证


app = Flask(__name__)


@app.route("/")
def index():
    return "123"


# 接受GET请求
@app.route("/regist/", methods=['GET', 'POST'])
def regist():
    if request.method == 'GET':
        return render_template('regist.html')
    else:
    	# 采用wtforms模块进行表单验证
        form = RegistForm(request.form)   # 将forms.py中的验证信息都传入form中
        if form.validate():
            return "success"
        else:
            # 验证错误信息
            print(form.errors)
            
            return "fail"
        
        # 表单验证
        # username = request.form.get('username')
        # password = request.form.get('password')
        # password_repate = request.form.get('password_repate')
        #
        # if len(username) > 3 or len(username) < 10:
        #     return "用户名长度不正确"
        #
        # if password != password_repate:
        #     return "密码不一致"
        #
        # if len(password) > 3 or len(password) < 10:
        #     return "密码长度不正确"


if __name__ == '__main__':
    app.run(debug=True)

网页界面:regist.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--   form验证表单     -->
<form action="" method="post">
<!--表单提交到Flask的名字    <br>换行-->
    用户名:<input type="text" name="username"><br>
    密码: <input type="text" name="password"><br>
    确认密码: <input type="text" name="password_repate"><br>
    <input type="submit" value="注册">
</form>

</body>
</html>

forms.py表单验证

# -*- encoding: utf-8 -*-
"""
@File    : forms.py
@Time    : 2020/4/29 17:07
@Author  : chen

"""
from wtforms import Form, StringField, validators
from wtforms.validators import Length, Regexp, EqualTo
# Regexp 正则表达式模块
# wtforms.validators包含很多内置验证器
# EqualTo 验证器:密码不一致
'''
表单验证

username = request.form.get('username')
password = request.form.get('password')
password_repate = request.form.get('password_repate')

if len(username) > 3 or len(username) < 10:
    return "用户名长度不正确"

if password != password_repate:
    return "密码不一致"

if len(password) > 3 or len(password) < 10:
    return "密码长度不正确"

'''

# 表单验证采用wtforms模块


class RegistForm(Form):
    # 实现判断username的长度验证3-10
    username = StringField(validators=[Length(min=3, max=10, message="用户名长度不正确")])
    
    # 判断password的长度验证3-10
    password = StringField(validators=[Length(min=3, max=10)])

    # 判断 password_repate 的长度验证3-10   EqualTo("password")验证password_repate是否一致
    password_repate = StringField(validators=[Length(min=3, max=10), EqualTo("password", message="两次密码不一致")])
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值