python之ORM
Posted 徘徊的游鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之ORM相关的知识,希望对你有一定的参考价值。
pymysql
python操作数据库的基本步骤:
- 导入相应的python模块;
- 使用connect函数连接数据库,并返回一个connection对象;
- 通过connection对象的cursor方法,返回一个cursor对象;
- 通过cursor对象的execute方法执行SQL语句;
- 如果执行的是查询语句,通过cursor对象的fetchall语句获取返回结果;
- 调用cursor对象的close方法关闭cursor;
- 调用connection对象的close方法关闭数据库连接。
1 import pymysql 2 3 conn = pymysql.Connect(host=\'127.0.0.1\',user=\'admin\',passwd=\'admin\',db=\'test_py\') 4 cur = conn.cursor() 5 6 reCount = cur.execute(\'select * from student\') 7 print(cur.fetchall()) # ((1, \'gareth\', 22, datetime.date(2011, 9, 1)),) 8 9 cur.close() 10 conn.close()
connection类成员
- begin: 开始事务
- commit: 提交事务
- rollback: 回滚事务
- cursor: 返回一个cursor对象
- autocommit: 设置是否事务自动提交
- set_character_set: 设置字符集编码
- get_server_info: 获取数据库版本信息
注: 一般不直接调用begin,commit和roolback函数,而是通过上下文管理器实现事务的提交与回滚操作。
cursor类成员对象:cursor对象表示数据库游标,用于执行SQL语句并获取SQL语句的执行结果。
- execute: 执行SQL语句
- close:关闭游标
- fetchall:获取SQL语句的所有记录
- fetchmany:获取SQL语句的多条记录
- fetchone:获取SQL语句的一条记录
- owncount:常量,表示SQL语句的结果集中返回了多少条记录
- arraysize:变量,保存了当前获取纪录的下标
- lastrowid:获取最新自增ID
注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:
- cursor.scroll(1,mode=\'relative\') # 相对当前位置移动
- cursor.scroll(2,mode=\'absolute\') # 相对绝对位置移动
默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
1 conn = pymysql.Connect(host=\'127.0.0.1\',user=\'admin\',passwd=\'admin\',db=\'test_py\') 2 cur = conn.cursor(cursor=pymysql.cursors.DictCursor) 3 4 reCount = cur.execute(\'select * from student\') 5 print(cur.fetchall()) 6 """ 7 [{\'stu_id\': 1, \'name\': \'gareth\', \'age\': 22, \'register_data\': datetime.date(2011, 9, 1)}, 8 {\'stu_id\': 3, \'name\': \'Bob\', \'age\': 19, \'register_data\': datetime.date(2012, 2, 3)}, 9 {\'stu_id\': 4, \'name\': \'Bob\', \'age\': 19, \'register_data\': datetime.date(2012, 2, 3)}, 10 {\'stu_id\': 5, \'name\': \'Mary\', \'age\': 18, \'register_data\': datetime.date(2013, 1, 2)}] 11 """ 12 13 cur.close() 14 conn.close()
使用上下文管理管理数据库:
1 import pymysql 2 import os 3 4 5 def get_conn(**kwargs): 6 if os.getenv(\'DB\',\'MYSQL\') == \'MYSQL\': 7 return pymysql.connect(host=kwargs.get(\'host\',\'localhost\'), 8 user=kwargs.get(\'user\'), 9 passwd=kwargs.get(\'passwd\'), 10 port=kwargs.get(\'port\',3306), 11 db=kwargs.get(\'db\')) 12 13 def execute_sql(conn, sql): 14 with conn as cur: 15 cur.execute(sql) 16 17 def insert_data(conn,sname,sage,sregister): 18 INSERT_FORMAT = """insert into student (name,age,register_data) values(\'{0}\',\'{1}\',\'{2}\')""" 19 sql = INSERT_FORMAT.format(sname,sage,sregister) 20 execute_sql(conn,sql) 21 22 def main(): 23 conn = get_conn(host=\'127.0.0.1\', 24 user=\'admin\', 25 passwd=\'admin\', 26 port=3306, 27 db=\'test_py\') 28 29 try: 30 insert_data(conn,\'Bob\',19,\'2012-02-03\') 31 insert_data(conn,\'Mary\',18,\'2013-01-02\') 32 33 with conn as cur: 34 cur.execute(\'select * from student\') 35 rows = cur.fetchall() 36 for row in rows: 37 print(row) 38 finally: 39 if conn: 40 conn.close() 41 42 if __name__ == \'__main__\': 43 main()
上面例子中如果values(\'{0}\',\'{1}\',\'{2}\')的引号去掉,则会报错:pymysql.err.InternalError: (1054, "Unknown column \'jack\' in \'field list\'")
1 cur.execute("insert into student (name,age,register_data) values(\'jack\',12,\'2012-02-03\')") 2 3 INSERT_FORMAT = """insert into student (name,age,register_data) values(\'{0}\',\'{1}\',\'{2}\')"""
批量插入:
1 cur = conn.cursor() 2 3 cur.executemany("insert into student (name,age,register_data) values(%s,%s,%s)", 4 [(\'jack\',12,\'2012-02-03\'),(\'Bob\',12,\'2012-02-03\')] )
ORM
orm英文全称object relational mapping,即对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。
优点:
- 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
- ORM使我们构造固化数据结构变得简单易行。
缺点:
- 自动化意味着映射和关联管理,代价是牺牲性能
sqlalchemy
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
SQLAlchemy本身无法操作数据库,其必须依赖pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
1 MySQL-Python 2 mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> 3 4 pymysql 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
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
1 from sqlalchemy import create_engine 2 3 engine = create_engine(\'mysql+pymysql://user_account:password@127.0.0.1/test\', max_overflow=5) 4 5 cur = engine.execute("insert into student(name,age,register_date) values(\'Jack\',20,\'2018-01-02\')") 6 print(cur.lastrowid) 7 # 1 8 9 cur = engine.execute("insert into student(name,age,register_date) values(%s,%s,%s)", 10 [(\'Gareth\',18,\'2018-07-02\'),(\'Mar\',17,\'2017-12-02\')]) 11 12 cur = engine.execute("insert into student(name,age,register_date) values(%(name)s,%(age)s,%(register_date)s)", 13 name = \'Ker\',age = 21,register_date=\'2016-09-01\') 14 cur = engine.execute(\'select * from student\') 15 print(cur.fetchone()) 16 # (1, \'Jack\', 20, datetime.date(2018, 1, 2)) 17 print(cur.fetchmany(2)) 18 # [(2, \'Gareth\', 18, datetime.date(2018, 7, 2)), (3, \'Mar\', 17, datetime.date(2017, 12, 2))] 19 print(cur.fetchall()) 20 # [(4, \'Ker\', 21, datetime.date(2016, 9, 1))]
ORM使用
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
创建表:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy import Column,Integer,String,ForeignKey, UniqueConstraint,Index 3 from sqlalchemy.orm import sessionmaker, relationship 4 from sqlalchemy import create_engine 5 6 7 engine = create_engine("mysql+pymysql://user_account:password@127.0.0.1:3306/test", max_overflow = 5) 8 Base = declarative_base() # 生成一个基类 9 10 class Classes(Base): 11 __tablename__=\'classes\' 12 id = Column(Integer, primary_key=True) 13 name = Column(String(32)) 14 15 Base.metadata.create_all(engine) #创建表结构
创建与数据库的会话session:
1 session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 2 session = session_class() # 生成session实例 3 4 cla_obj = Classes(name=\'python099\') #生成你要创建的数据对象 5 6 session.add(cla_obj) # 把要创建的数据对象添加到这个session里, 一会统一创建 7 8 session.commit() # 现此才统一提交,创建数据
查询
sqlalchemy把返回的数据映射成一个对象,调用每个字段可以像调用对象属性一样。
1 session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 2 session = session_class() # 生成session实例 3 4 5 my_classes = session.query(Classes) 6 print(my_classes) 7 # "SELECT classes.id AS classes_id, classes.name AS classes_name FROM classes" 8 9 my_classes = session.query(Classes).first() 10 print(my_classes) # 返回内存对象地址 11 # <__main__.Classes object at 0x000001832CE1A4E0> 12 13 my_classes = session.query(Classes).first() 14 print(\'id: \',my_classes.id,\'name:\',my_classes.name) 15 # id: 1 name: python011 16 17 my_classes = session.query(Classes) 18 for classes in my_classes: 19 # print(\'id: \',classes.id,\'name:\',classes.name) 20 # id: 1 name: python011 21 # id: 2 name: python016 22 # id: 3 name: linux
把内存对象地址转化为可读性数据,除了调用字段还可以使用__repr__(self) 函数。
1 def __repr__(self): 2 return "Classes(id=%s,name=%s)"%(self.id, self.name) 3 4 my_classes = session.query(Classes) 5 print(my_classes) # SELECT classes.id AS classes_id, classes.name AS classes_name FROM classes 6 7 print(my_classes[1]) # Classes(id=2,name=python016) 8 9 for classes in my_classes: 10 print(classes) 11 # Classes(id=1,name=python011) 12 # Classes(id=2,name=python016) 13 # Classes(id=3,name=linux)
过滤
1 print(session.query(Classes).filter(Classes.name.in_([\'mysql\',\'python_sqlalchemy\'])).all()) 2 3 session.query(Classes).filter_by(name=\'python011\').first()
多条件查询: 相当于id > 1 and id <4
1 obj = session.query(Classes).filter(Classes.id>1).filter(Classes.id<4).all() 2 print(obj) 3 # [Classes(id=2,name=python016), Classes(id=3,name=linux)]
获取所有数据
1 print(session.query(Classes).all()) 2 # [Classes(id=1,name=python_mysql), Classes(id=2,name=python016), Classes(id=3,name=linux)] 3 print(session.query(Classes.id,Classes.name).all()) 4 # [(1, \'python_mysql\'), (2, \'python016\'), (3, \'linux\')]
查询语法
1 Common Filter Operators 2 3 Here’s a rundown of some of the most common operators used in filter(): 4 5 equals: 6 7 query.filter(User.name == \'ed\') 8 not equals: 9 10 query.filter(User.name != \'ed\') 11 LIKE: 12 13 query.filter(User.name.like(\'%ed%\')) 14 15 IN: 16 17 NOT IN: 18 query.filter(~User.name.in_([\'ed\', \'wendy\', \'jack\'])) 19 20 IS NULL: 21 22 IS NOT NULL: 23 24 AND: 25 2.1. ObjectRelationalTutorial 17 26 27 query.filter(User.name.in_([\'ed\', \'wendy\', \'jack\'])) 28 # works with query objects too: 29 query.filter(User.name.in_( session.query(User.name).filter(User.name.like(\'%ed%\')) 30 31 )) 32 33 query.filter(User.name == None) 34 # alternatively, if pep8/linters are a concern 35 query.filter(User.name.is_(None)) 36 query.filter(User.name != None) 37 # alternatively, if pep8/linters are a concern 38 query.filter(User.name.isnot(None)) 39 SQLAlchemy Documentation, Release 1.1.0b1 40 41 # use and_() 42 43 from sqlalchemy import and_ 44 query.filter(and_(User.name == \'ed\', User.fullname == \'Ed Jones\')) 45 46 # or send multiple expressions to .filter() 47 query.filter(User.name == \'ed\', User.fullname == \'Ed Jones\') 48 # or chain multiple filter()/filter_by() calls 49 query.filter(User.name == \'ed\').filter(User.fullname == \'Ed Jones\') 50 Note: Makesureyouuseand_()andnotthePythonandoperator! • OR: 51 52 Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH: 53 54 query.filter(User.name.match(\'wendy\')) 55 Note: match() uses a database-specific MATCH or CONTAINS f
分组和统计
1 print(session.query(Classes).filter(Classes.name.like(\'p%\')).count()) 2 # 2 3 print(session.query(Classes).count()) 4 # 2 5 6 from sqlalchemy import func 7 print(session.query(Classes).all()) 8 # [Classes(id=1,name=python_mysql), Classes(id=2,name=python016), Classes(id=3,name=linux), Classes(id=5,name=linux)] 9 10 print(session.query(func.count(Classes.name),Classes.name).group_by(Classes.name).all()) 11 #[(1, \'python_mysql\'), (1, \'python016\'), (2, \'linux\')]
修改
使用修改,可以加个判断,如果没有查询到需要修改的信息则跳过,否则容易出现异常 AttributeError 。
1 my_classes = session.query(Classes).filter_by(name=\'python011\').first() 2 3 my_classes.name = \'python_mysql022\' 4 session.commit() 5 my_classes = session.query(Classes) 6 for classes in my_classes: 7 print(classes) 8 9 # Classes(id=1,name=python_mysql) 10 # Classes(id=2,name=python016) 11 # Classes(id=3,name=linux)
异常:
1 Traceback (most recent call last): 2 File "C:/D/personal_data/workspace/eleven/mysql_study/class_study/orm_01.py", line 64, in <module> 3 my_classes.name = \'python_mysql022\' 4 AttributeError: \'NoneType\' object has no attribute \'name\'
回滚
1 my_classes = session.query(Classes).filter_by(id=2).first() 2 my_classes.name = \'python_sqlalchemy\' 3 4 fake_classes = Classes(name=\'mysql\') 5 session.add(fake_classes) 6 7 print(session.query(Classes).filter(Classes.name.in_([\'mysql\',\'python_sqlalchemy\'])).all()) 8 #[Classes(id=2,name=python_sqlalchemy), Classes(id=4,name=mysql)] 9 session.rollback() 10 11 print(session.query(Classes).filter(Classes.name.in_([\'mysql\',\'python_sqlalchemy\'])).all()) 12 # []
程序:
1 from sqlalchemy.ext.declarative import declarative_base 2 from sqlalchemy import Column,Integer,String,ForeignKey, UniqueConstraint,Index 3 from sqlalchemy.orm import sessionmaker, relationship 4 from sqlalchemy import create_engine 5 6 7 engine = create_engine("mysql+pymysql://jiawenyx:intel@3117@127.0.0.1:3306/test", max_overflow = 5) 8 Base = declarative_base() 9 10 class Classes(Base): 11 __tablename__=\'classes\' 12 id = Column(Integer, primary_key=True) 13 name = Column(String(32)) 14 15 def __repr__(self): 16 return "Classes(id=%s,name=%s)"%(self.id, self.name) 17 18 Base.metadata.create_all(engine) #创建表结构 19 20 session_class = sessionmaker(bind=engine) #以上是关于python之ORM的主要内容,如果未能解决你的问题,请参考以下文章