Flask 学习笔记
一、ORM 介绍
随着项目越来越大,采用原生 SQL 的方式在代码中会出现大量的 SQL 语句,对项目的进展非常不利。
1. SQL 语句重复利用率不高。越复杂的 SQL 语句条件越多,代码越长。会出现大量相近似的 SQL 语句。
2. 很多 SQL 语句是在业务逻辑中拼出来的。如果有数据库需要更改,就要去修改这些逻辑,很容易漏掉某些 SQL 语句的修改。
3. 写 SQL 时容易忽略 web 安全问题。
ORM ( Object Relationship Mapping ) 对象关系映射,通过 ORM 我们可以通过类的方式去操作数据库,而不用写原生的 SQL 语句。通过把表映射成 类
,把行作为 实例
,把字段作为 属性
,ORM 在执行对象操作时候最终还是会把对应的操作转换为数据库原生语句。
使用 ORM 的优点:
-
易用性:使用 ORM 做数据库的开发可以有效的减少 SQL 语句,写出来的模型也更加直观
-
设计灵活:可以轻松写出来复杂的查询
-
可移植性:SQLAlchemy 封装了底层的数据库实现,支持多个关系型数据库,包括 MySQL、SQLite
-
性能损耗小
二、ORM 的使用
- 使用 declarative_base 函数生成基类
要使用 ORM 来操作数据库,首先需要创建一个类来与对应的表进行映射。现在以 User 表来做为例子,它有自增长的 id、name、fullname、password 这些字段。值得注意的是,要使用 ORM 来操作,那么这些类都要继承declarative_base
这个函数生成的基类。
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
# 连接数据库所需的参数
HOSTNAME = "127.0.0.1"
PORT = 3306
DATABASE = "dvwa"
USERNAME = "root"
PASSWORD = "root"
# 创建数据库引擎
DB_URL = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"
engine = create_engine(DB_URL)
# 使用 declarative_base 函数生成基类
# 相当于建立了连接
Base = declarative_base(engine)
- 定义类
# ORM 操作数据库
# 创建的类需要继承上面生成的基类
class Students(Base):
# 定义表名
__tablename__ = "students"
# 定义字段: Column(数据类型, 数据的约束)
# Column 与 Integer, String 等都要先导入
# primary_key=True, autoincrement=True 主键 自增
id = Column(Integer, primary_key=True, autoincrement=True)
# nullable=False 非空 (默认可以为空)
name = Column(String(50), nullable=False)
# comment 相当于这个字段的备注
gender = Column(Integer, default=1, comment="1为男,2为女")
- 将类映射到数据库中
以上创建完和表映射的类后,还没有真正的映射到数据库当中,执行以下代码将类映射到数据库中。
Base.metadata.create_all()
- 添加数据
# 添加数据
ed_stu = Students(name="li", gender=1)
ed_stu2 = Students(name="two", gender=2)
print(ed_stu.name, ed_stu.gender)
print(ed_stu.id) # 上面设置了自增, 所以应该会自动添加 id
- 把数据添加到数据库
可以看到,name 和 gender 能正常的打印,但 id 却为 None,这是就说明数据还未真正插入到数据库中,id 是不存在的。接下来让我们把创建的数据插入到数据库中,这其中和数据库打交道的,是一个叫做Session
的对象。
from sqlalchemy.orm import sessionmaker
# bind 输入引擎参数
Session = sessionmaker()
Session.configure(bind=engine)
# 或者
# Session = sessionmaker(bind=engine)()
# 还要使 Session 变为方法, 实际是调用类中的 __call__ 将类变成方法来调用
session = Session()
# 添加
session.add(ed_stu)
print(ed_stu.id)
# 添加多条要使用 add_all 添加列表
session.add_all([ed_stu, ed_stu2])
- 提交操作
到这里为止只是把数据添加到 session 中,但还是没有真正的把数据存储到数据库中。如果需要把数据存储到数据库中,还要做一次commit
操作。
session.commit()
print(ed_stu.id)
三、ORM 增删改查
3.1、先把表建好
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
HOSTNAME = "127.0.0.1"
PORT = 3306
DATABASE = "test"
USERNAME = "root"
PASSWORD = "root"
DB_URL = f"mysql+mysqlconnector://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8"
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Article(Base):
__tablename__ = "art"
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50), nullable=False)
content = Column(String(50))
author = Column(String(50))
# 打印实例对象, 实际是调用 __str__ 方法
def __str__(self):
return f"{self.id} {self.title} '{self.content}' {self.author}"
# 把模型映射到数据库(在创建完类后执行)
Base.metadata.create_all()
Session = sessionmaker(engine)
session = Session()
3.2、增
def add_data():
ed_art = Article(title="Python", content="人生苦短, 我用 Python!", author="龟叔")
ed_art2 = Article(title="JAVA", content="hahahah", author="xxx")
# session.add(ed_art)
session.add_all([ed_art, ed_art2])
session.commit()
print("添加完成!")
if __name__ == '__main__':
add_data()
3.3、删
def delete_data():
del_data = session.query(Article).first()
session.delete(del_data)
session.commit()
print("删除完成!")
if __name__ == '__main__':
delete_data()
3.4、改
def update_data():
data = session.query(Article).first()
data.content = "先获取记录,再修改其字段值。"
session.commit()
print("修改完成!")
if __name__ == '__main__':
update_data()
3.5、查
def search_data():
# 查询所有
print("\n查询所有")
data = session.query(Article).all()
# print(data)
for item in data:
print(item)
# 查询第一条
print("\n查询第一条")
data1 = session.query(Article).first()
print(data1)
# 条件查询, 使用到 filter
print("\n条件查询")
data2 = session.query(Article).filter(Article.title == "Python").all()
for item in data2:
print(item)
# 或者使用 filter_by, 注意用一个等号就行
data3 = session.query(Article).filter_by(title="JAVA").all()
for item in data3:
print(item)
# id 查询, 使用 get 方法传关键字参数 id(一次自能传一个), 不存在则返回 None
print("\nid 查询")
data4 = session.query(Article).get(2)
data5 = session.query(Article).get(5)
print(data4)
print(data5)
if __name__ == '__main__':
search_data()
3.6、回滚
最后说一下回滚 rollback()
,假如在对一条记录进行修改的过程中,若操作发生异常则可以使数据回滚到改变前的样子。
def update_data():
data = session.query(Article).first()
data.content = "先获取记录,再修改其字段值。"
print(data.content)
session.rollback()
print(data.content)
session.commit()
查看数据库,该条数据没有发生改变。注意,回滚操作要在提交操作前,提交之后就不能回滚了。所有一般回滚操作都会结合 try ... except ...
一起使用。
type:
pass
# 若报错, 发生异常则进行回滚
except Exception as e:
session.rollback()