木心

毕竟几人真得鹿,不知终日梦为鱼

导航

flask框架--数据库ORM框架flask-sqlalchemy

1、使用sqlalchemy的配置
2、创建模型类
3、用代码预插数据
4、常用的SQLAlchemy查询执行器
5、常用的SQLAlchemy查询过滤器
6、数据的修改和删除
7、关联查询
8、图书案例

1、使用sqlalchemy的配置    <-返回目录

  SQLALchemy实际上是对数据库的抽象,让开发者不用直接和SQL打交道,而是通过python对象来操作数据库,在舍弃一些性能开销的同时,换来的是开发效率的提升。SQLALchemy是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库的操作。flask-sqlalchemy是一个简化了SQLALchemy操作的扩展。

  安装

pip install flask-sqlalchemy
pip install flask-mysqldb -- 提供mysql驱动

  sqlalchemy 的配置

# coding:utf-8
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# sqlalchemy 的配置
app.config["SECRET_KEY"] = "TEST_SECRET_KEY"
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/db1"
# 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
# 查询时显示原始SQL语句
app.config["SQLALCHEMY_ECHO"] = True

db = SQLAlchemy(app)

 

2、创建模型类    <-返回目录

  创建模型类时,常用的列选项

选项名                  说明
primary_key            如果为True,代表表的主键
unique                 如果为True,代表该列不允许出现重复的值
index                  如果为True,为这列创建索引
nullable               如果为True,运行有空值,如果为False,不允许有空值
default                为该列定义默认值

  创建模型类Role和User

# coding:utf-8
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# sqlalchemy 的配置
app.config["SECRET_KEY"] = "TEST_SECRET_KEY"
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/db1"
# 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
# 查询时显示原始SQL语句
app.config["SQLALCHEMY_ECHO"] = True

db = SQLAlchemy(app)

# 创建模型类
class Role(db.Model):
    __tablename__ = "tb_role"
    id = db.Column(db.Integer, primary_key = True) # 整形的主键,默认设置为自增主键
    name = db.Column(db.String(32), unique =True)
    
    users = db.relationship("User", backref = "role")

class User(db.Model):
    __tablename__ = "tb_user"
    id = db.Column(db.Integer, primary_key = True) # 整形的主键,默认设置为自增主键
    name = db.Column(db.String(64), unique = True)
    email = db.Column(db.String(100), unique = True)
    password = db.Column(db.String(100))
    role_id = db.Column(db.Integer, db.ForeignKey("tb_role.id")) # 外键,存储tb_role表的id
if __name__ == "__main__":
    # app.run(host="0.0.0.0", port=8080, debug=True)
    db.drop_all()   # 清除数据库里的所有数据
    db.create_all() # 创建所有表

  db.create_all() 自动创建表,发送的SQL为:

CREATE TABLE tb_role (
        id INTEGER NOT NULL AUTO_INCREMENT,
        name VARCHAR(32),
        PRIMARY KEY (id),
        UNIQUE (name)
)

CREATE TABLE user (
        id INTEGER NOT NULL AUTO_INCREMENT,
        name VARCHAR(64),
        email VARCHAR(100),
        password VARCHAR(100),
        role_id INTEGER,
        PRIMARY KEY (id),
        UNIQUE (name),
        UNIQUE (email),
        FOREIGN KEY(role_id) REFERENCES tb_role (id)
)

 

3、用代码预插数据    <-返回目录

if __name__ == "__main__":
    db.drop_all()   # 清除数据库里的所有数据
    db.create_all() # 创建所有表

    role1 = Role(name="admin")
    role2 = Role(name="staff")
    db.session.add(role1)
    db.session.add(role2)
    db.session.commit()

    user1 = User(name='wang', email='wang@163.com', password='123456', role_id=role1.id)
    user2 = User(name='zhang', email='zhang@163.com', password='464643', role_id=role2.id)
    user3 = User(name='chen', email='chen@163.com', password='497594', role_id=role2.id)
    user4 = User(name='zhou', email='zhou@163.com', password='402994', role_id=role1.id)
    db.session.add_all([user1, user2, user3, user4])
    db.session.commit()

 

4、常用的SQLAlchemy查询执行器    <-返回目录

方法                   说明
all()                 以列表形式返回查询的所有结果
first()               返回查询的第一个结果,如果未查到,返回None
first_or_404()        返回查询的第一个结果,如果未查到,返回404
get()                 返回指定主键对应的行,如果不存在,返回None
get_or_404()          返回指定主键对应的行,如果不存在,返回404
count()               返回查询结果的数量
paginate()            返回一个Pagenate对象,它包含指定范围内的结果

  使用示例

def query():
    # 查询所有
    # li = Role.query.all()
    # print(type(li[0]))
    # print(li[0].name)

    # 查询first
    # Role.query.first()
    # print(type(r))
    # print(r.name)

    # 根据主键查询
    # r = Role.query.get(2)
    # print(type(r))
    # print(r.name)

    # 查询count
    # count = Role.query.count()
    # print(count)

    # 另外一种用法
    # li = db.session.query(Role).all()
    # print(type(li[0]))
    # print(li[0].name)

    r = db.session.query(Role).get(2)
    print(type(r))
    print(r.name)

if __name__ == "__main__":
    query()

 

5、常用的SQLAlchemy查询过滤器    <-返回目录

过滤器               说明
filter()            把过滤器添加到原查询上,返回一个新查询
filter_by()         把等值过滤器添加到原查询上,返回一个新查询
limit()             使用指定的值限定原查询返回的结果
offset()            偏移原查询返回的结果,返回一个新查询
order_by()          根据指定条件对原查询结果进行排序,返回一个新查询
group_by()          根据指定条件对原查询结果进行分组,返回一个新查询

  示例

def query():
    # li = User.query.filter_by(name="wang").all()
    # print(type(li[0]))
    # print(li[0].email)

    # li = User.query.filter_by(name="zhang", role_id=2).all()
    # print(type(li[0]))
    # print(li[0].email)

    li = User.query.filter(User.name=="zhang", User.role_id==2).all()
    print(type(li[0]))
    print(li[0].email)

if __name__ == "__main__":
    query()

 

  or 条件的拼接  from sqlalchemy import or_

li = User.query.filter(or_(User.name=="wang", User.email.endswith("163.com"))).all()
print(li)

  排序

li = User.query.order_by(User.id.desc()).all()
print(li)

  分组 from sqlalchemy import func

li = db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all()
print(li)

 

6、数据的修改和删除    <-返回目录

  修改

def update():
    # user = User.query.get(1)
    # user.name = "abc"
    # db.session.add(user)
    # db.session.commit()

    User.query.filter_by(name="abc").update({"name": "wang", "email": "abc@126.com"})
    db.session.commit()

if __name__ == "__main__":
    update()

  删除

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

 

7、关联查询    <-返回目录

# role = Role.query.get(1)
# print(role.users)

user = User.query.get(1)
print(user.role)

  因为模型类Role中配置了 users = db.relationship("User", backref="role"),所以根据主键查询User时,关联查询对应的Role。

  SQL打印信息:

2022-02-15 16:50:53,059 INFO sqlalchemy.engine.Engine SELECT tb_user.id AS tb_user_id, tb_user.name AS tb_user_name,
tb_user.email AS tb_user_email, tb_user.password AS tb_user_password, tb_user.role_id AS tb_user_role_id
FROM tb_user
WHERE tb_user.id = %s
2022-02-15 16:50:53,059 INFO sqlalchemy.engine.Engine [generated in 0.00023s] (1,)
2022-02-15 16:50:53,062 INFO sqlalchemy.engine.Engine SELECT tb_role.id AS tb_role_id, tb_role.name AS tb_role_name
FROM tb_role
WHERE tb_role.id = %s
2022-02-15 16:50:53,063 INFO sqlalchemy.engine.Engine [generated in 0.00050s] (1,)
<Role 1>

 

8、图书案例    <-返回目录

# coding:utf-8
from flask import Flask, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.config["SECRET_KEY"] = "TEST_SECRET_KEY"

# sqlalchemy 的配置
app.config["SECRET_KEY"] = "TEST_SECRET_KEY"
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:123456@127.0.0.1:3306/db1"
# 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
# 查询时显示原始SQL语句
app.config["SQLALCHEMY_ECHO"] = True

db = SQLAlchemy(app)

# 创建模型类
class Author(db.Model):
    __tablename__ = "tb_author"
    id = db.Column(db.Integer, primary_key = True) # 整形的主键,默认设置为自增主键
    name = db.Column(db.String(32), unique =True)
    
    books = db.relationship("Book", backref = "author")

class Book(db.Model):
    __tablename__ = "tb_book"
    id = db.Column(db.Integer, primary_key = True) # 整形的主键,默认设置为自增主键
    name = db.Column(db.String(64), unique = True)
    author_id = db.Column(db.Integer, db.ForeignKey("tb_author.id")) # 外键,存储tb_author表的id

# 预制数据库数据
def pre_insert_data():
    db.drop_all()
    db.create_all()

    auth1 = Author(name="罗贯中")
    auth2 = Author(name="施耐庵")
    auth3 = Author(name="吴承恩")
    auth4 = Author(name="曹雪芹")
    db.session.add_all([auth1, auth2, auth3, auth4])
    db.session.commit()

    book1 = Book(name="三国演义", author_id=auth1.id)
    book2 = Book(name="水浒传", author_id=auth2.id)
    book3 = Book(name="西游记", author_id=auth3.id)
    book4 = Book(name="红楼梦", author_id=auth4.id)
    book5 = Book(name="三国演义2", author_id=auth1.id)

    db.session.add_all([book1, book2, book3, book4, book5])
    db.session.commit()


# 定义表单模型类
class AuthorBookForm(FlaskForm):
    """自定义的表单模型类"""
    # DataRequired 验证器:保证数据必填,并且不能为空
    author_name = StringField(label="作者", validators=[DataRequired("作者不能为空")])
    book_name = StringField(label="书籍", validators=[DataRequired("书籍不能为空")])
    submit = SubmitField(label="提交")


@app.route("/index", methods = ["get", "post"])
def index():
    # 创建表单对象。如果时post请求,前端发送了数据,flask会把数据在构造form对象的时候,存放到对象中
    authorBookForm = AuthorBookForm()

    author_li = Author.query.all()
    return render_template("author_book.html", authors=author_li, authorBookForm=authorBookForm)

@app.route("/add", methods = ["post"])
def add_book():
    # 创建表单对象。如果时post请求,前端发送了数据,flask会把数据在构造form对象的时候,存放到对象中
    authorBookForm = AuthorBookForm()

    # 判断form中的数据是否合理。如果form中的数据完全满足所有的验证器,则返回真,否则返回假
    if authorBookForm.validate_on_submit():
        # 验证通过
        author_name = authorBookForm.author_name.data
        book_name = authorBookForm.book_name.data
        print("验证通过, author_name: {0}, book_name: {1}".format(author_name, book_name))

        # 根据作者名查询, 保存书籍
        author = Author.query.filter_by(name=author_name).first()
        if author == None:
            auth = Author(name=author_name)
            db.session.add(auth)
            db.session.commit()

            book = Book(name=book_name, author_id = auth.id)
            db.session.add(book)
            db.session.commit()
        else:
            book = Book(name=book_name, author_id = author.id)
            db.session.add(book)
            db.session.commit()

    return redirect(url_for("index"))

@app.route("/delete_book/<book_id>")
def delete_book(book_id):
    print("delete_book book_id: {0}".format(book_id))
    if book_id == None:
        return redirect(url_for("index"))

    book = Book.query.get(book_id)
    if book == None:
        print("查询不到书籍记录, book_id: {0}".format(book_id))
    else:
        db.session.delete(book)
        db.session.commit()

    return redirect(url_for("index"))

@app.route("/delete_author/<author_id>")
def delete_author(author_id):
    print("delete_authro author_id: {0}".format(author_id))
    if author_id == None:
        return redirect(url_for("index"))

    author = Author.query.get(author_id)
    if author == None:
        print("查询不到指定作者, author_id: {0}".format(author_id))
    else:
        # 先删除作者下面所有书籍,然后删除作者
        for book in author.books:
            db.session.delete(book)
        db.session.delete(author)
        db.session.commit()

    return redirect(url_for("index"))

if __name__ == "__main__":
    # pre_insert_data()
    app.run(host="0.0.0.0", port=8080, debug=True)

  author_book.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <form method="post" action="/add">
        {{ authorBookForm.csrf_token }}
        <!-- 作者 -->
        {{ authorBookForm.author_name.label }}:
        {{ authorBookForm.author_name }}  

        <!-- 错误提示信息 -->
        {% for msg in authorBookForm.author_name.errors %}
            {{ msg }}
        {% endfor %}
        <br/>
        
        <!-- 书籍 -->
        {{ authorBookForm.book_name.label }}:
        {{ authorBookForm.book_name }}
        {% for msg in authorBookForm.book_name.errors %}
            {{ msg }}
        {% endfor %}
        <br/>

        <!-- 提交 -->
        {{ authorBookForm.submit.label }}:
        {{ authorBookForm.submit }}

    </form>
    <hr/>

    <ul>
    {% for author in authors %}
        <li>
            作者:{{ author.name}}  &nbsp;&nbsp; <a href="/delete_author/{{author.id}}">删除</a>
            <ul>
                {% for book in author.books %}
                <li>书籍:{{ book.name }} &nbsp;&nbsp; <a href="/delete_book/{{book.id}}">删除</a></li>
                {% endfor %}
            </ul>
        </li>
    {% endfor %}
    </ul>
</body>
</html>

 

其他参考:

1)Flask-SQLAlchemy详解

posted on 2022-02-15 16:58  wenbin_ouyang  阅读(767)  评论(0编辑  收藏  举报