一、ORM简介
1.1、理解ORM
- ORM(Object Relational Mapping),对象关系映射
- 编程语言中的“虚拟对象数据库”
- 像操作Python对象一样操作数据库
1.2、ORM的重要特性
- 面向对象的编程思想,方便扩充
- 少写(几乎不写)SQL,提升开发效率
- 支持多种类型的数据库,方便切换
- ORM技术成熟,能解决绝大部分问题
1.3、ORM模型框架的选择
二、SQLAlchemy ORM模型介绍
- 数据库支持情况:PostgreSQL、MySQL、MariaDB、SQLite、Oracle、SQL Server
- Python兼容情况:cPython2.7、cPython3.6+、PyPy:2.1+
- 企业级的ORM框架
- 支持SQL中的几乎所有功能
- 分层架构:SQL构造系统、DBAPI交互层
- 支持连接池
2.1、SQLAlchemy安装
安装:pip install SQLAlchemy
验证
>>> import sqlalchemy
>>> sqlalchemy.__version__
'1.4.21'
>>>
2.2、SQLAlchemy的使用
- 第一步,开始连接数据库
- 第二步,声明ORM模型基类
- 第三步,实现ORM模型类
- 第四步,同步数据库表
2.2.1、第一步,开始连接数据库
-
延迟连接(Lazy Connecting):只有在真正操作数据库的时候才连接数据库
-
连接代码示例
from sqlalchemy import create_engine create_engine( 'mysql://root:123456@127.0.0.1:3306/school?charset=utf8', echo=True, future=True )
连接参数解释
- url(默认第一个参数)
连接到哪种类型的数据库,如:mysql
以哪种数据库连接器(驱动)来连接数据库,默认使用官方的连接器,如:mysqlclient
数据库连接参数:用户名、密码、数据库服务地址,端口等 - echo:是否输出logging日志信息,显示执行的sql语句
- future:使用SQLAlchemy 2.0 API风格
SQLAlchemy配置
- MySQL数据库URL参数格式:
mysql://user:password@host:port/db_name?charset=uft8mb4
当password中含有特殊字符时,需要进行转义
>>>from urllib.parse import quote_plus
>>>quote_plus('kx%jj5/g')
>>>'kx%25jj5%2Fg'
连接器的选择
如果加上charset报错,可以去掉,各个版本可能不一样。
- 使用mysqlclient:
mysql+mysqldb://user:password@host:port/db_name?charset=uft8mb4
- 使用pymysql:
mysql+pymysql://user:password@host:port/db_name?charset=uft8mb4
- 使用mysql-connector-python:
mysql+mysqlconnector://user:password@host:port/db_name?charset=uft8mb4
- MariaDB模式:
mariadb+pymysql://user:password@host:port/db_name?charset=uft8mb4
- 其它:aiomysql、cymysql、oursql
2.2.2、第二步,声明ORM模型基类
示例代码
from sqlalchemy.orm import declarative_base
Base = declarative_base()
2.2.3、第三步,实现ORM模型类
示例代码
from sqlalchemy import Column, Integer, String, DateTime
class Student(Base):
"""学生信息表"""
__tablename__ = 'student'
id = Column(Integer, primary_key=True)
stu_no = Column(Integer, nullable=False, comment='学号')
stu_name = Column(String(16), nullable=False, comment='姓名')
created_at = Column(DateTime, comment='创建日期')
2.2.4、 第四步,同步数据库表
- 手动创建数据库
- 创建表:
Base.metadata.create_all()
- 删除表:
Base.metadata.drop_all()
2.3、Column常用参数
- name:数据库表中对应的列的名称,默认是变量名称
- type_:该列对应的数据类型
- primary_key:是否为主键
- autoincrement:是否为自动增加,默认是“auto”,主键自动增加
- comment:数据库表中字段的注释,同步到数据库表中
- doc:注释,不会同步到数据库表中
- nullable:是否为空,默认是True
- default:指定默认值
- unique:唯一性约束,默认是False
10.index:是否是索引,默认是False
2.4、ORM模型字段类型
三、新建ORM模型
3.1、用户的地址信息表
用户表数据字典
地址表数据字典
3.2、CHAR类型的数据如何表示
-
导入:优选范围广的类型,支持多种数据库,其次再选择对应数据库的类型
from sqlalchemy.types import CHAR from sqlalchemy.dialects.mysql import TINYINT, CHAR
-
使用
phone_no = Column(CHAR(11), comment='电话号码') age = Column(TINYINT(unsigned=True), comment='年龄')
3.3、SQLAlchemy中的关联关系
- 一对多关系,外键关联,如:一个用户有多个地址列表
- 一对一关联,如:一个用户基础信息对应唯一的详细信息
- 多对多关联,使用场景不多
一对多关联关系
示例代码
class UserAddress(Base):
"""地址信息表"""
# 建立外键
user_id = Column(Integer, ForeignKey(User.id))
# 建立关联关系,backref:反向引用,User模型引用的addresses属性,可以访问address表中与用于关联的所有地址信息
user = relationship('User', backref='addresses')
一对一关联关系
示例代码
class User(Base):
"""用户信息表"""
profile = relationship('UserProfile', backref='user', uselist=False)
3.4、相关代码
包路径代码
数据库连接相关
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
SQLALCHEMY_DATABASE_URL = 'mariadb+pymysql://root:123456@127.0.0.1:3306/learn_data_base'
# 准备连接
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
echo=True,
future=True
)
# 声明ORM模型的基类
Base = declarative_base()
def create_table():
"""同步数据库表"""
Base.metadata.create_all(bind=engine)
def drop_table():
"""删除所有数据库表"""
Base.metadata.drop_all(bind=engine)
用户地址表
from datetime import datetime
from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey
from sqlalchemy.types import CHAR
from sqlalchemy.orm import relationship
from learn_orm.db_engine import Base
from learn_orm.models.user import User
class UserAddress(Base):
"""用户地址信息表"""
__tablename__ = 'account_user_address'
id = Column(Integer, primary_key=True)
area = Column(String(256), nullable=False, comment='地址')
phone = Column(CHAR(11), comment='电话号码')
remark = Column(String(512), comment='备注')
is_valid = Column(Boolean, default=True, comment='是否有效')
create_at = Column(DateTime, default=datetime.now(), comment='创建时间')
# 外键关联
user_id = Column(Integer, ForeignKey(User.id), comment='关联的用户ID')
# 建立一对多联系:关联的模型'User',backref:User模型反向关联的属性,lazy:对关联的属性值进行筛选
user = relationship('User', backref='addresses')
def __repr__(self):
return f'{self.area}_{self.phone_no}'
用户信息表
from datetime import datetime
from enum import IntEnum
from sqlalchemy import Column, String, Integer, Enum, Boolean, DateTime, ForeignKey
from sqlalchemy.dialects.mysql import TINYINT
from sqlalchemy.orm import relationship, backref
from learn_orm.db_engine import Base
class SexEnum(IntEnum):
"""
性别枚举,IntEnum: 整形枚举比较节省内存
"""
MAN = 1 # 男
WOMAN = 2 # 女
class User(Base):
"""用户信息表"""
__tablename__ = 'account_user'
id = Column(Integer, primary_key=True)
username = Column(String(32), nullable=False, unique=True, comment='用户名')
password = Column(String(512), nullable=False, comment='密码')
real_name = Column(String(16), comment='真实姓名')
sex = Column(Enum(SexEnum), default=None, comment='性别')
age = Column(TINYINT(unsigned=True), default=0, comment='年龄')
is_valid = Column(Boolean, default=True, comment='是否有效')
created_at = Column(DateTime, default=datetime.now(), comment='创建时间')
# 用户详情:建立一对一的关联关系
profile = relationship('UserProfile', backref='user', uselist=False)
def __repr__(self):
return f'{self.username}_{self.sex}'
class UserProfile(Base):
"""用户详细信息表"""
__tablename__ = 'account_user_profile'
id = Column(Integer, primary_key=True)
hobby = Column(String(255), comment='爱好')
# 外键
user_id = Column(Integer, ForeignKey(User.id), comment='关联的用户ID')
# 用户详情:建立一对一的关联关系
user = relationship('User', backref=backref('profile', uselist=False))
# profile = relationship('User', backref='profile', uselist=False)
启动
from learn_orm.models.user import User
from learn_orm.models.address import UserAddress
from learn_orm.db_engine import create_table, drop_table
if __name__ == '__main__':
# create_table()
drop_table()
四、ORM新增数据
操作步骤
- 第一步,构造ORM模型对象
- 第二步,添加到session
- 第三步,提交到数据库
- 第四步,出现异常,回滚事务
4.1、第一步,构造ORM模型对象
参考代码
user = User(
username='wangli',
password='123456',
real_name='王莉',
age=12
)
4.2、第二步,添加到session
- 添加一个对象:
session.add(user)
- 添加多个对象:
session.add_all([user1, user2, user3])
关于Session
- 建立与数据库之间的会话
- 使用Session对数据库中的数据进行增删改查
- 使用Session进行事务控制(提交与回滚)
- 使用完后需要关闭它:
session.close()
Session的创建
-
方式一:实例化Session类
from sqlalchemy.orm import Session with Session(engine) as session: session.add(User()) session.commit()
-
方式二:使用工厂函数创建
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
# begin():开启事务执行,上下文自动提交
with Session.begin() as session:
session.add(User())
4.3、第三步,提交到数据库
示例代码
with Session(engine) as session:
session.add(user_obj) # 提交一条数据
session.add_all([user1,user2,user3]) # 提交多条数据
session