目录
1、declare a mapping:... 2
2、connecting:... 2
3、create a schema:... 3
4、creating a session:... 4
5、create an instance of the mapped class:... 4
6、adding and updating:... 6
SQLAlchemy:
是一个ORM框架;
大量使用元编程;
编程时,先对象&关系映射,才能操作DB,已成为工业标准;
https://www.sqlalchemy.org/
https://docs.sqlalchemy.org/en/latest/ #Read this first
https://docs.sqlalchemy.org/en/latest/orm/tutorial.html
pip install sqlalchemy
pip show sqlalchemy
> import sqlalchemy
> sqlalchemy.__version__ #version check
开发中,一般都采用ORM框架,这样就可使用对象操作表了;
定义表映射的类,使用Column的描述器定义类属性,使用ForeignKey定义外键约束;
如果在一个对象中,想查看其它表对象的内容,就要使用relationship来定义关系;
是否使用FK?
支持,力挺派,能使数据保证完整性、一致性;
不支持,嫌弃派,开发难度增加,大量数据时影响插入、修改、删除的效率;
通常要在业务层保证数据一致性(事务);
注:
账号密码授权,若为前端用户,仅用来查数据,用grant select即可,不要grant all;
UML,统一建模语言;
navicat mysql,右键库或表,转储SQL文件,结构和数据;若仅导出结构,导出前要删除相关表中数据;
oralce中没有自增,用到sequence,from sqlalchemy import Sequence
1、declare a mapping:
创建映射:
创建基类Base,便于实体类继承;
创建实体类,Student表;
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base() #基类,创建基类,一次性的
from sqlalchemy import Column, Integer, String
class Student(Base): #实体类,declare a mapping
__tablename__ = 'student' #指定表名,必须写,防止忘记对应的表
id = Column('id', Integer, primary_key=True, autoincrement=True)) #定义属性对应字段,第1参数是字段名,如果和属性名一致可省,如果和属性名不一致要指定;Column类指定对应的字段,必须指定,Column即上例的Field;此处'id'可省,Integer为type不能省
name = Column(String(64), nullable=False)
age = Column(Integer)
def __repr__(self):
return &#39;<{} id:{} name:{} age:{}>&#39;.format(self.__class__.__name__, self.id, self.name, self.age)
__str__ &#61; __repr__
2、connecting&#xff1a;
数据库连接的事情&#xff0c;交给引擎&#xff1b;
echo&#61;True&#xff0c;引擎是否打印执行的语句&#xff0c;调试时打开很方便&#xff1b;
mysqldb的连接&#xff1a;
mysql&#43;mysqldb://
engine &#61; sqlalchemy.create_engine(&#39;mysql&#43;mysqldb://root:rootqazwsx&#64;10.113.129.2:3306/test1&#39;)
pymysql的连接&#xff1a;
mysql&#43;pymysql://
engine &#61; sqlalchemy.create_engine(&#39;mysql&#43;pymysql://root:rootqazwsx&#64;10.113.129.2:3306/test1&#39;)
https://docs.sqlalchemy.org/en/latest/core/engines.html
engine-configuration&#xff1a;
注&#xff1a;
内部使用了连接池&#xff1b;
dialect&#xff0c;方言&#xff0c;sql差异&#xff1b;
from sqlalchemy import create_engine
host &#61; &#39;10.113.129.2&#39;
port &#61; 3306
user &#61; &#39;root&#39;
password &#61; &#39;rootqazwsx&#39;
database &#61; &#39;test1&#39;
conn_str &#61; &#39;mysql&#43;pymysql://{}:{}&#64;{}:{}/{}&#39;.format(user, password, host, port, database)
# engine &#61; create_engine(&#39;mysql&#43;pymysql://root:rootqazwsx&#64;10.113.129.2:3306/test1&#39;, echo&#61;True) #
engine &#61; create_engine(conn_str, echo&#61;True) #引擎&#xff0c;管理连接池&#xff0c;connecting&#xff1b;echo&#61;True&#xff0c;执行的语句是否打印&#xff0c;可在配置文件中全局设置&#xff0c;调试时打开
3、create a schema&#xff1a;
Base.metadata.drop_all(engine) #删除继承自Base的所有表
Base.metadata.create_all(engine) #create a schema&#xff0c;创建继承自Base的所有表&#xff1b;Base.metadata中有一张表记录着所有用Base创建的实体类&#xff08;实体类继承自Base)&#xff0c;遍历所有实体类&#xff0c;将查到的定义信息填到创建表的语句中&#xff1b;engine的echo&#61;True&#xff0c;打开&#xff0c;执行后会有建表语句&#xff1b;创建表&#xff0c;共用的功能&#xff0c;而子类上是个性化的功能
注&#xff1a;
生产很少这样创建表&#xff0c;都是系统上线时由脚本生成&#xff0c;如用navicat mysql在测试里右键库或表&#xff0c;转储SQL文件&#xff0c;再导入到生产里&#xff1b;
生产很少删除表&#xff0c;废弃都不能删除&#xff1b;
4、creating a session&#xff1a;
在一个会话中操作数据库&#xff0c;会话建立在连接上&#xff0c;连接被引擎管理&#xff1b;
from sqlalchemy.orm import sessionmaker
Session &#61; sessionmaker(bind&#61;engine) #方式一&#xff1b;返回类&#xff1b;另&#xff0c;autoflush&#61;False&#xff0c;autocommit&#61;False
session &#61; Session() #实例化&#xff0c;session.add()&#xff0c;session.add_all()&#xff0c;session.commit()&#xff0c;session.rollback()&#xff0c;session.query()&#xff0c;session.cursor&#xff0c;session.execute()
# Session &#61; sessionmaker() #方式二
# session &#61; Session(bind&#61;engine)
注&#xff1a;
class sessionmaker(_SessionClassMethods):
def __init__(self, bind&#61;None, class_&#61;Session, autoflush&#61;True,
autocommit&#61;False,
expire_on_commit&#61;True,
info&#61;None, **kw):
5、create an instance of the mapped class&#xff1a;
例&#xff0c;增&#xff1a;
try:
stu1 &#61; Student()
stu1.name &#61; &#39;tom&#39; #属性赋值
stu1.age &#61; 20
# student.id &#61; 100 #有自增字段和有默认值的可不加
# session.add(stu1) #状态为pending
stu2 &#61; Student(name&#61;&#39;jerry&#39;, age&#61;18) #构造的时候传入
session.add_all([stu1, stu2]) #状态为pending
# lst &#61; []
# for i in range(10):
# stu &#61; Student()
# stu.name &#61; &#39;jessica&#39; &#43; str(i)
# stu.age &#61; 20 &#43; i
# lst.append(stu)
# session.add_all(lst)
session.commit()
except Exception as e:
print(e)
session.rollback()
finally:
pass
输出&#xff1a;
2018-10-10 17:04:18,319 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE &#39;sql_mode&#39;
2018-10-10 17:04:18,320 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,333 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2018-10-10 17:04:18,333 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,355 INFO sqlalchemy.engine.base.Engine show collation where &#96;Charset&#96; &#61; &#39;utf8&#39; and &#96;Collation&#96; &#61; &#39;utf8_bin&#39;
2018-10-10 17:04:18,355 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,371 INFO sqlalchemy.engine.base.Engine SELECT CAST(&#39;test plain returns&#39; AS CHAR(60)) AS anon_1
2018-10-10 17:04:18,371 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,382 INFO sqlalchemy.engine.base.Engine SELECT CAST(&#39;test unicode returns&#39; AS CHAR(60)) AS anon_1
2018-10-10 17:04:18,382 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,393 INFO sqlalchemy.engine.base.Engine SELECT CAST(&#39;test collated returns&#39; AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
2018-10-10 17:04:18,393 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,414 INFO sqlalchemy.engine.base.Engine DESCRIBE &#96;student&#96;
2018-10-10 17:04:18,414 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,430 INFO sqlalchemy.engine.base.Engine
DROP TABLE student
2018-10-10 17:04:18,430 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,447 INFO sqlalchemy.engine.base.Engine COMMIT
2018-10-10 17:04:18,468 INFO sqlalchemy.engine.base.Engine DESCRIBE &#96;student&#96;
2018-10-10 17:04:18,468 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,482 INFO sqlalchemy.engine.base.Engine ROLLBACK
2018-10-10 17:04:18,494 INFO sqlalchemy.engine.base.Engine
CREATE TABLE student (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
2018-10-10 17:04:18,494 INFO sqlalchemy.engine.base.Engine {}
2018-10-10 17:04:18,537 INFO sqlalchemy.engine.base.Engine COMMIT
2018-10-10 17:04:18,562 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-10-10 17:04:18,563 INFO sqlalchemy.engine.base.Engine INSERT INTO student (name, age) VALUES (%(name)s, %(age)s)
2018-10-10 17:04:18,563 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 20, &#39;name&#39;: &#39;tom&#39;}
2018-10-10 17:04:18,574 INFO sqlalchemy.engine.base.Engine INSERT INTO student (name, age) VALUES (%(name)s, %(age)s) #用了参数化查询
2018-10-10 17:04:18,574 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 18, &#39;name&#39;: &#39;jerry&#39;}
2018-10-10 17:04:18,586 INFO sqlalchemy.engine.base.Engine COMMIT
6、adding and updating&#xff1a;
CRUD操作&#xff1b;
每一个实体都有一个状态属性_sa_instance_state&#xff0c;其类型是sqlalchemy.orm.state.InstanceState&#xff0c;可使用sqlalchemy.inspect(entity)函数查看状态&#xff1b;
常见的状态有&#xff1a;
transient&#xff0c;实体类尚未加入到session中&#xff0c;同时并没有保存到数据库中&#xff1b;
pending&#xff0c;transient的实体被add()到session中&#xff0c;状态切换为pending&#xff0c;但还未flush到DB中&#xff1b;
persistent&#xff0c;session中的实体对象对应着DB中的真实记录&#xff0c;pending状态在提交成功后变为persistent状态&#xff0c;或查询成功返回的实体也是persistent状态&#xff1b;
deleted&#xff0c;实体被删除且已flush但未commit完成&#xff0c;事务提交成功了&#xff0c;实体变成detached&#xff0c;事务失败返回persistent状态&#xff1b;
detached&#xff0c;删除成功的实体进入这个状态&#xff1b;
新建一个实体&#xff0c;状态是transient临时的&#xff1b;
一旦add()后&#xff0c;由transient-->pending&#xff1b;
成功commit()后&#xff0c;由pending-->persistent&#xff1b;
成功查询返回的实体对象&#xff0c;也是persistent&#xff1b;
persistent状态的实体&#xff0c;依然是persistent状态&#xff1b;
persistent状态的实体&#xff0c;删除后&#xff0c;已flush但没commit()&#xff0c;转为deleted&#xff0c;事务成功提交&#xff0c;转为detached&#xff0c;事务提交失败&#xff0c;转为persistent&#xff1b;
只有在persistent状态的实体&#xff0c;才能delete和update&#xff0c;即删除、修改操作&#xff0c;&#xff1b;
例&#xff0c;commit()后的增&#xff1a;
try:
stu1 &#61; Student()
stu1.name &#61; &#39;tom&#39;
stu1.age &#61; 20
# student.id &#61; 100
print(stu1.id)
session.add(stu1)
# stu2 &#61; Student(name&#61;&#39;jerry&#39;, age&#61;18)
# session.add_all([stu1, stu2])
session.commit()
print(&#39;~~~~~~~~~~~~~~~~~~~~~~~~~~~&#39;)
print(&#39;&#64;&#64;&#64;&#64;&#64;&#64;&#64;&#39;, stu1.id)
stu1.age &#61; 22 #session.commit()后再改&#xff0c;会先查询
session.add(stu1) #再次session.add()和session.commit()&#xff0c;由于id为PK且自增&#xff0c;无论有无stu1.age&#61;22都会新增一条记录&#xff1b;若id不是自增&#xff0c;有stu1.age&#61;22则是update一条记录
session.commit() #始终与状态有关&#xff0c;感知到stu1有变化才会提交&#xff0c;能否提交成功看stu1有无变化&#xff0c;有变化了才提交&#xff08;id为autoincrement&#xff0c;此例只要执行就会新增一条记录&#xff09;&#xff1b;stu1主键没有值&#xff0c;就是新增&#xff0c;主键有值&#xff0c;就是找到对应的记录修改
except Exception as e:
print(e)
session.rollback()
finally:
pass
输出&#xff1a;
……
2018-10-10 21:20:13,812 INFO sqlalchemy.engine.base.Engine {}
None
2018-10-10 21:20:13,839 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-10-10 21:20:13,840 INFO sqlalchemy.engine.base.Engine INSERT INTO student (name, age) VALUES (%(name)s, %(age)s)
2018-10-10 21:20:13,841 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 20, &#39;name&#39;: &#39;tom&#39;}
2018-10-10 21:20:13,852 INFO sqlalchemy.engine.base.Engine COMMIT
~~~~~~~~~~~~~~~~~~~~~~~~~~~
2018-10-10 21:20:13,881 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-10-10 21:20:13,881 INFO sqlalchemy.engine.base.Engine SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age
FROM student
WHERE student.id &#61; %(param_1)s
2018-10-10 21:20:13,881 INFO sqlalchemy.engine.base.Engine {&#39;param_1&#39;: 16}
&#64;&#64;&#64;&#64;&#64;&#64;&#64; 16
2018-10-10 21:20:13,894 INFO sqlalchemy.engine.base.Engine UPDATE student SET age&#61;%(age)s WHERE student.id &#61; %(student_id)s
2018-10-10 21:20:13,894 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 22, &#39;student_id&#39;: 16}
2018-10-10 21:20:13,909 INFO sqlalchemy.engine.base.Engine COMMIT
例&#xff0c;简单查询&#xff1a;
try:
queryobj &#61; session.query(Student).filter(Student.id&#61;&#61;8) #query()方法将实体类传入&#xff0c;返回类对象&#xff08;是可迭代对象&#xff0c;查看源码有__iter__()&#xff09;&#xff0c;这时候并不查询&#xff0c;迭代它就执行sql来查询数据库&#xff0c;封装数据到指定类的实例&#xff1b;get()方法使用主键查询&#xff0c;返回一条传入类的一个实例
# queryobj &#61; session.query(Student) #无条件
for i in queryobj:
print(&#39;########&#39;, i)
except Exception as e:
print(e)
输出&#xff1a;
……
2018-10-10 22:42:32,931 INFO sqlalchemy.engine.base.Engine SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age
FROM student
WHERE student.id &#61; %(id_1)s
2018-10-10 22:42:32,931 INFO sqlalchemy.engine.base.Engine {&#39;id_1&#39;: 8}
########
例&#xff0c;改&#xff0c;错误示例&#xff1a;
try:
stu1 &#61; Student()
stu1.id &#61; 2 #这种不是改&#xff0c;而是是一个全新的stu1&#xff0c;如果该id已有&#xff0c;会PK冲突&#xff1b;正确改的做法&#xff0c;先查再改&#xff0c;得到PK才能改
stu1.name &#61; &#39;jerry&#39;
stu1.age &#61; 28
session.add(stu1)
session.commit()
except Exception as e:
print(e)
session.rollback()
finally:
pass
输出&#xff1a;
2018-10-11 08:07:21,772 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-10-11 08:07:21,773 INFO sqlalchemy.engine.base.Engine INSERT INTO student (id, name, age) VALUES (%(id)s, %(name)s, %(age)s)
2018-10-11 08:07:21,773 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 28, &#39;id&#39;: 2, &#39;name&#39;: &#39;jerry&#39;}
2018-10-11 08:07:21,785 INFO sqlalchemy.engine.base.Engine ROLLBACK
(pymysql.err.IntegrityError) (1062, "Duplicate entry &#39;2&#39; for key &#39;PRIMARY&#39;") [SQL: &#39;INSERT INTO student (id, name, age) VALUES (%(id)s, %(name)s, %(age)s)&#39;] [parameters: {&#39;age&#39;: 28, &#39;id&#39;: 2, &#39;name&#39;: &#39;jerry&#39;}]
例&#xff0c;改&#xff1a;
先查回来&#xff0c;修改后&#xff0c;再提交&#xff1b;
改不能改PK字段&#xff1b;
先SELECT再UPDATE&#xff1b;
try:
stu1 &#61; session.query(Student).get(2)
print(&#39;$$$$$$$&#39;, stu1)
stu1.name &#61; &#39;jowin&#39;
stu1.age &#61; 28
print(&#39;#######&#39;, stu1)
session.add(stu1)
session.commit()
except Exception as e:
print(e)
session.rollback()
finally:
pass
输出&#xff1a;
2018-10-11 08:39:56,595 INFO sqlalchemy.engine.base.Engine SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age
FROM student
WHERE student.id &#61; %(param_1)s
2018-10-11 08:39:56,595 INFO sqlalchemy.engine.base.Engine {&#39;param_1&#39;: 2}
$$$$$$$
#######
2018-10-11 08:39:56,607 INFO sqlalchemy.engine.base.Engine UPDATE student SET name&#61;%(name)s, age&#61;%(age)s WHERE student.id &#61; %(student_id)s
2018-10-11 08:39:56,608 INFO sqlalchemy.engine.base.Engine {&#39;age&#39;: 28, &#39;student_id&#39;: 2, &#39;name&#39;: &#39;jowin&#39;}
2018-10-11 08:39:56,619 INFO sqlalchemy.engine.base.Engine COMMIT
例&#xff0c;删&#xff0c;错误示例&#xff1a;
try:
stu1 &#61; Student(id&#61;2, name&#61;&#39;sam&#39;, age&#61;26)
session.delete(stu1)
session.commit()
except Exception as e:
print(e)
session.rollback()
finally:
pass
输出&#xff1a;
Instance &#39;
例&#xff0c;删&#xff1a;
正确做法&#xff0c;先查再删&#xff1b;
from sqlalchemy import inspect
try:
stu1 &#61; session.query(Student).get(2)
session.delete(stu1)
print(&#39;$$$$$$$$$$$$&#39;, inspect(stu1))
session.commit()
print(&#39;##########&#39;, inspect(stu1))
except Exception as e:
print(e)
session.rollabck()
finally:
pass
输出&#xff1a;
2018-10-11 08:52:12,317 INFO sqlalchemy.engine.base.Engine SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age
FROM student
WHERE student.id &#61; %(param_1)s
2018-10-11 08:52:12,318 INFO sqlalchemy.engine.base.Engine {&#39;param_1&#39;: 2}
$$$$$$$$$$$$
2018-10-11 08:52:12,330 INFO sqlalchemy.engine.base.Engine DELETE FROM student WHERE student.id &#61; %(id)s
2018-10-11 08:52:12,330 INFO sqlalchemy.engine.base.Engine {&#39;id&#39;: 2}
2018-10-11 08:52:12,342 INFO sqlalchemy.engine.base.Engine COMMIT
##########
例&#xff0c;删&#xff1a;
from sqlalchemy import inspect
def show(entity):
ins &#61; inspect(entity)
print(&#39;~~~~~~~~~~~~~~~&#39;, ins.transient, ins.pending, ins.persistent, ins.detached)
try:
# print(&#39;~~~~~~~~~~~~~&#39;, Student.__dict__)
stu1 &#61; session.query(Student).get(4)
session.delete(stu1)
# ins &#61; inspect(stu1)
# print(&#39;$$$$$$$$$$$$&#39;, ins)
show(stu1)
session.commit()
# ins &#61; inspect(stu1)
# print(&#39;##########&#39;, ins)
show(stu1)
except Exception as e:
print(e)
session.rollabck()
finally:
pass
输出&#xff1a;
018-10-11 14:40:28,111 INFO sqlalchemy.engine.base.Engine SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age
FROM student
WHERE student.id &#61; %(param_1)s
2018-10-11 14:40:28,111 INFO sqlalchemy.engine.base.Engine {&#39;param_1&#39;: 4}
~~~~~~~~~~~~~~~ False False True False
2018-10-11 14:40:28,126 INFO sqlalchemy.engine.base.Engine DELETE FROM student WHERE student.id &#61; %(id)s
2018-10-11 14:40:28,126 INFO sqlalchemy.engine.base.Engine {&#39;id&#39;: 4}
2018-10-11 14:40:28,152 INFO sqlalchemy.engine.base.Engine COMMIT
~~~~~~~~~~~~~~~ False False False True