Flask-05-数据库迁移、flask-session、蓝图 Blueprint(模块化,运行机制,蓝图的url前缀,注册蓝图中的静态文件的相关路由,设置蓝图中模版的目录)

一、数据库迁移

  • 在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。
  • 更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。
  • 在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。
  • 为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。

首先要在虚拟环境中安装Flask-Migrate。

pip install flask-migrate
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
app = Flask(__name__)


class Config():
    # DEBUG调试模式
    DEBUG = True
    # json多字节转unicode编码
    JSON_AS_ASCII = False
    # 数据库链接配置
    # SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 查询时会显示原始SQL语句
    SQLALCHEMY_ECHO = True

app.config.from_object(Config)

manager = Manager()
manager.app = app

db = SQLAlchemy()
db.init_app(app)

# 数据迁移
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)

#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db', MigrateCommand)

"""创建模型类"""
from datetime import datetime
class Achievement(db.Model):
    __tablename__ = "tb_achievement"
    id = db.Column(db.Integer, primary_key=True,comment="主键")
    student_id = db.Column(db.Integer, db.ForeignKey("tb_student.id"), comment="学生")
    course_id = db.Column(db.Integer, db.ForeignKey("tb_course.id"), comment="课程")
    score = db.Column(db.DECIMAL(5,2), nullable=True, comment="成绩分数")
    created_time = db.Column(db.DateTime, default=datetime.now(), comment="考试时间")

    def __repr__(self):
        return "[%s],%s进行了一次%s科目考试,成绩:%s" % (self.created_time,self.student.name,self.course.name,self.score)

class Student(db.Model):
    __tablename__ = "tb_student"
    id = db.Column(db.Integer, primary_key=True,comment="主键ID")
    name = db.Column(db.String(250), comment="姓名")
    avatar = db.Column(db.String(250), comment="头像")
    age = db.Column(db.Integer, comment="年龄")
    sex = db.Column(db.Boolean, default=False, comment="性别")
    money = db.Column(db.DECIMAL(8,2), nullable=True, comment="钱包")
    achievement_list = db.relationship("Achievement",uselist=True, backref="student", lazy="select")
    # backref 反向引用,类似django的related,通过外键模型查询主模型数据时的关联属性
    info = db.relationship("StudentInfo", backref="own", uselist=False)
    def __repr__(self):
        return self.name

class StudentInfo(db.Model):
    __tablename__ = "tb_student_info"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    sid= db.Column(db.Integer,db.ForeignKey(Student.id), comment="学生")
    address = db.Column(db.String(255), nullable=True, comment="家庭住址")
    mobile = db.Column(db.String(15), unique=True, comment="紧急联系电话")

    def __repr__(self):
        return self.own.name

class Teacher(db.Model):
    __tablename__ = "tb_teacher"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250), comment="姓名")
    option = db.Column(db.Enum("讲师","助教","班主任"), default="讲师", comment="教职")
    course_list = db.relationship("Course",uselist=True, backref="teacher",lazy="subquery")
    def __repr__(self):
        return self.name

class Course(db.Model):
    __tablename__ = "tb_course"
    id = db.Column(db.Integer, primary_key=True, comment="主键ID")
    name = db.Column(db.String(250), unique=True, comment="课程名称")
    price = db.Column(db.Numeric(6, 2))
    teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id),comment="老师")
    achievement_list = db.relationship("Achievement", backref="course", uselist=True, lazy="select")
    def __repr__(self):
        return self.name

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

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

1.创建迁移版本仓库
#这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python main.py db init
2.创建迁移版本
  • 自动创建迁移版本有两个函数
    • upgrade():函数把迁移中的改动应用到数据库中。
    • downgrade():函数则将改动删除。
  • 自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。
  • 对比不一定完全正确,有可能会遗漏一些细节,需要进行检查
python main.py db migrate -m 'initial migration'

# 这里等同于django里面的 makemigrations,生成迁移版本文件
3.升级版本库的版本
python main.py db upgrade 
4.降级版本库的版本
python main.py db downgrade
5.版本库的历史管理

可以根据history命令找到版本号,然后传给downgrade命令:

python manage.py db history

输出格式:<base> ->  版本号 (head), initial migration

回滚到指定版本

python manage.py db downgrade # 默认返回上一个版本
python manage.py db downgrade 版本号   # 返回到指定版本号对应的版本

数据迁移的步骤:

1. 初始化数据迁移的目录
python manage.py db init

2. 数据库的数据迁移版本初始化
python manage.py db migrate -m 'initial migration'

3. 升级版本[创建表/创建字段/修改字段]
python manage.py db upgrade 

4. 降级版本[删除表/删除字段/恢复字段]
python manage.py db downgrade
6.模块推荐

文档: https://faker.readthedocs.io/en/master/locales/zh_CN.html

批量生成测试数据: https://github.com/joke2k/faker

二、flask-session

允许设置session到指定存储的空间中
安装命令:

pip install flask-Session

使用session之前,必须配置一下配置项:

SECRET_KEY = "*(%#4sxcz(^(#$#8423" # session秘钥
1.redis保存session的基本配置
from flask import Flask,session
from flask_redis import FlaskRedis
from flask_session import Session
app = Flask(__name__)
redis = FlaskRedis()
session_store = Session()
class Config():
    # DEBUG调试模式
    DEBUG = True
    # json多字节转unicode编码
    JSON_AS_ASCII = False
    # 数据库链接配置
    SECRET_KEY = "*(%#4sxcz(^(#$#8423"
    # session存储方式为redis
    SESSION_TYPE = "redis"
    # session保存数据到redis时启用的链接对象
    SESSION_REDIS = redis
    # 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
    SESSION_PERMANENT = True
    # 是否对发送到浏览器上session的cookie值进行加密
    SESSION_USE_SIGNER = True
    # 保存到redis的session数的名称前缀
    SESSION_KEY_PREFIX = "session:"
    # redis的链接配置
    REDIS_URL = "redis://localhost:6379/1"

app.config.from_object(Config)
redis.init_app(app)
session_store.init_app(app)

@app.route("/")
def index():
    session["username"] = "xiaoming"
    return "Ok"

@app.route("/get_session")
def get_session():
    print( session["username"] )
    return "ok"

@app.route("/redis1")
def set_redis():
    redis.set("username","xiaohuihui")
    redis.hset("brother","zhangfei","17")
    return "ok"

@app.route("/redis2")
def get_redis():
    user = redis.get("username").decode()
    print(user)

    brother = redis.hgetall("brother")
    print(brother)
    print(brother["zhangfei".encode()].decode())

    return "ok"

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

2.SQLAlchemy存储session的基本配置

需要手动创建session表,在项目第一次启动的时候,使用db.create_all()来完成创建。

from flask import Flask,session
from flask_redis import FlaskRedis
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy()
redis = FlaskRedis()
session_store = Session()
class Config():
    # DEBUG调试模式
    DEBUG = True
    # json多字节转unicode编码
    JSON_AS_ASCII = False
    SECRET_KEY = "*(%#4sxcz(^(#$#8423"
    # 数据库链接配置
    # SQLALCHEMY_DATABASE_URI = "mysql://账号:密码@IP/数据库名?编码"
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/students?charset=utf8mb4"
    # 动态追踪修改设置,如未设置只会提示警告
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 查询时会显示原始SQL语句
    SQLALCHEMY_ECHO = True

    # redis的链接配置
    REDIS_URL = "redis://localhost:6379/1"

    # 数据库保存session
    SESSION_TYPE = 'sqlalchemy'  # session类型为sqlalchemy
    SESSION_SQLALCHEMY = db  # SQLAlchemy对象
    SESSION_SQLALCHEMY_TABLE = 'tb_session'  # session要保存的表名称
    SESSION_PERMANENT = True  # 如果设置为True,则关闭浏览器session就失效。
    SESSION_USE_SIGNER = False  # 是否对发送到浏览器上session的cookie值进行加密
    SESSION_KEY_PREFIX = 'session:'  # 保存到session中的值的前缀

db.init_app(app)
app.config.from_object(Config)
redis.init_app(app)
session_store.init_app(app)

@app.route("/")
def index():
    session["username"] = "xiaohui"
    return "Ok"

@app.route("/get_session")
def get_session():
    return session["username"]

if __name__ == '__main__':
    # with app.app_context():
    #     db.create_all()
    app.run()

三、蓝图 Blueprint

1.模块化

随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理

简单来说,Blueprint 是一个存储视图方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。

Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:

  • 一个项目可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
  • 在一个应用中,一个模块可以注册多次
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint

但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

Blueprint对象用起来和一个应用/Flask对象差不多,最大的区别在于一个 蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效
使用蓝图可以分为四个步骤:
①.创建一个蓝图的包,例如users,并在__init__.py文件中创建蓝图对象

users_blue = Blueprint("users",__name__,template_folder="users_templates",static_folder='users_static',static_url_path="/libs")

②.在这个蓝图目录下, 创建views.py文件,保存当前蓝图使用的视图函数

from . import users_blue
from flask import render_template
# 2. 编写视图
@users_blue.route("/")
def index():
    return render_template("index.html",title="users/index/index.html")

@users_blue.route("/list")
def list():
    return "users/list"

③.在users/init.py中引入views.py中所有的视图函数

from flask import Blueprint
# 1. 创建蓝图目录并对蓝图对象进行初始化
users_blue = Blueprint("users",__name__,template_folder="users_templates",static_folder='users_static',static_url_path="/libs")

# 3. 注册视图
from .views import *

④.在主应用main.py文件中的app对象上注册这个users蓝图对象

# 4. 注册蓝图
from users import users_blue

app.register_blueprint(users_blue,url_prefix="/users")
2.运行机制
  • 蓝图是保存了一组将来可以在应用对象上执行的操作,注册路由就是一种操作
  • 当在app对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表
  • 然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
  • 当执行app对象的 register_blueprint() 方法时,应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用应用对象的 add_url_rule() 方法,这将真正的修改应用对象的usr_map路由表
3.蓝图的url前缀
  • 当我们在应用对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)
  • 在应用最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个前缀,这个可以保证在多个蓝图中使用相同的URL规则而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可
  • url_for在使用时,如果要生成一个蓝图里面的视图对应的路由地址,则需要声明当前蓝图名称+视图名称
url_for('users.home') # /users/home
4.注册蓝图中的静态文件的相关路由

和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时指定static_folder参数。
下面的示例将蓝图所在目录下的static_users目录设置为静态目录

# users/__init__.py,代码:
user_blu = Blueprint("users",__name__,static_folder='users_static')

# 启动文件 main.py,代码:
from users import users_blue
app.register_blueprint(users_blue,url_prefix="/users")

可以在创建蓝图对象时使用 static_url_path 来改变静态目录的路由。

users_blue = Blueprint("users",__name__,template_folder="users_templates",static_folder='users_static',static_url_path="/libs")
5.设置蓝图中模版的目录

蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录

创建蓝图中的模板目录users_templates :

users_blue = Blueprint("users",__name__,template_folder="users_templates",static_folder='users_static',static_url_path="/libs")

views.py

from . import users_blue
from flask import render_template
# 2. 编写视图
@users_blue.route("/")
def index():
    return render_template("index.html",title="users/index/index.html")

:如果在 templates 中存在和 templates_users 有同名模板文件时, 则系统会优先使用 templates 中的文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值