orm英文全稱object relational mapping,就是對象映射關係程序,簡單來講咱們相似python這種面向對象的程序來講一切皆對象,可是咱們使用的數據庫卻都是關係型的,爲了保證一致的使用習慣,經過orm將編程語言的對象模型和數據庫的關係模型創建映射關係,這樣咱們在使用編程語言對數據庫進行操做的時候能夠直接使用編程語言的對象模型進行操做就能夠了,而不用直接使用sql語言。html
orm的優勢:python
缺點:mysql
在Python中,最有名的ORM框架是SQLAlchemy。用戶包括openstack\Dropbox等知名公司或應用,主要用戶列表http://www.sqlalchemy.org/organizations.html#openstack程序員
Dialect用於和數據API進行交流,根據配置文件的不一樣調用不一樣的數據庫API,從而實現對數據庫的操做,如:sql
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交互
|
下面就開始讓你見證orm的nb之處,盤古開天劈地以前,咱們建立一個表是這樣的express
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)
#建立表結構
|
你說,娘那個腚的,並無感受代碼量變少啊,呵呵, 孩子莫猴急,好戲在後面api
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. bash
除上面的建立以外,還有一種建立表的方式,雖不學用,但仍是看看吧
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
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就能分清哪一個外鍵是對應哪一個字段了
如今來設計一個能描述「圖書」與「做者」的關係的表結構,需求是
此時你會發現,用以前學的外鍵好像沒辦法實現上面的需求了,由於
固然你更不能夠像下面這樣幹,由於這樣就你就至關於有多條書的記錄了,太low b了,改書名還得都改。。。
那怎麼辦呢? 此時,咱們能夠再搞出一張中間表,就能夠了
這樣就至關於經過book_m2m_author表完成了book表和author表以前的多對多關聯
用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()
|