Flask_SQLalchemy


Flask-SQLAlchemy 是 Flask 框架的一个扩展,它将强大的 SQLAlchemy ORM(对象关系映射)工具与 Flask 无缝集成,简化了在 Flask 应用中操作数据库的流程。通过它,你可以用 Python 类(模型)映射数据库表,用类方法和属性替代 SQL 语句,实现数据库的增删改查(CRUD)操作,无需直接编写原生 SQL。

一、Flask-SQLAlchemy 安装

首先通过 pip 安装扩展:

pip install flask-sqlalchemy

二、核心概念与配置

1. 基本概念

  • ORM(对象关系映射):将数据库表映射为 Python 类(称为“模型”),表的列映射为类的属性,表的行映射为类的实例。
  • 会话(Session):SQLAlchemy 通过会话(db.session)管理数据库操作,所有增删改查都需通过会话提交后才会生效。
  • 模型(Model):继承自 db.Model 的 Python 类,对应数据库中的一张表。

2. 数据库配置

在 Flask 应用中,需先配置数据库连接信息(如数据库类型、地址、账号密码等),支持 SQLite、MySQL、PostgreSQL 等主流数据库。

配置格式

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 配置数据库连接 URI(不同数据库格式不同)
# 1. SQLite(无需额外服务,文件型数据库,适合开发测试)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydb.db'  # 数据库文件存当前目录

# 2. MySQL(需提前安装 MySQL 服务器,依赖 pymysql 驱动)
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://用户名:密码@主机:端口/数据库名'
# 示例:'mysql+pymysql://root:123456@localhost:3306/flask_db'

# 3. PostgreSQL(需安装 PostgreSQL 服务器,依赖 psycopg2 驱动)
# app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://用户名:密码@主机:端口/数据库名'

# 禁用 SQLAlchemy 的修改跟踪功能(提高性能,可选)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 初始化 SQLAlchemy 实例(关联 Flask 应用)
db = SQLAlchemy(app)

三、定义模型(数据库表)

模型是 Python 类,继承自 db.Model,类的属性对应表的列。每个属性通过 db.Column 定义,需指定数据类型和约束(如主键、非空、唯一等)。

1. 基本模型示例(用户表)

# 定义 User 模型(对应数据库中的 users 表)
class User(db.Model):
    # 表名(默认是类名小写,可自定义)
    __tablename__ = 'users'

    # 列定义:db.Column(类型, 约束)
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 主键,自增
    username = db.Column(db.String(50), unique=True, nullable=False)  # 用户名,唯一,非空
    email = db.Column(db.String(100), unique=True, nullable=False)    # 邮箱,唯一,非空
    age = db.Column(db.Integer, default=0)  # 年龄,默认值 0

    # 可选:定义 __repr__ 方法,方便打印实例信息
    def __repr__(self):
        return f'<User {self.username}>'

2. 常用数据类型

SQLAlchemy 类型 对应数据库类型 说明
db.Integer INT 整数
db.String(length) VARCHAR(length) 字符串(指定长度)
db.Text TEXT 长文本(无长度限制)
db.Boolean BOOLEAN 布尔值(True/False)
db.DateTime DATETIME 日期时间(需用 datetime 模块)
db.Float FLOAT 浮点数
db.ForeignKey FOREIGN KEY 外键(用于关联表)

四、创建数据库和表

定义模型后,需通过 db.create_all() 创建数据库文件和表结构(仅首次需要)。

1. 创建表结构

# 在应用上下文(App Context)中执行创建操作
with app.app_context():
    db.create_all()  # 检查并创建所有模型对应的表
print("数据库和表创建成功")
  • 执行后,SQLite 会在当前目录生成 mydb.db 文件;MySQL/PostgreSQL 会在指定数据库中创建 users 表。
  • 若模型已修改(如新增字段),直接调用 db.create_all() 不会更新表结构,需用迁移工具(见后文)。

五、数据库操作(CRUD)

所有操作需通过 db.session 执行,最终用 db.session.commit() 提交事务(写入数据库)。

1. 创建(Create):新增数据

with app.app_context():
    # 1. 创建用户实例(对应表中的一行)
    user1 = User(username='张三', email='zhangsan@example.com', age=25)
    user2 = User(username='李四', email='lisi@example.com')  # age 用默认值 0

    # 2. 将实例添加到会话
    db.session.add(user1)  # 添加单个
    db.session.add_all([user2])  # 批量添加

    # 3. 提交会话(写入数据库)
    db.session.commit()
    print("数据新增成功")

2. 读取(Read):查询数据

SQLAlchemy 提供丰富的查询方法,通过 模型名.query 发起查询。

with app.app_context():
    # 1. 查询所有用户
    all_users = User.query.all()
    print("所有用户:", all_users)  # 输出 __repr__ 定义的格式

    # 2. 按条件查询(filter_by:简单条件,参数为关键字参数)
    user_zhangsan = User.query.filter_by(username='张三').first()  # 取第一个结果
    print("张三的邮箱:", user_zhangsan.email)

    # 3. 更复杂的条件(filter:支持表达式,如大于、包含等)
    # 示例:查询年龄 > 20 的用户
    adults = User.query.filter(User.age > 20).all()
    print("年龄>20的用户:", adults)

    # 4. 按主键查询(get 方法,效率最高)
    user_id1 = User.query.get(1)  # 查询 id=1 的用户
    print("id=1的用户:", user_id1)

    # 5. 排序(order_by)和限制数量(limit)
    # 示例:按年龄升序,取前 2 个用户
    sorted_users = User.query.order_by(User.age).limit(2).all()
    print("按年龄排序的前2用户:", sorted_users)

3. 更新(Update):修改数据

with app.app_context():
    # 1. 先查询要修改的用户
    user = User.query.filter_by(username='李四').first()
    if user:
        # 2. 修改属性
        user.age = 30
        user.email = 'new_lisi@example.com'

        # 3. 提交会话(保存修改)
        db.session.commit()
        print("数据更新成功")

4. 删除(Delete):删除数据

with app.app_context():
    # 1. 先查询要删除的用户
    user = User.query.filter_by(username='张三').first()
    if user:
        # 2. 从会话中删除
        db.session.delete(user)

        # 3. 提交会话(执行删除)
        db.session.commit()
        print("数据删除成功")

六、表关系(一对多示例)

实际应用中,表之间常有关联(如“用户-帖子”:一个用户可发多个帖子)。通过 db.relationshipdb.ForeignKey 定义关系。

1. 定义关联模型(帖子表)

class Post(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text)
    # 外键:关联 users 表的 id 列(user_id 是 posts 表的外键)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    # 关系属性:通过 post.author 可访问该帖子的作者(User 实例)
    # backref='posts':在 User 模型中添加 posts 属性,通过 user.posts 访问该用户的所有帖子
    author = db.relationship('User', backref=db.backref('posts', lazy=True))

    def __repr__(self):
        return f'<Post {self.title}>'

2. 使用关联关系

with app.app_context():
    # 1. 创建用户和关联的帖子
    user = User.query.get(2)  # 假设 id=2 是李四
    post1 = Post(title='我的第一篇帖子', content='Hello World', author=user)
    post2 = Post(title='第二篇帖子', content='Flask 学习', user_id=user.id)  # 也可直接指定 user_id

    db.session.add_all([post1, post2])
    db.session.commit()

    # 2. 通过用户访问其所有帖子(user.posts)
    print(f"{user.username} 的帖子:")
    for post in user.posts:
        print(f"- {post.title}")

    # 3. 通过帖子访问其作者(post.author)
    post = Post.query.get(1)
    print(f"帖子《{post.title}》的作者:{post.author.username}")

七、数据库迁移(Flask-Migrate)

当模型修改(如新增字段、修改类型)时,直接用 db.create_all() 无法更新表结构,需用 Flask-Migrate 扩展管理数据库迁移(类似 Git 版本控制)。

1. 安装与初始化

pip install flask-migrate
from flask_migrate import Migrate

# 初始化迁移工具(关联 app 和 db)
migrate = Migrate(app, db)

2. 迁移命令(终端执行)

# 1. 初始化迁移环境(仅首次执行,生成 migrations 文件夹)
flask db init

# 2. 生成迁移脚本(检测模型与数据库的差异)
flask db migrate -m "描述信息(如:新增 age 字段)"

# 3. 应用迁移(更新数据库表结构)
flask db upgrade
  • 若需回滚到上一版本:flask db downgrade

八、最佳实践

  1. 应用上下文:Flask-SQLAlchemy 操作需在 应用上下文 中执行(通过 with app.app_context(): 或在视图函数中,因视图函数默认运行在上下文内)。
  2. 避免硬编码配置:数据库账号密码等敏感信息应放在环境变量或配置文件中(如 .env),而非直接写在代码里。
  3. 生产环境数据库:开发用 SQLite 方便,但生产环境建议用 MySQL 或 PostgreSQL(支持高并发、数据持久化更可靠)。
  4. 事务管理:复杂操作(如多表修改)需确保原子性,失败时用 db.session.rollback() 回滚。

总结

Flask-SQLAlchemy 简化了 Flask 应用的数据库操作:
- 通过 模型类 映射数据库表,无需编写原生 SQL。
- 用 db.session 管理 CRUD 操作,commit() 提交事务。
- 支持表关系(一对多、多对多等),通过 relationship 实现关联查询。
- 配合 Flask-Migrate 处理模型变更,确保数据库结构同步。

掌握它后,可高效开发需要数据库支持的 Flask 应用(如用户系统、博客、电商平台等)。

SQLalchemy

demo01_sqlalchemy.py

"""
数据库操作流程

- 1. 安装扩展
    - pip install flask-sqlalchemy
    - pip install flask-mysqldb / pymysql
- 2. 设置数据库的配置信息
- 3. 创建 sqlalchemy 对象 db,关联 app
- 4. 编写模型类,继承自 db.Model
- 5. 操作数据库
    - 增删改
    - 查询
"""


from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 2. 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/data36"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False


# 3. 创建 sqlalchemy 对象 db,关联 app
db = SQLAlchemy(app)


# 4. 编写模型类,继承自 db.Model
class Student(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "person"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))


@app.route("/")
def helloworld():
    return "hello world Flask"


if __name__ == "__main__":
    # 创建数据库的表,创建的是继承自 db.Model 的表
    with app.app_context():
        # 创建所有继承自db.Model的表
        db.create_all()
        # 删除所有继承自db.Model的表
        # db.drop_all()
    app.run(debug=True)

CRUD

demo02_CRUD.py

"""
增删改查

- 全部都是 db.session 操作
- 常见方法:
    - db.session.add(obj) 添加单个对象
    - db.session.add_all([obj1, obj2]) 添加多个对象
    - db.session.delete(obj) 删除单个对象
    - db.session.commit() 提交会话
    - db.drop_all() 删除继承自 db.Model 的所有表
    - db.create_all() 创建继承自 db.Model 的所有表
    - db.session.rollback() 回滚
    - db.session.remove() 移除会话

- 如果想要打印一个对象的时候想看到指定信息,那么请重写 __repr__ 方法
"""

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 2. 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/data36"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False


# 3. 创建 sqlalchemy 对象 db,关联 app
db = SQLAlchemy(app)


# 4. 编写模型类,继承自 db.Model
# 角色【一方】
class Role(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "roles"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))


# 用户【多方】
class User(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "users"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 建立外键
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))


@app.route("/")
def helloworld():
    return "hello world Flask"


def add():
    role = Role(name="role")
    db.session.add(role)
    db.session.commit()
    user = User(name="user", role_id=role.id)
    db.session.add(user)
    db.session.commit()


if __name__ == "__main__":
    # 创建数据库的表,创建的是继承自 db.Model 的表
    with app.app_context():
        # 创建所有继承自db.Model的表
        db.create_all()
        # 删除所有继承自db.Model的表
        # db.drop_all()

        add()
    app.run(debug=True)

查询

demo03_select.py

"""
查询

xxx.query.[过滤器].[执行器]

- 查询过滤器【可选】
    - filter()
    - filter_by()
    - limit
    - offset()
    - order_by()
    - group_by()

- 查询执行器【必选】
    - all()
    - first()
    - first_or_404()
    - get()
    - get_or_404()
    - count()

    # page: 表示要查询的页数;per_page: 表示每页有多少条数据;
    # Error_out: False查不到不会报错;会返回 paginate 对象
    - paginate(page, per_page, Error_out)  # 分页

    # paginate.pages 总页数
    # paginate.page 当前页
    # paginate.items 当前的对象列表

"""

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 2. 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/data36"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False


# 3. 创建 sqlalchemy 对象 db,关联 app
db = SQLAlchemy(app)


# 4. 编写模型类,继承自 db.Model
# 角色【一方】
class Role(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "roles"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))


# 用户【多方】
class User(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "users"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 建立外键
    role_id = db.Column(db.Integer, db.ForeignKey(Role.id))


@app.route("/")
def helloworld():
    return "hello world Flask"


def add():
    role = Role(name="role")
    db.session.add(role)
    db.session.commit()
    user = User(name="user", role_id=role.id)
    db.session.add(user)
    db.session.commit()


if __name__ == "__main__":
    # 创建数据库的表,创建的是继承自 db.Model 的表
    with app.app_context():
        # 创建所有继承自db.Model的表
        # db.create_all()
        # 删除所有继承自db.Model的表
        # db.drop_all()
        pass

        add()
    app.run(debug=True)

图书馆案例

demo04_library.py

"""
图书馆案例

- 1. 数据库配置
    - 作者模型【一方】
    - 书籍模型【多方】
- 2. 添加测试数据
- 3. 添加作者、书籍
- 4. 删除作者、删除书籍
"""
from flask import Flask, render_template, request, redirect, flash
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)

# 设置密钥
app.config["SECRET_KEY"] = "dsfsdfasdfa"

# 使用 CSRFProtect 保护 app
CSRFProtect(app)

# 1. 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/library"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 2. 创建 sqlalchemy 对象,关联app
db = SQLAlchemy(app)


# 3. 作者模型类
# 作者模型【一方】
class Author(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "authors"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 关系属性
    # 查看作者名下的书籍:Author.books
    # 查看书籍背后的作者:Book.author
    books = db.relationship("Book", backref="author")


# 4. 书籍模型类
# 书籍模型【多方】
class Book(db.Model):  # 默认表名为 student
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "books"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 建立外键
    author_id = db.Column(db.Integer, db.ForeignKey(Author.id))
    # 等价语句:author_id = db.Column(db.Integer, db.ForeignKey("authors.id"))


@app.route("/")
def show_index():
    # 1. 查询所有作者的信息
    authors = Author.query.all()

    # 2. 携带作者的信息,渲染页面
    response = render_template("Cdemo01_library.html", authors=authors)
    return response


"""
添加数据的逻辑

作者不存在,则添加数据
作者存在,书籍存在,不添加数据
作者存在,书籍不存在,添加数据

"""


# 添加书籍
@app.route("/add_data", methods=["POST", "GET"])
def add_data():
    # 1. 获取提交的数据
    author_name = request.form.get("author")
    book_name = request.form.get("book")

    # 判断输入的数据是否为空
    if not all([author_name, book_name]):
        flash("作者或书籍不能为空!")
        return redirect("/")

    # 2. 携带作者的信息,渲染页面
    author = Author.query.filter(Author.name == author_name).first()

    # 3.判断作者是否存在
    if author:
        # 4. 通过书籍名称查询书籍对象
        book = Book.query.filter(
            # 判断作者是否写过该书
            Book.name == book_name,
            Book.author_id == author.id,
        ).first()

        # 5. 判断书籍对象是否存在
        if book:
            flash(f"{author_name}已经写了《{book_name}》")
        else:
            # 创建书籍对象,添加到数据库
            book_adding = Book(name=book_name, author_id=author.id)
            db.session.add(book_adding)
            db.session.commit()
    else:
        # 创建作者对象,添加到数据库
        author_adding = Author(name=author_name)
        db.session.add(author_adding)
        db.session.commit()

        # 创建书籍对象,添加到数据库
        book_adding = Book(name=book_name, author_id=author_adding.id)
        db.session.add(book_adding)
        db.session.commit()

    return redirect("/")


# 删除书籍
@app.route("/del_data/<int:book_id>", methods=["POST", "GET"])
def del_data(book_id):
    # 根据书籍编号取出书籍对象
    book = Book.query.get(book_id)

    # 删除该书籍对象
    db.session.delete(book)
    db.session.commit()

    # 重定向到显示页面
    return redirect("/")


# 删除书籍
@app.route("/del_author/<int:author_id>", methods=["POST", "GET"])
def del_author(author_id):
    # 根据作者编号取出作者对象
    author = Author.query.get(author_id)

    # 删除作者名下所有书籍
    for book in author.books:
        db.session.delete(book)

    # 删除该作者对象
    db.session.delete(author)
    db.session.commit()

    # 重定向到显示页面
    return redirect("/")


if __name__ == "__main__":
    # 为了演示方便,先删除后创建
    # 创建数据库的表,创建的是继承自 db.Model 的表
    with app.app_context():
        # 删除所有继承自db.Model的表
        db.drop_all()
        # 创建所有继承自db.Model的表
        db.create_all()

        # 添加测试数据
        # 生成作者数据
        au1 = Author(name="金庸")
        au2 = Author(name="古龙")
        au3 = Author(name="莫言")

        # 把数据提交给用户会话
        db.session.add_all([au1, au2, au3])

        # 提交会话
        db.session.commit()

        # 生成书籍数据
        bk1 = Book(name="笑傲江湖", author_id=au1.id)
        bk2 = Book(name="神雕侠侣", author_id=au1.id)
        bk3 = Book(name="欢乐英雄", author_id=au2.id)
        bk4 = Book(name="白玉老虎", author_id=au2.id)
        bk5 = Book(name="丰乳肥臀", author_id=au3.id)
        bk6 = Book(name="红高粱家族", author_id=au3.id)

        # 把数据提交给用户会话
        db.session.add_all([bk1, bk2, bk3, bk4, bk5, bk6])

        # 提交会话
        db.session.commit()

    app.run(debug=True)

templates/Cdemo01_library.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>library</title>
</head>

<body>
    <form action="add_data" , method="post">

        {# 设置隐藏字段csrf_token,
        只要使用了CSRFProtect,然后使用模板渲染时可以直接使用csrf_token() 方法 #}
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">

        作者:<input type="text" name="author"><br>
        书籍:<input type="text" name="book"><br>
        <input type="submit" value="添加"><br>
        {% for message in get_flashed_messages() %}
        <span style="color: red;">{{message}}</span>
        {% endfor %}
    </form>
    <hr>

    {# 数据展示 #}
    <ul>
        {% for author in authors %}
        <!-- <li>作者:{{ author.name }} <a href="/del_author/{{author.id}}">删除</a> </li> -->
        <li>作者:{{ author.name }} <a href="{{url_for('del_author', author_id = author.id)}}">删除</a> </li>
        <ul>
            {% for book in author.books %}
            <li>书籍:{{book.name}} <a href="/del_data/{{book.id}}">删除</a> </li>
            {% endfor %}
        </ul>
        {% endfor %}
    </ul>
</body>

</html>

多对多

demo05_many_to_many.py

"""
多对多

案例:学生和课程
"""

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 2. 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/school"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False


# 3. 创建 sqlalchemy 对象 db,关联 app
db = SQLAlchemy(app)


# 4. 编写模型类,继承自 db.Model

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")),
)


class Student(db.Model):
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "students"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))

    # 关系属性
    # secondary:使用在多对多中,用来表示二次查询的
    courses = db.relationship(
        "Course", backref="students", secondary="tb_student_course"
    )


class Course(db.Model):
    # 【主键】 参数1:表示 id 的类型;参数2:表示 id 的约束类型
    __tablename__ = "courses"  # 手动设置表名
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))


@app.route("/")
def helloworld():
    return "hello world Flask"


if __name__ == "__main__":
    # 创建数据库的表,创建的是继承自 db.Model 的表
    with app.app_context():
        # 删除所有继承自db.Model的表
        db.drop_all()
        # 创建所有继承自db.Model的表
        db.create_all()

        # 添加测试数据
        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_all([stu1, stu2, stu3])
        db.session.add_all([cou1, cou2, cou3])

        db.session.commit()

    app.run(debug=True)

数据库迁移

"""
数据库迁移

- 目的:当数据的表结构发生变化之后,如果直接删除原有的数据,再添加新的数据,有可能会导致数据丢失
- 注意点:
    - 1. 是为了备份表结构,而不是数据
    - 2. 如果想要备份数据,需要使用工具,navicat,mysqlworkbench……
- 操作流程
    - 1. 安装扩展
        - pip install flask_script
        - pip install flask_migrate
    - 2. 导入三个类
        - from flask_script import Manager
        - from install flask_migrate
    - 3. 通过 Manager 类创建对象 manager,管理app
        - manager = Manager(app)
    - 4. 使用 Migrate,关联app,db
        - Migrate(app, db)
    - 5. 给manager添加一条操作命令
        - manager.add_command("db", MigrateCommand)
    - 相关迁移命令:
        - 生成迁移文件夹:
            - python xxx.py db init
        - 将模型类生成迁移脚本
            - python xxx.py db migrate -m '注释'
        - 将迁移脚本更新到数据库中
            - python xxx.py db upgrade
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager

# 导入数据迁移核心类
from flask_migrate import Migrate, MigrateCommand

app = Flask(__name__)

# 设置数据库的配置信息
# mysql://[用户名]:[密码]@[主机地址][端口号]/[数据库名]
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/flasktest"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 创建 sqlalchemy 对象 db,关联 app
db = SQLAlchemy(app)

manager = Manager(app)

# 初始化数据迁移
migrate = Migrate(app, db)

# 给manager添加一条操作命令
manager.add_command("db", MigrateCommand)


# 编写模型类
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))


@app.route("/")
def helloworld():
    return "hello world Flask"


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