• Flask Web——数据库


    MySQL

    以下展示在代码中连接mysql数据库的操作。
    首先需要安装mysql;之后需要在命令行中进入mysql(通过指令mysql -u root -p);进入mysql后,使用create database database_learn;创建名为database_learn的数据库;最后在代码中连接数据库,当pycharm命令行显示结果(1, )时,说明连接成果。

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    
    HOST_NAME = "127.0.0.1"
    PORT = 3306
    USERNAME = "root"
    PASSWORD = ""
    DATABASE = "database_learn"
    app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:" \
                                            f"{PASSWORD}@{HOST_NAME}:{PORT}/{DATABASE}?" \
                                            f"charset=utf8"
    
    db = SQLAlchemy(app)
    with db.engine.connect() as conn:
        rs = conn.execute("select 1")
        print(rs.fetchone())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    使用Flask-SQLAlchemy创建表

    class User(db.Model):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(100))
        password = db.Column(db.String(100))
    
    db.create_all()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    首先创建一个User类,并且它继承自db.Model类,所有ORM模型必须是db.Model的直接或间接子类。然后通过__tablename__属性,指定User模型映射到数据库中表的名称(即,待会儿使用SQLAlchemy创建的表名称就是__tablename__属性指定的名称)。接着定义三个db.Column类型的类属性,分别是id、username、password,只有使用db.Column定义的类属性,才会被映射到数据库表中称为字段。 在以上的数据库表字段定义中,将id定义为数据库类型中的整型,设置primary_key = True表示将id视为主键,传递autoincrement = True设置id自增长。username和password类型均为db.String,在数据库中表现为varchar(100)。最后通过db.create_all()把User模型映射成数据库中的表。
    在这里插入图片描述

    CRUD操作

    可以直接使用ORM模型进行CRUD(增删改查)操作(前面定义的User类即一个ORM模型,因为它直接继承自db.Model),需要先把操作添加到会话中,通过db.session可以获取会话对象。会话对象在内存中,调用db.session.commit(),可以执行对应的操作,执行db.session.rollback(),可以回滚。

    Create操作

    通过ORM模型可以实现向表中添加数据的操作,使用的方法是db.session.add(x),其中x是对应数据库某张表的ORM模型实例,如,一个User类的实例。

    @app.route('/user/add')
    def user_add():
        user1 = User(username="张三", password="111111")
        user2 = User(username="里斯", password="222222")
        user3 = User(username="汪芜", password="333333")
        db.session.add(user1)
        db.session.add(user2)
        db.session.add(user3)
        db.session.commit()
        return "用户添加成功!"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    在database_learn数据库的user表中查看三条信息是否添加:
    在这里插入图片描述
    注:在创建User对象时,必须通过关键字参数为字段赋值,否则SQLAlchemy将不知道给哪个字段赋值,从而报错。

    Read操作

    可以使用db.Model内置的query实现Read操作。query上的方法分为提取方法和过滤方法两大类,先介绍提取方法。

    @app.route('/user/fetch')
    def user_fetch():
        #👇获取user中的全部数据
        users = User.query.all()
        print(users)
        #👇使用get方法获取主键为1的数据
        user = User.query.get(1)
        print(user)
        #获取第一条数据
        user = User.query.first()
        print(user)
        return "数据提取成功!"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    将输出重定向至命令行,发现使用SQLAlchemy提取的数据仍为User类型的对象。
    使用filter和filter_by方法可以实现数据过滤操作,二者的区别在于传递的参数类型不同。filter传递查询条件,而filter_by传递关键字参数。

    @app.route('/user/filter')
    def user_filter():
        users = User.query.filter(User.username=="张三").all()
        print(users)
        users = User.query.filter_by(username="张三").all()
        print(users)
        return "数据过滤成功!"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    update操作

    update操作分为两种:

    • 一种是针对单条数据的操作,通过query的get方法取出数据(推测get方法返回的是数据对象(如,User类实例)的弱引用),在其上进行修改,再使用session.commit()提交修改结果即可。
    • 另一种是针对多条数据,需要先使用filter取出BaseQuery对象,再使用update方法进行修改,最后使用session.commit()提交结果。
    """ 👇update操作👇 """
    #👇针对一条数据的修改,直接通过get或filter取出这条数据对象,在其上进行修改(推测get的返回值是对象的引用<列表>)
    #之后再使用session进行commit即可。
    user = User.query.get(1)
    user.username = "被修改过的张三"
    db.session.commit()
    #👇针对批量修改,先使用filter或filter_by获取Basequery对象,之后调用update方法修改即可。
    User.query.filter(User.username.like("%张三%")).update({"password":User.password+"_被修改过的密码"},synchronize_session=False)
    db.session.commit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    delete操作

    仍然是针对单条数据和多条数据,操作方式与update操作类似。

    """👇delete操作👇"""
    #仍然是针对单条数据和多条数据
    #对于单条数据,直接使用session.delete()即可。
    user = User.query.get(1)
    db.session.delete(user)
    db.session.commit()
    #针对多条数据,仍然先使用filter获取Basequery对象,再对Basequery使用delete方法。
    User.query.filter(User.username.contains("张三")).delete(synchronize_session=False)
    db.session.commit()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    表关系

    外键

    👇在定义属性时,通过设置ForeignKey来指定表中哪个属性是外键,以及外键与其他表的引用关系。

    class Article(db.Model):
        __tablename__ = "article"
        id = db.column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(200),nullable=False)
        content = db.Column(db.text,nullable=False)
        #👇设置author_id为Article表的外键,引用的是user.id,必须设置author_id和其引用的user_id为相同类型
        # (同为db.Integer),否则报错
        author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    一对多关系

    建立关系

    在定义ORM模型时,可以通过加入db.relationship()属性来指定表与表之间的关联关系,如,可以为Article类加入author = db.relationship("User")来关联User对象。之后,便可以通过Article.author来访问User对象了。其底层逻辑是:Flask-SQLAlchemy会自动匹配两张表的外键关联,以寻找数据。

    class Article(db.Model):
        __tablename__ = "article"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(200),nullable=False)
        content = db.Column(db.Text,nullable=False)
        #👇设置author_id为Article表的外键,引用的是user.id,必须设置author_id和其引用的user_id为相同类型
        # (同为db.Integer),否则报错
        author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
        author = db.relationship("User")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    👆注:还需要通过执行db.create_all()来建立article这张表。
    👇通过author属性,从article实例访问外键关联的user:

    """👇通过article.author访问user,以获取article的作者的名字👇"""
    user = User.query.first()
    article = Article(title="aa", content="bb", author=user) #将user对象关联到author上
    db.session.add(article)
    db.session.commit()
    
    article = Article.query.filter_by(title="aa").first() #通过filter_by查询title为aa的记录
    print(article.author.username)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    建立双向关系

    现在可以通过article的author访问user,但是不能通过user来访问article实例,因此在user中加入外键与article类进行关联,可以实现双向访问。同时,需要设置二者在db.relationship方法中的参数back_populates

    class User(db.Model):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(100))
        password = db.Column(db.String(100))
    
        articles = db.relationship("Article",back_populates="author")
    
    class Article(db.Model):
        __tablename__ = "article"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(200),nullable=False)
        content = db.Column(db.Text,nullable=False)
        #👇设置author_id为Article表的外键,引用的是user.id,必须设置author_id和其引用的user_id为相同类型
        # (同为db.Integer),否则报错
        author_id = db.Column(db.Integer,db.ForxeignKey("user.id"))
        author = db.relationship("User",back_populates="articles")
    
    user = User.query.first()
    for article in user.articles:   #通过user的articles关联直接访问article实例
        print(article.title)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    简化关联关系

    通过只在一个ORM模型上使用db.relationship(),并定义backref参数,可以简化关联关系的定义,直接实现双向绑定。但是使用强大的backref参数也可能使庞大项目的维护人员产生困扰。

    class User(db.Model):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(100))
        password = db.Column(db.String(100))
    
        # articles = db.relationship("Article",back_populates="author")
    
    class Article(db.Model):
        __tablename__ = "article"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(200),nullable=False)
        content = db.Column(db.Text,nullable=False)
        #👇设置author_id为Article表的外键,引用的是user.id,必须设置author_id和其引用的user_id为相同类型
        # (同为db.Integer),否则报错
        author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
        author = db.relationship("User",backref="articles")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    设置一对一关系

    想要为两个关系设置为一对一关系(如,用户类和用户拓展类是纯粹的一对一关系),需要设置两项:

    • 设置db.relationship中的参数uselist = False
    • 为了在数据库层面实现一对一,还需要在设置外键时,将参数unique设置为True。
    class User(db.Model):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(100))
        password = db.Column(db.String(100))
    
        # articles = db.relationship("Article",back_populates="author")
        extension = db.relationship("UserExtension",back_populates="user",uselist=False)
       
    class UserExtension(db.Model):
        id = db.Column(db.Integer, primary_key=True,autoincrement=True)
        school = db.Column(db.String(100))
        user_id = db.Column(db.Integer, db.ForeignKey("user.id"),unique=True)
        user = db.relationship("User",back_populates="extension")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    多对多关系

    建立多对多关系需要建立一张中间表,如,建立文章Article和标签Tag的多对多关系时,需要建立一张中间表article_tag_table,设置属性article_id和tag_id,设置为联合主键,并且二者分别为Article的id的外键及Tag的id的外键。由于设置的是多对多关系,因此向Article实例中追加Tag时,使用的是append,删除时使用remove。当使用db.session.add_all()将数据加入数据库时,由于两个Tag对象均与Article对象关联,因此会一并入库。

    article_tag_table = db.Table(
        "article_tag_table",
        db.Column("article_id", db.Integer, db.ForeignKey("article.id"), primary_key=True),
        db.Column("tag_id", db.Integer, db.ForeignKey("tag.id"),primary_key=True)
        #👆设置为联合主键, 并且二者分别是两张表的外键
    )
    
    class Article(db.Model):
        __tablename__ = "article"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(200),nullable=False)
        content = db.Column(db.Text,nullable=False)
        #👇设置author_id为Article表的外键,引用的是user.id,必须设置author_id和其引用的user_id为相同类型
        # (同为db.Integer),否则报错
        author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
        author = db.relationship("User",backref="articles")
        tags = db.relationship("Tag",secondary=article_tag_table, back_populates="articles")
    
    class Tag(db.Model):
        __tablename__ = "tag"
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        name = db.Column(db.String(100))
        articles = db.relationship("Article", secondary=article_tag_table, back_populates='tags')
    
    db.create_all()
    @app.route('/many2many')
    def many2many():
        article1 = Article(title="11", content="aa")
        article2 = Article(title="22", content="bb")
    
        tag1 = Tag(name="python")
        tag2 = Tag(name="flask")
    
        article1.tags.append(tag1)
        article1.tags.append(tag2)
    
        article2.tags.append(tag1)
        article2.tags.append(tag2)
    
        db.session.add_all([article1, article2])
        db.session.commit()
        return "多对多关系创建成功!"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述

    级联

    在db.relationship()中设置cascade参数,即可实现级联,谁设置了cascade参数,谁就是父表。

    ORM模型迁移

    实际开发中,不会使用db.create_all()来做ORM的模型迁移,而借助第三方的Flask-Migrate实现。
    定义ORM模型后,执行以下代码创建迁移变量:

    db = SQLAlchemy(app)
    migrate = Migrate(app, db)
    
    • 1
    • 2

    👆对Migrate类进行了实例化,传入app和db对象,并赋值给migrate。在后续执行迁移命令时,Flask-Migrate自动读取app.py中的migrate变量,因此Migrate对象的实例必须命名为migrate。

    初始化迁移环境

    在项目根路径下执行flask db init,执行后会创建migrations文件夹。
    在这里插入图片描述
    此工作只需要做一次,后续只需要不断地生成和执行迁移脚本即可。

    生成迁移脚本

    执行flask db migrate -m "备注信息"。

    执行迁移脚本

    生成迁移脚本只是写好了要执行的内容,还需执行脚本才能将变动同步到数据库。
    执行flask db upgrade

  • 相关阅读:
    使用开源的zip.cpp和unzip.cpp实现压缩包的创建与解压(附源码)
    集合框架----源码解读LikedeHashSet篇
    为什么说MES管理系统是车间层与管理层的桥梁
    MySQL–第4关:查询用户日活数及支付金额
    django cloudflare csrf 403
    【HDFS】XXXRpcServer和ClientNamenodeProtocolServerSideTranslatorPB小记
    诞生在 KFC 的《开源之迷》:作者如何在嘈杂而开放的环境中进行创作
    禅道bug同步到飞书
    MySQL 中的 INSERT 是怎么加锁的?(荣耀典藏版)
    【实用软件】电脑wifi密码查看器
  • 原文地址:https://blog.csdn.net/fatfairyyy/article/details/126826052