以下展示在代码中连接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())

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()
首先创建一个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模型映射成数据库中的表。

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

在database_learn数据库的user表中查看三条信息是否添加:

注:在创建User对象时,必须通过关键字参数为字段赋值,否则SQLAlchemy将不知道给哪个字段赋值,从而报错。
可以使用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 "数据提取成功!"

将输出重定向至命令行,发现使用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 "数据过滤成功!"

update操作分为两种:
session.commit()提交修改结果即可。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()

仍然是针对单条数据和多条数据,操作方式与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()

👇在定义属性时,通过设置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"))
在定义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")
👆注:还需要通过执行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)
现在可以通过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)
通过只在一个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")
想要为两个关系设置为一对一关系(如,用户类和用户拓展类是纯粹的一对一关系),需要设置两项:
uselist = False;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")
建立多对多关系需要建立一张中间表,如,建立文章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 "多对多关系创建成功!"

在db.relationship()中设置cascade参数,即可实现级联,谁设置了cascade参数,谁就是父表。
实际开发中,不会使用db.create_all()来做ORM的模型迁移,而借助第三方的Flask-Migrate实现。
定义ORM模型后,执行以下代码创建迁移变量:
db = SQLAlchemy(app)
migrate = Migrate(app, db)
👆对Migrate类进行了实例化,传入app和db对象,并赋值给migrate。在后续执行迁移命令时,Flask-Migrate自动读取app.py中的migrate变量,因此Migrate对象的实例必须命名为migrate。
在项目根路径下执行flask db init,执行后会创建migrations文件夹。

此工作只需要做一次,后续只需要不断地生成和执行迁移脚本即可。
执行flask db migrate -m "备注信息"。
生成迁移脚本只是写好了要执行的内容,还需执行脚本才能将变动同步到数据库。
执行flask db upgrade