Web后端学习笔记 Flask (5) 数据库
MySql数据库安装:省略
SQLAlchemy介绍和基本使用:
数据库是开发网站的基础,,在Flask中,支持的数据库有:MySql,PostgreSql,SQLite,Redis,MongoDB来写原生的语言实现功能,也可以使用高级别的数据库抽象方式,例如SQLAlchemy或者MongoEngine这样的ORM.
除此之外,还需要在python中安装pymsql,以及SQLAlchemy
SQLAlchemy可以独立于Flask使用,对数据库进行操作,可以避免写sql语句。
连接的格式:dialect+driver://username:password@host:port/database
SQLAlchemy连接数据库实例代码:
# -*- coding: utf-8 -*- from sqlalchemy import create_engine HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) conn = engine.connect() result = conn.execute("select 1") print(result.fetchone())
数据库ORM介绍:
使用SQLAlchemy可以写原生的sql语句,也可以使用数据库ORM对数据库进行操作,ORM(Object Relation MAP),及对象关系映射,即数据库表与模型对象之间的映射。将数据库中的表与python中的类映射起来。
这样主要是为了解决:
1. SQL语句重复利用率不高,且越复杂的sql语句条件越多,代码越长,会出现很多相近的SQL语句。
2. 很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要更改,就要去修改大量的sql语句,容易造成错误及遗漏
3. 写sql时容易忽略web安全问题,给未来造成隐患。
ORM优点:
1. 易用性:使用ORM做数据库开发,可以有效减少重复SQL语句,写出来的模型更加的直观,清晰
2. 性能损耗小:ORM转换成底层数据库指令会有一些开销,但是通过不断的优化,这种性能开销已经非常小,且综合考虑易用性,这种损耗是值得的。(数据:性能损耗不足5%)
3. 设计灵活:可以轻松写出复杂的查询
4. 可移植性:SQLAlchemy封装了底层的数据库实现,支持多个关系数据库引擎,可以非常容易的切换数据库。
映射的步骤:
1. 用declarative_base根据engine创建一个ORM基类Base
2. 用Base作为基类创建自定义的ORM类,定义__tablename__属性来指定创建好的表名,
3. 创建属性来映射到表中的字段,所有的属性都应该为Column类型
4. 使用Base.metadata.create_all()来将模型映射到数据库中
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Person(Base): __tablename__ = "person" # 定义表的名字 id = Column(Integer, primary_key=True, autoincrement=True) # 创建字段 name = Column(String(50)) age = Column(Integer) Base.metadata.create_all()
运行脚本,在cms数据库中创建表person:
将模型映射到数据库之后,即使改变了类中的属性重新运行代码,也不会重新映射模型。
SQLALchemy属性常用数据类型:
1. Column常用参数
default:默认值
nullable:是否可空
primary_key:是否为主键
unique:是否唯一
autoincrement:是否自动增长
name:该属性在数据库中的映射字段
onupdate: 如果数据库中某个字段被更新了,则onupdate=True的字段的值也会随着更新。(例如,一篇文章的更新时间的属性可以设置位onupdate=True, 当文章的标题被修改后,对应的更新时间也会发生变化)
2. 常用数据类型
Integer:整形,映射到数据库中是int类型
Float:浮点类型,32位,会出现精度丢失
Double:浮点类型,64位,会出现精度丢失
Boolean:传递True/False进去
DECIMAL:定点类型,不会出现精度丢失,有参数,DECIMAL(10,4), 存储10位长度的数字,精确到小数点后4位
enum:枚举类型, Enum('a', 'b', 'c')
Date:传递datetime.date()参数 (年月日)
DateTime:传递datetime.datetime()进去 (年月日时间)
Time:传递datetime.time()进去 (只有时间)
String:字符类型,使用时需要指定长度,区别于Text类型
Text:文本类型
LONGTEXT:长文本类型
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) view_cnt = Column(Integer, default=0) title = Column(String(50), nullable=False) create_time = Column(DateTime, default=datetime.now) # 创建时间 Base.metadata.drop_all() Base.metadata.create_all() artice = Article(title="Gone With Wind", view_cnt=11) db_session.add(artice) db_session.commit() # 提交
可以查询数据库中的数据:
还可以使用一个方法:
artice1 = Article(title="Gone With Wind", view_cnt=11) artice2 = Article(title="Gone With Wind", view_cnt=11) db_session.add_all([artice1, artice2]) db_session.commit() # 提交
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) view_cnt = Column(Integer, default=0) title = Column(String(50), nullable=False) create_time = Column(DateTime, default=datetime.now) # 创建时间 update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now) # create_time必须同时指定default值和onupdate值,在第一次插入数据的时候会有时间, # 在修改数据的时候也会更新时间 Base.metadata.drop_all() Base.metadata.create_all() artice1 = Article(title="Gone With Wind", view_cnt=11) artice2 = Article(title="Jane Austing") db_session.add_all([artice1, artice2]) db_session.commit() # 提交 # article = db_session.query(Article).first() # article.title = "Jane Aye" # db_session.commit() # 提交
数据展示:
现在对数据进行修改:
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) view_cnt = Column(Integer, default=0) title = Column(String(50), nullable=False) create_time = Column(DateTime, default=datetime.now) # 创建时间 update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now) # create_time必须同时指定default值和onupdate值,在第一次插入数据的时候会有时间, # 在修改数据的时候也会更新时间 # Base.metadata.drop_all() # Base.metadata.create_all() # # # artice1 = Article(title="Gone With Wind", view_cnt=11) # artice2 = Article(title="Jane Austing") # db_session.add_all([artice1, artice2]) # db_session.commit() # 提交 article = db_session.query(Article).first() # 查询数据 article.title = "Jane Aye" # 修改数据 db_session.commit() # 提交
数据展示:
可以看到,被修改的数据,title字段发生了变化,同时,update_time字段的值也随着更新了,但是update_time字段必须指定默认值,在第一次插入数据的时候使用。
query函数可查询的数据:
1. query可用参数:
/模型对象,指定查找这个模型中的所有的对象
/模型中的属性,可以指定查找某个模型的其中几个属性
/聚合函数,func.count, func.avg, func.max, func.min,func.sum
首先,在数据库中插入一些测试数据
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime import random HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) price = Column(Float, nullable=False) def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + str(self.price) # Base.metadata.drop_all() # Base.metadata.create_all() # 数据库中插入测试数据 # for cnt in range(10): # article = Article(title="article" + str(cnt), price=random.randint(25, 100)) # db_session.add(article) # db_session.commit() # 提交 # 查询数据 articles = db_session.query(Article).all() # 查询数据 # 返回article对象 for article in articles: print(article) print("-"*50) articles_title_price = db_session.query(Article.title, Article.price).all() # 返回元组 for data in articles_title_price: print(data) print("-"*50) # 聚合函数 count = db_session.query(func.count(Article.id)).all() print("共有{}条数据".format(count[0])) average_price = db_session.query(func.avg(Article.price)).all() print("平均值{}".format(average_price[0]))
filter函数实现条件过滤:
过滤是数据提取中的一个重要的功能,常用的过滤功能有:
1. equals
2. not equals
3. like 做匹配 ilike 不区分大小写
4. in
5. not in
6. is null
7. is not null
8. and
9. or
示例代码:
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func from sqlalchemy import and_, or_ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime import random HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) price = Column(Float, nullable=False) def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + str(self.price) # Base.metadata.drop_all() # Base.metadata.create_all() # 数据库中插入测试数据 # for cnt in range(10): # article = Article(title="article" + str(cnt), price=random.randint(25, 100)) # db_session.add(article) # db_session.commit() # 提交 # equal articles = db_session.query(Article).filter(Article.id == 1).all() # 查询数据 # 返回article对象 for article in articles: print(article) # not equal print("-"*50) articles = db_session.query(Article).filter(Article.title != "title2").all() # 查询数据 # 返回article对象 for article in articles: print(article) # like print("-"*50) articles_title_price = db_session.query(Article).filter(Article.title.like("shel%")).all() # 返回元组 for data in articles_title_price: print(data) # in_ print("-"*50) articles = db_session.query(Article).filter(Article.title.in_(["shell", "article3", "article1"])).all() for article in articles: print(article) # not in print("-"*50) articles = db_session.query(Article).filter(~Article.title.in_(["shell", "article3", "article1"])).all() for article in articles: print(article) # is null print("-"*50) articles = db_session.query(Article).filter(Article.title is None).all() for article in articles: print(article) # is not null print("-"*50) articles = db_session.query(Article).filter(Article.title is not None).all() for article in articles: print(article) # and 用于多个条件连接 and_也可以用 print("-"*50) articles = db_session.query(Article).filter(Article.title == "article8", Article.id == 9).all() for article in articles: print(article) # or 用于多个条件连接 print("-"*50) articles = db_session.query(Article).filter(or_(Article.title == "article8", Article.id == 2)).all() for article in articles: print(article)
如果想查看原生的SQL语句,可以不在后面加all()或者first()
外键及其四种约束
1. 外键:是多表之间进行关联的基础,SQLAlchemy同样也支持外键,同构foreignKey来实现,并且可以指定表的外键约束。
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func from sqlalchemy import Text, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime import random HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) uid = Column(Integer, ForeignKey("user.id"), nullable=False) # 创建外键 def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid) class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) Base.metadata.drop_all() Base.metadata.create_all()
使用SQLAlchemy创建外键,需要在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段即可,从表中外键的字段,必须和父表的主键字段类型保持一致。
外键约束: 由于从表会引用主表中的字段,从表中的总段记录删除,主表中的数据应该如何变化呢?
外键约束有以下几项:
RESTRICT:父表数据被删除,会阻止删除
NO ACTION:在mysql中,同RESTRICT
CASCADE:级联删除,父表中的数据删除,子表中的数据也跟随删除
SET NULL:父表数据被删除,子表数据相应的字段会被设置位null
class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False) # 创建外键 # ondelete表示的是外键的约束 def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid)
ORM层面外键的优化使用,以及一对多的关系
在两个表中查询数据:
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func from sqlalchemy import Text, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime import random HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False) # 创建外键 # ondelete表示的是外键的约束 def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid) class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) def __repr__(self): return str(self.id) + " | " + self.username article = db_session.query(Article).first() uid = article.uid user = db_session.query(User).filter(User.id == uid).first() print(article.title, article.content, user.username)
上述查找数据的方法过于繁琐,sqlAlchemy提供了更加简便和强大的方法:
一对多与多对一:
SQLAlchemy提供的relationship可以定义属性,在以后访问相关联的表的时候,就可以直接通过属性访问的方式进行访问
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func from sqlalchemy import Text, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship from datetime import datetime import random HOST_NAME = "127.0.0.1" PORT = "3306" DATABASE = "cms" USERNAME = "root" PASSWORD = "root1234" # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format( username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE ) # 创建数据库引擎 engine = create_engine(DB_URI) Base = declarative_base(engine) # 通过继承Base创建ORM模型/ 创建基类 db_session = sessionmaker(engine)() # 创建会话,才能实现增删改查 # 1. 创建ORM模型,必须继承自SQLAlchemy: # 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型 # 3. 将创建好的ORM模型映射到数据库中 class Article(Base): __tablename__ = "article" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False) # 创建外键 # ondelete表示的是外键的约束 author = relationship("User", backref="articles") # relationship 一个Article对应一个author def __repr__(self): return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid) class User(Base): __tablename__ = "user" id = Column(Integer, primary_key=True, autoincrement=True) username = Column(String(50), nullable=False) # articles也可以通过在Article中赋值backref参数实现 # articles = relationship("Article") # relationship 一个author可能对应多个Article def __repr__(self): return str(self.id) + " | " + self.username article = db_session.query(Article).first() print(article.author.username) print("-"*50) user = db_session.query(User).filter(User.id == article.uid).first() print(user.articles)
----------------------------------------------------------------------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)