ORM sqlachemy学习
内容:
1.ORM介绍
2.SQLAlchemy介绍
3.SQLAlchemy内部处理
4.SQLAlchemy使用
参考:
http://www.cnblogs.com/wupeiqi/articles/5713330.html
http://www.cnblogs.com/alex3714/articles/5978329.html
1.ORM介绍
ORM(object relational mapping),就是对象映射关系,简单来说:
对于python而已一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过ORM将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型(类和对象)进行操作就可以了,而不用直接使用sql语言。
ORM优点:
- 隐藏了数据访问细节,“封闭”的通用数据库交互,使开发更方便快捷(数据库交互更简单)
- ORM使我们构造固化数据结构变得简单易行
ORM缺点:
无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)
现在各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。
2.SQLAlchemy介绍
(1)什么是SQLAlchemy
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
(2)SQLAlchemy原理
数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含id
和name
的user
表:
1 [ 2 ('1', 'woz'), 3 ('2', 'wyb'), 4 ('3', 'alex') 5 ]
Python的DB-API返回的数据结构就是像上面这样表示的,但是用tuple表示一行很难看出表的结构。
如果把一个tuple用class实例来表示,就可以更容易地看出表的结构来:
1 class User(object): 2 def __init__(self, id, name): 3 self.id = id 4 self.name = name 5 6 [ 7 User('1', 'wyb'), 8 User('2', 'woz'), 9 User('3', 'alex') 10 ]
这就是传说中的ORM技术:Object-Relational Mapping,就是把关系数据库的表结构映射到类的对象上
但是由谁来做这个转换呢?由专业的ORM框架来做转换,Python中最有名的ORM框架是SQLAlchemy
(3)安装
1 pip3 install SQLAlchemy
3.SQLAlchemy内部处理
(1)依赖第三方
SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
1 MySQL-Python (py2语法) 2 mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> 3 4 pymysql (py3语法) 5 mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] 6 7 MySQL-Connector 8 mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> 9 10 cx_Oracle 11 oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...] 12 13 更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
(2)内部处理
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句
4.SQLAlchemy使用
SQLAlchemy的使用本质上就是对其ORM功能的使用,详细说就是使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有组件对数据进行操作。根据类创建对象,对象转换成SQL,执行SQL
(1)创建表
创建一个表(user)并插入数据:
1 # __author__ = "wyb" 2 # date: 2018/8/20 3 from sqlalchemy.ext.declarative import declarative_base 4 from sqlalchemy.orm import sessionmaker 5 from sqlalchemy import ( 6 create_engine, 7 Column, 8 Integer, 9 String, 10 ) 11 12 # create_engine类似pymysql中的connect 13 # 设置echo将打印一系列过程信息 14 engine = create_engine("mysql+pymysql://root:root@127.0.0.1:3306/wyb", 15 encoding="utf-8", 16 echo=True, 17 max_overflow=5) 18 19 Base = declarative_base() # 生成ORM基类 20 21 22 # 接下来创建一个User表 23 class User(Base): 24 __tablename__ = 'user' # 表名 25 id = Column(Integer, primary_key=True) 26 name = Column(String(32)) 27 password = Column(String(64)) 28 29 30 Base.metadata.create_all(engine) # 创建表结构 31 32 33 # 接下来对表中数据进行操作 34 # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 35 Session_class = sessionmaker(bind=engine) 36 Session = Session_class() # 生成session实例(类似pymysql中的cursor) 37 38 # 生成数据对象(未创建,commit之后才创建) 39 user_obj = User(name="wyb", password="666") 40 user_obj2 = User(name="woz", password="3399") 41 Session.add(user_obj) # 把数据对象添加到session里, 后面统一创建 42 Session.add(user_obj2) # 把数据对象添加到session里, 后面统一创建 43 Session.commit() # 现此统一提交,创建数据
(2)操作表(增删改查)
1 # __author__ = "wyb" 2 # date: 2018/8/20 3 from sqlalchemy.ext.declarative import declarative_base 4 from sqlalchemy.orm import sessionmaker 5 from sqlalchemy import ( 6 create_engine, 7 Column, 8 Integer, 9 String, 10 ) 11 12 # create_engine类似pymysql中的connect 13 # 设置echo将打印一系列过程信息 14 engine = create_engine("mysql+pymysql://root:root@127.0.0.1:3306/wyb", 15 encoding="utf-8", 16 # echo=True, 17 max_overflow=5) 18 19 Base = declarative_base() # 生成ORM基类 20 Session_class = sessionmaker(bind=engine) # 创建session类 21 Session = Session_class() # 生成session实例(类似pymysql中的cursor) 22 23 24 # 接下来创建一个User表 25 class User(Base): 26 __tablename__ = 'user' # 表名 27 id = Column(Integer, primary_key=True) 28 name = Column(String(32)) 29 password = Column(String(64)) 30 31 # 直接输出查询结果 32 # __repr__是魔法方法 当直接输出User对象的实例会调用此方法 33 def __repr__(self): 34 return "<User(id='%s', name='%s', password='%s')>" % ( 35 self.id, self.name, self.password) 36 37 38 # 创建表结构 39 def create_table(): 40 Base.metadata.create_all(engine) 41 42 43 # 插入 44 def insert(user_obj): 45 print("插入数据:") 46 print(user_obj) 47 Session.add(user_obj) # 把数据对象添加到session里, 后面统一创建 48 Session.commit() # 现此统一提交,创建数据 49 50 51 # 查找 52 def select(): 53 print("查询数据:") 54 55 # 查询一条数据: 56 data = Session.query(User).filter_by(name="woz").first() # 不存在返回None 57 print("name=woz: ", data) 58 data = Session.query(User).filter_by(id=1).first() 59 print("id=1: ", data) 60 # print(my_user.id, my_user.name, my_user.password) # 输出具体值 61 62 # 查询所有数据: 63 print("表中所有数据: ", Session.query(User.id, User.name).all()) 64 65 # 多条件查询: 66 objs = Session.query(User).filter(User.id > 0).filter(User.id < 3).all() 67 # 上面2个filter的关系相当于 user.id >1 AND user.id <3 的效果 68 print("id为0到3之间的数据: ", objs) 69 70 71 # 删除 72 def delete(): 73 print("删除数据:") 74 result = Session.query(User).filter(User.name == 'alex').first() 75 print(result) 76 Session.delete(result) 77 Session.commit() 78 79 80 # 更新 81 def update(): 82 print("更新数据:") 83 my_user = Session.query(User).filter_by(name="wyb").first() 84 my_user.name = "wyb666" 85 my_user.password = "wyb666" 86 print(my_user) 87 Session.commit() 88 89 90 # 主程序 91 def main(): 92 create_table() 93 # 插入数据: 94 # insert(User(name="alex", password="3373")) 95 # 查询数据: 96 # select() 97 # update() 98 # delete() 99 100 101 if __name__ == '__main__': 102 main()
(3)回滚以及统计分组
回滚:
1 my_user = Session.query(User).filter_by(id=1).first() 2 my_user.name = "Jack" 3 4 fake_user = User(name='Rain', password='12345') 5 Session.add(fake_user) 6 7 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) # 这时看session里有你刚添加和修改的数据 8 Session.rollback() #此时你rollback一下 9 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) # 再查就发现刚才添加的数据没有了。 10 11 # Session.commit()
统计分组:
1 # 统计: 2 Session.query(User).filter(User.name.like("w%")).count() 3 4 # 分组 5 from sqlalchemy import func 6 print(Session.query(func.count(User.name),User.name).group_by(User.name).all() ) 7 # 相当于以下原生SQL: 8 SELECT count(user.name) AS count_1, user.name AS user_name 9 FROM user GROUP BY user.name
(4)外键关联
外键关联:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy.orm import sessionmaker, relationship 3 from sqlalchemy import ( 4 create_engine, 5 Column, 6 Integer, 7 String, 8 ForeignKey, 9 ) 10 11 engine = create_engine("mysql+pymysql://root:root@127.0.0.1:3306/wyb", 12 encoding="utf-8", 13 max_overflow=5) 14 15 Base = declarative_base() # 生成ORM基类 16 Session_class = sessionmaker(bind=engine) # 创建session类 17 Session = Session_class() # 生成session实例(类似pymysql中的cursor) 18 19 # User表 20 class User(Base): 21 __tablename__ = 'user' 22 id = Column(Integer, primary_key=True) 23 name = Column(String(32)) 24 password = Column(String(64)) 25 26 def __repr__(self): 27 return "<User(id='%s', name='%s', password='%s')>" % ( 28 self.id, self.name, self.password) 29 30 31 # Address表 32 class Address(Base): 33 __tablename__ = 'address' 34 id = Column(Integer, primary_key=True) 35 email_address = Column(String(32), nullable=False) 36 user_id = Column(Integer, ForeignKey('user.id')) # 这是外键 37 38 user = relationship("User", backref="address") # 这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项 39 40 def __repr__(self): 41 return "<Address(email_address='%s')>" % self.email_address 42 43 # 创建表结构 44 def create_table(): 45 Base.metadata.create_all(engine)
上述表创建好后,可以这样反查:
1 objs = Session.query(User).all() 2 add_objs = Session.query(Address).all() 3 for i in objs: # 通过遍历user对象反查关联的address记录 4 print(i.address) 5 for add_obj in add_objs: # 遍历add_objs里直接查关联的user对象 6 print(add_obj.user.name)
创建关联对象:
1 obj = Session.query(User).filter(User.name == 'woz').first() 2 # 查询关联对象的属性: 3 print(obj.address) 4 # 添加关联对象: 5 obj.address = [Address(email_address="wyb@126.com")] 6 Session.commit()
多外键关联:
下表中,Customer表有2个字段都关联了Address表
1 class Address(Base): 2 __tablename__ = 'address' 3 id = Column(Integer, primary_key=True) 4 street = Column(String) 5 city = Column(String) 6 state = Column(String) 7 8 class Customer(Base): 9 __tablename__ = 'customer' 10 id = Column(Integer, primary_key=True) 11 name = Column(String) 12 13 billing_address_id = Column(Integer, ForeignKey("address.id")) 14 shipping_address_id = Column(Integer, ForeignKey("address.id")) 15 16 # 这样写 会导致SQLAlchemy分不清哪个外键是对应哪个字段 会导致程序报错 17 # billing_address = relationship("Address") 18 # shipping_address = relationship("Address") 19 20 # 所以要这样写: 21 billing_address = relationship("Address", foreign_keys=[billing_address_id]) 22 shipping_address = relationship("Address", foreign_keys=[shipping_address_id])
(5)多对多联系
什么是多对多联系:学生和课程,一个课程可以有多个学生,一个学生可以有多个课程
如下所示:
1 # 多对多 2 # 一个学生可以有多个课程,一个课程可以对应多个学生 3 student_m2m_course = Table('student_m2m_course', Base.metadata, 4 Column('student_id', Integer, ForeignKey('student.id')), 5 Column('author_id', Integer, ForeignKey('course.id')), 6 ) 7 8 # 学生类 9 class Student(Base): 10 __tablename__ = 'student' 11 id = Column(Integer, primary_key=True) 12 name = Column(String(64)) 13 attach = relationship('Attach', secondary=student_m2m_course, backref='students') 14 15 def __repr__(self): 16 return "<name: %s;>" % self.name 17 18 # 课程类 19 class Course(Base): 20 __tablename__ = 'course' 21 id = Column(Integer, primary_key=True) 22 name = Column(String(32)) 23 24 def __repr__(self): 25 return "<name: %s;>" % self.name
具体操作:
1 # 关联插入 2 a1 = Student(name="12342ds") 3 a2 = Student(name="222") 4 a3 = Student(name="666") 5 6 b1 = Course(name="跟wyb学Python") 7 b2 = Course(name="跟wyb学把妹") 8 b3 = Course(name="跟wyb学装逼") 9 b4 = Course(name="跟wyb学开车") 10 11 a1.courses = [b1, b2] 12 a2.courses = [b2, b3] 13 a3.courses = [b1, b3] 14 15 Session.add_all([a1, a2, a3, b1, b2, b3, b4]) 16 Session.commit() 17 18 19 # 多对多删除 20 # 删除数据时不用管student_m2m_course这个表, sqlalchemy会自动帮你把对应的数据删除 21 student_obj = Session.query(Student).filter_by(name="12342ds").first() 22 23 course_obj = Session.query(Course).filter_by(name="跟wyb学把妹").first() 24 print(student_obj, course_obj) 25 # 以下是两种删除方法: 26 student_obj.courses.remove(course_obj) # 从课程中里删除一个学生 27 Session.delete(course_obj ) # 直接删除课程时会把这个课程和所有学生的关联关系删除 28 Session.commit()
(6)其他
处理中文:
sqlalchemy设置编码字符集一定要在数据库访问的URL上增加charset=utf8,否则数据库的连接就不是utf8的编码格式
eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)
最后内容 - 今日作业:
主题:学员管理系统
需求:
- 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下
- 讲师视图
- 管理班级,可创建班级,根据学员qq号把学员加入班级
- 可创建指定班级的上课纪录,注意一节上课纪录对应多条学员的上课纪录, 即每节课都有整班学员上, 为了纪录每位学员的学习成绩,需在创建每节上课纪录是,同时 为这个班的每位学员创建一条上课纪录
- 为学员批改成绩, 一条一条的手动修改成绩
- 学员视图
- 提交作业
- 查看作业成绩
- 一个学员可以同时属于多个班级,就像报了Linux的同时也可以报名Python一样, 所以提交作业时需先选择班级,再选择具体上课的节数
- 附加:学员可以查看自己的班级成绩排名