python【第十二篇下】操作MySQL数据库以及ORM之 sqlalchemy

2018-09-05 10:01:46 浏览数 (1)

内容一览:

1.Python操作MySQL数据库

2. ORM sqlachemy

2.1 ORM简介

  对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。

面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。

  对象关系映射(Object-Relational Mapping)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则: 简单:以最基本的形式建模数据。 传达性:数据库结构被任何人都能理解的语言文档化。 精确性:基于数据模型创建正确标准化的结构。 典型地,建模者通过收集来自那些熟悉应用程序但不熟练的数据建模者的人的信息开发信息模型。建模者必须能够用非技术企业专家可以理解的术语在概念层次上与数据结构进行通讯。建模者也必须能以简单的单元分析信息,对样本数据进行处理。ORM专门被设计为改进这种联系。

简单的说:ORM相当于中继数据。

  通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。

orm的优点:

  1. 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
  2. ORM使我们构造固化数据结构变得简单易行。

缺点:

  1. 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。

2.2 sqlalchemy

在Python中,最有名的ORM框架是SQLAlchemy

(1)sqlalchemy的安装

pip install sqlalchemy

前提是安装了pymysql

(2)基本使用

SQL语句创建一个MySQL表是这样的:

代码语言:javascript复制
1 CREATE TABLE user (
2     id INTEGER NOT NULL AUTO_INCREMENT,
3     name VARCHAR(32),
4     password VARCHAR(64),
5     PRIMARY KEY (id)
6 )

这只是最简单的sql表,如果再加上外键关联什么的,一般程序员的脑容量是记不住那些sql语句的,于是有了orm,实现上面同样的功能,代码如下

代码语言:javascript复制
 1 #! /usr/bin/env python3
 2 # -*- coding:utf-8 -*-
 3 
 4 from sqlalchemy import create_engine
 5 from sqlalchemy.ext.declarative import declarative_base
 6 from sqlalchemy import Column, Integer, String
 7 
 8 engine = create_engine("mysql pymysql://root:Root-123@192.168.100.64/liuyouyuan?charset=utf8",echo=True)
 9 
10 Base = declarative_base()  # 生成orm基类
11 
12 class User(Base):
13     __tablename__ = 'user'  # 表名
14     id = Column(Integer, primary_key=True)
15     name = Column(String(32))
16     password = Column(String(64))
17 
18 Base.metadata.create_all(engine)  # 创建表结构

看了上面的代码是不是觉得更复杂了?这时你脑中有没有浮现黑格尔的那句“存在的就是合乎理性的。”?没有的话,你文盲。如果这个orm没毛用,就没有存在的必要了。且听我慢慢道来:

上面的创建表的方式还有一种,了解一下就行:

代码语言:javascript复制
 1 from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey
 2 from sqlalchemy.orm import mapper
 3  
 4 metadata = MetaData()
 5  
 6 user = Table('user', metadata,
 7             Column('id', Integer, primary_key=True),
 8             Column('name', String(50)),
 9             Column('fullname', String(50)),
10             Column('password', String(12))
11         )
12  
13 class User(object):
14     def __init__(self, name, fullname, password):
15         self.name = name
16         self.fullname = fullname
17         self.password = password
18  
19 mapper(User, user) #the table metadata is created separately with the Table construct, then associated with the User class via the mapper() function

最基本的表我们创建好了,那我们开始用orm创建一条数据试试:

代码语言:javascript复制
 1 #! /usr/bin/env python3
 2 # -*- coding:utf-8 -*-
 3 from sqlalchemy import create_engine
 4 from sqlalchemy.ext.declarative import declarative_base
 5 from sqlalchemy import Column, Integer, String
 6 from sqlalchemy.orm import sessionmaker
 7 
 8 engine = create_engine("mysql pymysql://root:Root-123@192.168.100.64/liuyouyuan?charset=utf8",echo=True)
 9 
10 Base = declarative_base()  # 生成orm基类
11 
12 class User(Base):
13     __tablename__ = 'user'  # 表名
14     id = Column(Integer, primary_key=True)
15     name = Column(String(32))
16     password = Column(String(64))
17 
18 Session_class = sessionmaker(bind=engine)  # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
19 Session = Session_class()  # 生成session实例
20 
21 user_obj = User(name="ZhaoLiyin", password="admin123")  # 生成你要创建的数据对象  也就是你要在user表中插入这样一条数据(这里只是一个对象)
22 print(user_obj.name, user_obj.id)  # 此时还没创建对象呢,不信你打印一下id发现还是None
23 
24 Session.add(user_obj)  # 把要创建的数据对象添加到这个session里, 一会统一创建
25 print(user_obj.name, user_obj.id)  # 此时也依然还没创建
26 Session.commit()  # 现此才统一提交,创建数据
27 print(user_obj.name, user_obj.id)  # 此时也依然还没创建

运行结果:

代码语言:javascript复制
 1 ZhaoLiyin None
 2 ZhaoLiyin None
 3 2016-10-25 11:39:53,882 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
 4 2016-10-25 11:39:53,882 INFO sqlalchemy.engine.base.Engine ()
 5 2016-10-25 11:39:53,885 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
 6 2016-10-25 11:39:53,885 INFO sqlalchemy.engine.base.Engine ()
 7 2016-10-25 11:39:53,886 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'
 8 2016-10-25 11:39:53,886 INFO sqlalchemy.engine.base.Engine ()
 9 2016-10-25 11:39:53,887 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
10 2016-10-25 11:39:53,887 INFO sqlalchemy.engine.base.Engine ()
11 2016-10-25 11:39:53,888 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
12 2016-10-25 11:39:53,888 INFO sqlalchemy.engine.base.Engine ()
13 2016-10-25 11:39:53,889 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
14 2016-10-25 11:39:53,889 INFO sqlalchemy.engine.base.Engine ()
15 2016-10-25 11:39:53,890 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
16 2016-10-25 11:39:53,892 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, password) VALUES (%s, %s)
17 2016-10-25 11:39:53,892 INFO sqlalchemy.engine.base.Engine ('ZhaoLiyin', 'admin123')
18 2016-10-25 11:39:53,893 INFO sqlalchemy.engine.base.Engine COMMIT
19 2016-10-25 11:39:53,896 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
20 2016-10-25 11:39:53,896 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.name AS user_name, user.password AS user_password 
21 FROM user 
22 WHERE user.id = %s
23 2016-10-25 11:39:53,896 INFO sqlalchemy.engine.base.Engine (2,)
24 ZhaoLiyin 2
25 
26 Process finished with exit code 0

注意代码中的三个print  对应的输出结果。明白什么时候数据才真正插入user表中。到这里真TM够了,是不是感觉很费劲才插入一条数据?别走,错过就没有下次了

查询:

代码语言:javascript复制
 1 #! /usr/bin/env python3
 2 # -*- coding:utf-8 -*-
 3 from orm_1 import User
 4 from sqlalchemy import create_engine
 5 from sqlalchemy.orm import sessionmaker
 6 
 7 engine = create_engine("mysql pymysql://root:Root-123@192.168.100.64/liuyouyuan?charset=utf8",echo=False)
 8 Session_class = sessionmaker(bind=engine)  # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
 9 Session = Session_class()  # 生成session实例
10 my_user = Session.query(User).filter_by(name="YangZi").first()
11 
12 print(my_user)
13 print(my_user.id,my_user.name,my_user.password)

输出结果:

代码语言:javascript复制
1 <orm_1.User object at 0x03AA8F50>
2 1 YangZi admin123

可以看出:sqlalchemy帮你把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样。

不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码

代码语言:javascript复制
1 def __repr__(self):
2     return "<User(name='%s',  password='%s')>" % (
3         self.name, self.password)

修改:

代码语言:javascript复制
1 my_user = Session.query(User).filter_by(name="alex").first()
2  
3 my_user.name = "Alex Li"
4  
5 Session.commit()

回滚:

代码语言:javascript复制
 1 my_user = Session.query(User).filter_by(id=1).first()
 2 my_user.name = "Jack"
 3  
 4  
 5 fake_user = User(name='Rain', password='12345')
 6 Session.add(fake_user)
 7  
 8 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() )  #这时看session里有你刚添加和修改的数据
 9  
10 Session.rollback() #此时你rollback一下
11  
12 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #再查就发现刚才添加的数据没有了。
13  
14 # Session
15 # Session.commit()

获取所有数据:

代码语言:javascript复制
1 print(Session.query(User.name,User.id).all())

多条件查询:

代码语言:javascript复制
1 objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()

上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果


统计和分组:

代码语言:javascript复制
1 Session.query(User).filter(User.name.like("Ra%")).count()

分组:

代码语言:javascript复制
1 from sqlalchemy import func
2 print(Session.query(func.count(User.name),User.name).group_by(User.name).all() )

相当于原生sql为:

代码语言:javascript复制
SELECT count(user.name) AS count_1, user.name AS user_name
FROM user GROUP BY user.name

外键关联多对一

我们创建一个addresses表,跟user表关联

一个人有多个邮箱地址,或者说多个邮箱地址对应同一个人。这就要用到多对一。

代码语言:javascript复制
 1 #! /usr/bin/env python3
 2 # -*- coding:utf-8 -*-
 3 
 4 from sqlalchemy import ForeignKey,create_engine
 5 from sqlalchemy.orm import relationship
 6 from sqlalchemy import Column, Integer, String
 7 from sqlalchemy.ext.declarative import declarative_base
 8 from sqlalchemy.orm import sessionmaker
 9 
10 
11 engine = create_engine("mysql pymysql://root:admin123@localhost/test_db?charset=utf8")
12 Base = declarative_base()  # 生成orm基类
13 
14 Session_class = sessionmaker(bind=engine)  # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
15 session = Session_class()  # 生成session实例
16 
17 
18 class User(Base):
19     __tablename__ = 'user'  # 表名
20     id = Column(Integer, primary_key=True,autoincrement=True)
21     name = Column(String(32))
22     password = Column(String(64))
23 
24     def __repr__(self):
25         return "<%s name:%s password:%s>" % (self.id,self.name,self.password)
26 
27 class Address(Base):
28     """地址表,一个user有多个邮箱地址"""
29     __tablename__ = 'addresses'
30     id = Column(Integer, primary_key=True,autoincrement=True)
31     email_address = Column(String(32), nullable=False)
32     postal_code = Column(Integer)
33     user_id = Column(Integer, ForeignKey('user.id'))     # 外键
34     user = relationship("User", backref="add")
35     # 这句只用于查询。
36         # 功能1 相当于给Address这个表添加了一个属性列user,查询时可以用.user得到对应的User对象。但是这列不能插入数据,仅用于查询。
37         # 功能2 相当于给User这个表添加了一个属性列add,查询时在user表中可以通过.add得到Address对象。
38 
39     def __repr__(self):
40         return "email-%s  postal_code-%s" % (self.email_address,self.postal_code)
41 
42 def init_db():
43     Base.metadata.create_all(engine)  # 创建表结构
44 
45 def drop_db():
46     Base.metadata.drop_all(engine)  # 删除
47 
48 
49 def inser_test_data():
50     init_db()
51     name = ["杨幂", "赵丽颖", "刘亦菲","林志玲", "汤唯", "张馨予","赵伟彤", "陈意涵", "周冬雨","林心如", "范冰冰","梁静茹"]
52     user_obj = []
53     # 向user表中插入数据
54     for i in range(0,11):
55         obj = User(name=name[i],password="admin")
56         user_obj.append(obj)
57     session.add_all(user_obj)
58     session.commit()
59 
60     # addresses表中插入数据,这里要指定外键关联的user表中的主键
61     a1 = Address(email_address="yangmi@163.com",postal_code=12345,user_id=1)
62     a2 = Address(email_address="YangMi@163.com",postal_code=12345,user_id=1)
63     a3 = Address(email_address="zhaoliying@163.com",postal_code=12340,user_id=2)
64     a4 = Address(email_address="ZhaoLiying@163.com",postal_code=12340,user_id=2)
65     session.add_all([a1,a2,a3,a4])
66     session.commit()
67     print("Test data is inserted... ")
68 
69 # inser_test_data()
70 
71 add_obj = session.query(Address).filter_by(email_address="yangmi@163.com").first()
72 print(add_obj.user)  # 通过addresses表查询user
73 #  <1 name:杨幂 password:admin>
74 
75 
76 user_obj = session.query(User).filter_by(id=2).first()   # 这样是取第一个对象
77 print(user_obj.add)  # 通过user表查询对应的地址
78 # [email-zhaoliying@163.com  postal_code-12340, email-ZhaoLiying@163.com  postal_code-12340]
79 print(user_obj.add[1].email_address)
80 # ZhaoLiying@163.com
81 
82 # session会话    query查询    filter过滤
83 # 这里filter的用法可以这样  filter(User.id>1)  filter(User.id==1)  filter_by(id=1)

多对多:

现实生活中  

一个班级或者一门课程 可以对应多个学生

一个学生可以有多门课程或者报了多个班级

这就要用到多对多

grade表:

代码语言:javascript复制
mysql> select * from grade;
 ---- -------- 
| id | name   |
 ---- -------- 
|  1 | Python |
|  2 | Linux  |
|  3 | Go     |
 ---- -------- 
3 rows in set (0.00 sec)

student表:

grade_student表:

代码语言:javascript复制
mysql> select * from grade_student;
 ---------- ------------ 
| grade_id | student_id |
 ---------- ------------ 
|        1 |         10 |
|        2 |         10 |
|        1 |         17 |
|        2 |         17 |
|        3 |         17 |
|        1 |         15 |
|        2 |         15 |
|        3 |         15 |
|        1 |          4 |
|        2 |          4 |
|        1 |          8 |
|        2 |          8 |
|        1 |          9 |
|        2 |          9 |
|        1 |         12 |
|        2 |         12 |
|        1 |         19 |
|        2 |         19 |
|        3 |         19 |
|        1 |         20 |
|        2 |         20 |
|        3 |         20 |
|        1 |          5 |
|        2 |          5 |
|        1 |         14 |
|        2 |         14 |
|        1 |         18 |
|        2 |         18 |
|        3 |         18 |
|        1 |          2 |
|        2 |          2 |
|        1 |         11 |
|        2 |         11 |
|        1 |          1 |
|        2 |          1 |
|        1 |          3 |
|        2 |          3 |
|        1 |         13 |
|        2 |         13 |
|        1 |          7 |
|        2 |          7 |
|        1 |          6 |
|        2 |          6 |
|        1 |         16 |
|        2 |         16 |
|        3 |         16 |
 ---------- ------------ 
46 rows in set (0.00 sec)

完整代码示例:

代码语言:javascript复制
  1 #! /usr/bin/env python3
  2 # -*- coding:utf-8 -*-
  3 
  4 from sqlalchemy import ForeignKey,create_engine
  5 from sqlalchemy.orm import relationship
  6 from sqlalchemy import Column, Integer, String
  7 from sqlalchemy.ext.declarative import declarative_base
  8 from sqlalchemy.orm import sessionmaker
  9 
 10 
 11 engine = create_engine("mysql pymysql://root:admin123@localhost/test_db?charset=utf8")
 12 Base = declarative_base()  # 生成orm基类
 13 
 14 Session_class = sessionmaker(bind=engine)  # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
 15 session = Session_class()  # 生成session实例
 16 
 17 
 18 #! /usr/bin/env python3
 19 # -*- coding:utf-8 -*-
 20 
 21 from sqlalchemy import Table, Column, Integer,String, ForeignKey
 22 from sqlalchemy.orm import relationship
 23 from sqlalchemy.ext.declarative import declarative_base
 24 
 25 
 26 from sqlalchemy import create_engine
 27 from sqlalchemy.orm import sessionmaker
 28 
 29 
 30 engine = create_engine("mysql pymysql://root:admin123@localhost/test_db?charset=utf8")
 31 Base = declarative_base()
 32 
 33 SessionCls = sessionmaker(bind=engine)  # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
 34 session = SessionCls()
 35 
 36 grade_m2m_student = Table('grade_student', Base.metadata,
 37                                  Column('grade_id', Integer, ForeignKey('grade.id')),
 38                                  Column('student_id', Integer, ForeignKey('student.id')),
 39                                  )
 40 
 41 class Grade(Base):            # 定义班级表
 42     """班级表"""
 43     __tablename__ = 'grade'    # 表名
 44     id = Column(Integer, primary_key=True,autoincrement=True)
 45     name = Column(String(32))
 46 
 47     def __repr__(self):
 48         return "<Grade->id:%s name:%s>" % (self.id,self.name)
 49 
 50 class Student(Base):
 51     """学生表"""
 52     __tablename__ = 'student'
 53     id = Column(Integer, primary_key=True,autoincrement=True)
 54     name = Column(String(32))
 55     qq = Column(String(32))
 56     grades = relationship("Grade",secondary=grade_m2m_student,backref="students")
 57 
 58     def __repr__(self):
 59         return "<Student-->id:%s name:%s qq:%s>" % (self.id,self.name,self.qq)
 60 
 61 def init_db():
 62     Base.metadata.create_all(engine)  # 创建表结构
 63 
 64 def drop_db():
 65     Base.metadata.drop_all(engine)  # 删除
 66 
 67 def create_grade(name):
 68     obj = Grade(name=name)
 69     return obj
 70 
 71 def create_student(name,qq):
 72     obj = Student(name=name,qq=qq)
 73     return obj
 74 
 75 
 76 # drop_db()  # 删除表结构
 77 # init_db()   # 创建表结构
 78 #
 79 # # 创建三个班级
 80 # grade_obj = []
 81 # grades = ["Python","Linux","Go"]
 82 # for grade in grades:
 83 #     obj = create_grade(grade)
 84 #     grade_obj.append(obj)
 85 # session.add_all(grade_obj)
 86 # session.commit()
 87 #
 88 # # 添加多个学生
 89 # stu_obj = []
 90 # students = [("杨幂","10001"),("赵丽颖","10002"),("刘亦菲","10003"),("胡歌","10004"),("勒布朗","10005"),("科比","10006"),("布兰妮","10007"),("林志玲","10008"),
 91 #              ("汤唯", "10009"),("张馨予","10010"),("赵伟彤","10011"),("李四","10012"),("王宝强","10013"),
 92 #              ("陈意涵", "10014"),("周冬雨","10015"),("林心如","10016"),("范冰冰","10017"),("梁静茹","10018"),("武藤兰","10019"),("小苍","10020"),]
 93 # for i in range(0,14):
 94 #     obj = create_student(students[i][0],students[i][1])
 95 #     obj.grades = [grade_obj[0],grade_obj[1]]  # 为学生关联班级
 96 #     stu_obj.append(obj)
 97 # for j in range(14,20):
 98 #     obj = create_student(students[j][0], students[j][1])
 99 #     obj.grades = grade_obj   # 为学生关联班级
100 #     stu_obj.append(obj)
101 # session.add_all(stu_obj)
102 # session.commit()
103 # print("ok...")
104 
105 # 从grade表中通过.students查询Python班 所有的学生
106 grade_obj = session.query(Grade).filter_by(name="Python").first()
107 for stu in grade_obj.students:
108     print(stu)
109 print("----------------------------------------------------------")
110 
111 # 从stu 表中 通过.grades查询 id为4的学生所在的 所有班级
112 student_obj = session.query(Student).filter_by(id=4).first()
113 print(student_obj.grades)
114 
115 
116 # 运行结果:
117 # <Student-->id:10 name:张馨予 qq:10010>
118 # <Student-->id:17 name:范冰冰 qq:10017>
119 # <Student-->id:15 name:周冬雨 qq:10015>
120 # <Student-->id:4 name:胡歌 qq:10004>
121 # <Student-->id:8 name:林志玲 qq:10008>
122 # <Student-->id:9 name:汤唯 qq:10009>
123 # <Student-->id:12 name:李四 qq:10012>
124 # <Student-->id:19 name:武藤兰 qq:10019>
125 # <Student-->id:20 name:小苍 qq:10020>
126 # <Student-->id:5 name:勒布朗 qq:10005>
127 # <Student-->id:14 name:陈意涵 qq:10014>
128 # <Student-->id:18 name:梁静茹 qq:10018>
129 # <Student-->id:2 name:赵丽颖 qq:10002>
130 # <Student-->id:11 name:赵伟彤 qq:10011>
131 # <Student-->id:1 name:杨幂 qq:10001>
132 # <Student-->id:3 name:刘亦菲 qq:10003>
133 # <Student-->id:13 name:王宝强 qq:10013>
134 # <Student-->id:7 name:布兰妮 qq:10007>
135 # <Student-->id:6 name:科比 qq:10006>
136 # <Student-->id:16 name:林心如 qq:10016>
137 # ----------------------------------------------------------
138 # [<Grade->id:1 name:Python>, <Grade->id:2 name:Linux>]
代码语言:javascript复制

0 人点赞