python走起之第十二话
1. ORM介绍
orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。
orm的优点:
- 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
- ORM使我们构造固化数据结构变得简单易行。
缺点:
- 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。
2. sqlalchemy安装
在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstack
Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
MySQL-Python mysql+mysqldb: // <user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql: // <username>:<password>@<host>/<dbname>[?<options>] MySQL-Connector mysql+mysqlconnector: // <user>:<password>@<host>[:<port>]/<dbname> cx_Oracle oracle+cx_oracle: //user :pass@host:port /dbname [?key=value&key=value...] 更多详见:http: //docs .sqlalchemy.org /en/latest/dialects/index .html |
安装sqlalchemy
1
|
pip install SQLAlchemy<br><br>pip install pymysql #由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互 |
3.sqlalchemy基本使用
下面就开始让你见证orm的nb之处,盘古开天劈地之前,我们创建一个表是这样的
1
2
3
4
5
6
|
CREATE TABLE user ( id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR (32), password VARCHAR (64), PRIMARY KEY (id) ) |
这只是最简单的sql表,如果再加上外键关联什么的,一般程序员的脑容量是记不住那些sql语句的,于是有了orm,实现上面同样的功能,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String engine = create_engine( "mysql+pymysql://root:alex3714@localhost/testdb" , encoding = 'utf-8' , echo = True ) Base = declarative_base() #生成orm基类 class User(Base): __tablename__ = 'user' #表名 id = Column(Integer, primary_key = True ) name = Column(String( 32 )) password = Column(String( 64 )) Base.metadata.create_all(engine) #创建表结构 |
你说,娘那个腚的,并没有感觉代码量变少啊,呵呵, 孩子莫猴急,好戏在后面
Lazy Connecting
The Engine, when first returned by create_engine(), has not actually tried to connect to the database yet; that happens only the first time it is asked to perform a task against the database.
除上面的创建之外,还有一种创建表的方式,虽不学用,但还是看看吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey from sqlalchemy.orm import mapper metadata = MetaData() user = Table( 'user' , metadata, Column( 'id' , Integer, primary_key = True ), Column( 'name' , String( 50 )), Column( 'fullname' , String( 50 )), Column( 'password' , String( 12 )) ) class User( object ): def __init__( self , name, fullname, password): self .name = name self .fullname = fullname self .password = password mapper(User, user) #the table metadata is created separately with the Table construct, then associated with the User class via the mapper() function |
最基本的表我们创建好了,那我们开始用orm创建一条数据试试
1
2
3
4
5
6
7
8
9
10
11
|
Session_class = sessionmaker(bind = engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 Session = Session_class() #生成session实例 user_obj = User(name = "alex" ,password = "alex3714" ) #生成你要创建的数据对象 print (user_obj.name,user_obj. id ) #此时还没创建对象呢,不信你打印一下id发现还是None Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建 print (user_obj.name,user_obj. id ) #此时也依然还没创建 Session.commit() #现此才统一提交,创建数据 |
我擦,写这么多代码才创建一条数据,你表示太tm的费劲了,正要转身离开,我拉住你的手不放开,高潮还没到。。
查询
1
2
|
my_user = Session.query(User).filter_by(name = "alex" ).first() print (my_user) |
此时你看到的输出是这样的应该
1
|
<__main__.User object at 0x105b4ba90 > |
我擦,这是什么?这就是你要的数据呀, 只不过sqlalchemy帮你把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样啦,like this..
1
2
3
4
|
print (my_user. id ,my_user.name,my_user.password) 输出 1 alex alex3714 |
不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码
1
2
3
|
def __repr__( self ): return "<User(name='%s', password='%s')>" % ( self .name, self .password) |
修改
1
2
3
4
5
|
my_user = Session.query(User).filter_by(name = "alex" ).first() my_user.name = "Alex Li" Session.commit() |
回滚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
my_user = Session.query(User).filter_by( id = 1 ).first() my_user.name = "Jack" fake_user = User(name = 'Rain' , password = '12345' ) Session.add(fake_user) print (Session.query(User). filter (User.name.in_([ 'Jack' , 'rain' ])). all () ) #这时看session里有你刚添加和修改的数据 Session.rollback() #此时你rollback一下 print (Session.query(User). filter (User.name.in_([ 'Jack' , 'rain' ])). all () ) #再查就发现刚才添加的数据没有了。 # Session # Session.commit() |
获取所有数据
1
|
print (Session.query(User.name,User. id ). all () ) |
多条件查询
1
|
objs = Session.query(User). filter (User. id > 0 ). filter (User. id < 7 ). all () |
上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果
统计和分组
1
|
Session.query(User). filter (User.name.like( "Ra%" )).count() |
分组
1
2
|
from sqlalchemy import func print (Session.query(func.count(User.name),User.name).group_by(User.name). all () ) |
相当于原生sql为
输出为
[(1, 'Jack'), (2, 'Rain')]
外键关联
我们创建一个addresses表,跟user表关联
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from sqlalchemy import ForeignKey from sqlalchemy.orm import relationship class Address(Base): __tablename__ = 'addresses' id = Column(Integer, primary_key = True ) email_address = Column(String( 32 ), nullable = False ) user_id = Column(Integer, ForeignKey( 'user.id' )) user = relationship( "User" , backref = "addresses" ) #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项 def __repr__( self ): return "<Address(email_address='%s')>" % self .email_address |
The
relationship.back_populates
parameter is a newer version of a very common SQLAlchemy feature calledrelationship.backref
. Therelationship.backref
parameter hasn’t gone anywhere and will always remain available! Therelationship.back_populates
is the same thing, except a little more verbose and easier to manipulate. For an overview of the entire topic, see the section Linking Relationships with Backref.
表创建好后,我们可以这样反查试试
1
2
3
4
5
6
|
obj = Session.query(User).first() for i in obj.addresses: #通过user对象反查关联的addresses记录 print (i) addr_obj = Session.query(Address).first() print (addr_obj.user.name) #在addr_obj里直接查关联的user表 |
创建关联对象
1
2
3
4
5
6
7
8
|
obj = Session.query(User). filter (User.name = = 'rain' ). all ()[ 0 ] print (obj.addresses) obj.addresses = [Address(email_address = "r1@126.com" ), #添加关联对象 Address(email_address = "r2@126.com" )] Session.commit() |
常用查询语法
Common Filter Operators
Here’s a rundown of some of the most common operators used in filter():
equals:
query.filter(User.name == 'ed')
not equals:
query.filter(User.name != 'ed')
LIKE:
query.filter(User.name.like('%ed%'))
IN:
NOT IN:
query.filter(~User.name.in_(['ed', 'wendy', 'jack']))IS NULL:
IS NOT NULL:
AND:
2.1. ObjectRelationalTutorial 17
query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:
query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))
))
query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))
SQLAlchemy Documentation, Release 1.1.0b1
# use and_()
from sqlalchemy import and_
query.filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))
# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:
Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:
query.filter(User.name.match('wendy'))
Note: match() uses a database-specific MATCH or CONTAINS f
4.多外键关联
One of the most common situations to deal with is when there are more than one foreign key path between two tables.
Consider a Customer
class that contains two foreign keys to an Address
class:
下表中,Customer表有2个字段都关联了Address表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
from sqlalchemy import Integer, ForeignKey, String, Column from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship Base = declarative_base() class Customer(Base): __tablename__ = 'customer' id = Column(Integer, primary_key = True ) name = Column(String) billing_address_id = Column(Integer, ForeignKey( "address.id" )) shipping_address_id = Column(Integer, ForeignKey( "address.id" )) billing_address = relationship( "Address" ) shipping_address = relationship( "Address" ) class Address(Base): __tablename__ = 'address' id = Column(Integer, primary_key = True ) street = Column(String) city = Column(String) state = Column(String) |
创建表结构是没有问题的,但你Address表中插入数据时会报下面的错
1
2
3
4
5
6
|
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent / child tables on relationship Customer.billing_address - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table. |
解决办法如下
1
2
3
4
5
6
7
8
9
10
|
class Customer(Base): __tablename__ = 'customer' id = Column(Integer, primary_key = True ) name = Column(String) billing_address_id = Column(Integer, ForeignKey( "address.id" )) shipping_address_id = Column(Integer, ForeignKey( "address.id" )) billing_address = relationship( "Address" , foreign_keys = [billing_address_id]) shipping_address = relationship( "Address" , foreign_keys = [shipping_address_id]) |
这样sqlachemy就能分清哪个外键是对应哪个字段了
5.多对多关系
现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是
- 一本书可以有好几个作者一起出版
- 一个作者可以写好几本书
此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为
当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low b了,改书名还得都改。。。
那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了
这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联
用orm如何表示呢?
orm 多对多
接下来创建几本书和作者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
Session_class = sessionmaker(bind = engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 s = Session_class() #生成session实例 b1 = Book(name = "跟Alex学Python" ) b2 = Book(name = "跟Alex学把妹" ) b3 = Book(name = "跟Alex学装逼" ) b4 = Book(name = "跟Alex学开车" ) a1 = Author(name = "Alex" ) a2 = Author(name = "Jack" ) a3 = Author(name = "Rain" ) b1.authors = [a1,a2] b2.authors = [a1,a2,a3] s.add_all([b1,b2,b3,b4,a1,a2,a3]) s.commit() |
此时,手动连上mysql,分别查看这3张表,你会发现,book_m2m_author中自动创建了多条纪录用来连接book和author表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
mysql> select * from books; + - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + | id | name | pub_date | + - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + | 1 | 跟Alex学Python | NULL | | 2 | 跟Alex学把妹 | NULL | | 3 | 跟Alex学装逼 | NULL | | 4 | 跟Alex学开车 | NULL | + - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + 4 rows in set ( 0.00 sec) mysql> select * from authors; + - - - - + - - - - - - + | id | name | + - - - - + - - - - - - + | 10 | Alex | | 11 | Jack | | 12 | Rain | + - - - - + - - - - - - + 3 rows in set ( 0.00 sec) mysql> select * from book_m2m_author; + - - - - - - - - - + - - - - - - - - - - - + | book_id | author_id | + - - - - - - - - - + - - - - - - - - - - - + | 2 | 10 | | 2 | 11 | | 2 | 12 | | 1 | 10 | | 1 | 11 | + - - - - - - - - - + - - - - - - - - - - - + 5 rows in set ( 0.00 sec) |
此时,我们去用orm查一下数据
1
2
3
4
5
6
7
8
9
|
print ( '--------通过书表查关联的作者---------' ) book_obj = s.query(Book).filter_by(name = "跟Alex学Python" ).first() print (book_obj.name, book_obj.authors) print ( '--------通过作者表查关联的书---------' ) author_obj = s.query(Author).filter_by(name = "Alex" ).first() print (author_obj.name , author_obj.books) s.commit() |
输出如下
1
2
3
4
|
- - - - - - - - 通过书表查关联的作者 - - - - - - - - - 跟Alex学Python [Alex, Jack] - - - - - - - - 通过作者表查关联的书 - - - - - - - - - Alex [跟Alex学把妹, 跟Alex学Python] |
牛逼了我的哥!!完善实现多对多
多对多删除
删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除
通过书删除作者
1
2
3
4
5
6
|
author_obj = s.query(Author).filter_by(name = "Jack" ).first() book_obj = s.query(Book).filter_by(name = "跟Alex学把妹" ).first() book_obj.authors.remove(author_obj) #从一本书里删除一个作者 s.commit() |
直接删除作者
删除作者时,会把这个作者跟所有书的关联关系数据也自动删除
1
2
3
4
|
author_obj = s.query(Author).filter_by(name = "Alex" ).first() # print(author_obj.name , author_obj.books) s.delete(author_obj) s.commit() |
python走起之第十二话的更多相关文章
- python走起之第十一话
Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...
- python走起之第十五话
CSS Positioning(定位) 定位有时很棘手! 决定显示在前面的元素! 元素可以重叠! Positioning(定位) CSS定位属性允许你为一个元素定位.它也可以将一个元素放在另一个元素后 ...
- python走起之第十四话
HTML 标题 HTML 标题(Heading)是通过<h1> - <h6> 标签来定义的. 实例 <h1>这是一个标题</h1> <h2> ...
- Python之路【第十九篇】:爬虫
Python之路[第十九篇]:爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用 ...
- Python之路【第十八篇】:Web框架们
Python之路[第十八篇]:Web框架们 Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...
- Python之路【第十六篇】:Django【基础篇】
Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了O ...
- Python之路【第十五篇】:Web框架
Python之路[第十五篇]:Web框架 Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1 2 3 4 5 6 ...
- Python之路【第二十篇】:待更新中.....
Python之路[第二十篇]:待更新中.....
- Python之路【第十四篇】:AngularJS --暂无内容-待更新
Python之路[第十四篇]:AngularJS --暂无内容-待更新
随机推荐
- iar调试
我们可以自己建立自己的工程了,但这一步只是开发中的第一小步.今天就来说说开发中举足轻重的另外一件事:调试. 其实调试本身也并不难,楼主总结,调试关键在于两件事,一是运行,二是观察,为了更好的实现这两者 ...
- Android隐藏状态栏、导航栏
Android隐藏状态栏.导航栏 private void hideStatusNavigationBar(){ if(Build.VERSION.SDK_INT<16){ this.getWi ...
- Centos下设置VNC为3389端口
1.安装vnc yum install vnc vnc-server yum install vnc vnc-server 2.修改vnc端口,修改/usr/bin/vncserver ,把5900变 ...
- 夺命雷公狗-----React---23--小案例之react经典案例todos(完成添加任务)
我们这次来处理用户添加的数据,我们还是赵老规矩看看组建大致图... 子组件对父组建进行数据的传递其实是react内部的机智进行处理的了,, 代码如下所示: <!DOCTYPE html> ...
- js基础教程四之无缝滚动
前面学习了相关js的一些基础知识,这节主要针对定时器作综合运用: 无缝滚动-基础 效果演示: *物体运动基础 *让div移动起来 *offsetLeft的作用 *用定时器让物体连续移动 <sty ...
- 课程设计 --- 黑白棋中的 AI
原文链接:https://www.dreamwings.cn/reversi/3013.html 到了考试周了佯,可是偏偏这个时候迎来了很多很多的课程设计,幸好教授把C语言的课程设计提前发出了,不然都 ...
- java.sql.SQLException: 索引中丢失 IN 或 OUT 参数:: x
我的x值是2 我的SQL语句采用的是预编译的形式,我先单独把SQL语句提出来在数据库里运行正常,值也传输正常.仔细查了一下预编译的代码,发现当时粘贴复制 忘把序号修改了,改正后
- HTML空格标签
双学位论文+本学位开题报告+实训项目+实训考试+学员考试,真是脑子都要爆炸... 话不多说,留个小知识点,HTML的空格替代符号表示法: 1. 半个空白,1个字符宽度: 或者 2.1个空白,2个 ...
- zigbee学习之路(十五):基于协议栈的按键实验
一.前言 经过上次的学习,相信大家已经初步学会使用zigbee协议进行发送和接受数据了.今天,我们要进行的实验是按键的实验,学会如何在协议栈里实现按键中断. 二.实验功能 在协议栈上实现按键中断,BU ...
- this的问题
javascript this可以绑定到:全局对象,自己定义的对象,用构造函数生成的对象,通过call或者apply更改绑定的对象 1.全局对象 1 2 3 4 5 function glob ...