本文為 Python SQLAlchemy ORM 一系列教學文:

刪除

學會如何查詢之後,就能夠進行後續的刪除、更新等操作。

同樣地,以幾個範例做為學習的捷徑。

1
2
3
4
5
6
7
8
9
user_1 = User('user1', 'username1', 'password_1')
user_2 = User('user2', 'username2', 'password_2')
session.add(user_1)
session.add(user_2)
affected_rows = session.query(User).filter_by(id=1).delete()
print('Affected rows:', affected_rows) if session.query(User).filter_by(id=1).count() == 0:
print('id 1 not found')

上述的範例中,將 id 為 1 的 user 查詢出來後,直接呼叫 delete() 方法進行刪除。

呼叫 delete() 後會回傳刪除成功的資料筆數。

更新

事實上,更新也只需要呼叫 update() 並提供欄位名稱與欄位值的 dictionary 做為參數即可。

1
2
3
4
5
6
7
8
9
user_1 = User('user1', 'username1', 'password_1')
user_2 = User('user2', 'username2', 'password_2')
session.add(user_1)
session.add(user_2)
affected_rows = session.query(User).filter_by(id=1).update({'id':3, 'username': 'sqlalchemy'})
print('Affected rows:', affected_rows) for r in session.query(User):
print(r.id, r.username)

表格關聯(Relationship)

SQLAlchemy ORM 最大的特點就是能夠透過 Python 類別間關聯的建立,實作資料庫表格間的關聯,能夠讓程式開發者很方便的取得相關聯的資料。

而關聯的種類有:

  • One to Many
  • Many to one
  • One to one
  • Many to Many

分別代表一筆資料與另一個表格的資料間的關係。

如果有興趣了解的人可以詳閱 Basic Relationship Patterns 。

接下來同樣用一個範例了解 SQLAlchemy ORM 的表格關聯。範例中,除了原先已經定義過的 User 類別之外,還會再多定義一個 Address 類別,兩者間的關係為一對多,代表一個 user 允許有多個 address 。

一對多關聯

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# -*- coding: utf-8 -*-
import hashlib
import sqlalchemy.orm
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import sessionmaker
from sqlalchemy import desc
from sqlalchemy.orm import aliased
from sqlalchemy import func Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True)
name = Column(String)
username = Column(String)
password = Column(String) def __init__(self, name, username, password):
self.name = name
self.username = username
self.password = hashlib.sha1(password).hexdigest() def __repr__(self):
return "User('{}','{}', '{}')".format(
self.name,
self.username,
self.password
) class Address(Base): __tablename__ = 'user_address' id = Column(Integer, primary_key=True)
address = Column(String, nullable=False)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship('User', backref=backref('address', order_by=id)) def __init__(self, address):
self.address = address def __repr__(self):
return "Address('{}')".format(self.address) if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine) Session = sessionmaker(bind=engine)
session = Session() user_1 = User('user1', 'username1', 'password_1'.encode('utf-8'))
user_2 = User('user2', 'username2', 'password_2'.encode('utf-8'))
user_1.address = [Address('臺灣凱達格蘭大道')]
user_2.address = [Address('美國紐約時代廣場'), Address('美國華盛頓DC')]
session.add(user_1)
session.add(user_2) for r in session.query(Address):
print(r.id, r.address, r.user_id) for r in session.query(User):
for a in r.address:
print(r.username, 'living at', a.address)

範例說明:

首先看到第 7, 8 行 import 所需的模組( ForeignKey, relationship, backref ) 。

然後在第 38 - 51 行的部份進行 Address 類別的定義,其中第 44 行定義了一個關聯至 user.id 的 Foreign Key,宣告了它是一個指到 user.id 的 Foreign Key。

並且在第 45 行以 relationship() 方法定義類別 Address 與類別 User 間有一個參照的關聯存在。 SQLAlchemy ORM 就會依照 backref('address', order_by=id) 所定義的關聯,將 User 與 Address 之間以 address 屬性關聯起來。

若第 45 行單單只有 relationship('User') 就代表只是單向的從 Address 到 User 之間的關聯存在(Address → User),但由於我們希望查詢 User 時也能夠得知 User 到那些 Address 的資料,因此就得從 User 關聯回 Address (User → Address),形成一種雙向的關係(User ↔ Address) ,在 SQLAlchemy 中,就稱這種雙向的關係為 backref 。因此在第 45 行可以看到 backref('address', order_by=id),其實就是代表若要從 User 關聯回 Address 就得存取 User 的 address 屬性。

backref – indicates the string name of a property to be placed on the
related mapper’s class that will handle this relationship in the other
direction. The other property will be created automatically when the
mappers are configured. Can also be passed as a backref() object to
control the configuration of the new relationship.

第 60, 61 行分別指定了 user_1 , user_2 的地址,然後在第 64, 65 行將這些資料一併新增到資料庫內(包含 Address 的部份, SQLAlchemy 會自動處理)。

第 70 行查詢資料庫內的所有 user 資料,並且透過 relationship('User', backref=backref('address', order_by=id)) 中所指明的 backref('address', order_by=id),以 address 屬性取得與 User 有關聯的 Address 相關資料。 最後,由於 User 與 Address 之間是一對多的關係,因此需要在第 71 行以一個迴圈將各自的 Address 實例一一取出。

一對一關聯

預設的關聯就是一對多,因此我們可以看到前述的範例是以 list 的進行新增,如 user_2.address = [Address('美國紐約時代廣場'), Address('美國華盛頓DC')]。

可是有些時候我們會限制資料間只能有一對一的關係存在,此時就需要在 backref() 中加上 uselist=False 做為參數,以表明一對一的關係。

例如前述範例的第 45 行可變更為:

1
backref('address', uselist=False, order_by=id)

那麼在第 62, 63 行就不需再以 list 的形式指定,同時在第 71 行也不需要再多一個迴圈將各別的 Address 取出。 多對一關聯 同樣以 User, Address 兩個類別作為例子,試想當多個使用者住在同一個地址時,就是多對一的情況。

此時,前述範例就得稍作變更,變成將 Address 的 Foreign Key 移除,而改至 User 中,並將關係指向 Address。同時,我們已不再需要從 Address 雙向關聯回 User ,因此在 relationship() 中也不需再指定 backref 。

多對一關聯範例如下:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding: utf-8 -*-
import hashlib
import sqlalchemy.orm
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import sessionmaker
from sqlalchemy import desc
from sqlalchemy.orm import aliased
from sqlalchemy import func Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True)
name = Column(String)
username = Column(String)
password = Column(String)
address_id = Column(Integer, ForeignKey('user_address.id'))
address = relationship('Address') def __init__(self, name, username, password):
self.name = name
self.username = username
self.password = hashlib.sha1(password).hexdigest() def __repr__(self):
return "User('{}','{}', '{}')".format(
self.name,
self.username,
self.password
) class Address(Base): __tablename__ = 'user_address' id = Column(Integer, primary_key=True)
address = Column(String, nullable=False) def __init__(self, address):
self.address = address def __repr__(self):
return "Address('{}')".format(self.address) if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine) Session = sessionmaker(bind=engine)
session = Session() user_1 = User('user1', 'username1', 'password_1'.encode('utf-8'))
user_2 = User('user2', 'username2', 'password_2'.encode('utf-8'))
address_1 = Address('臺灣凱達格蘭大道')
user_1.address = address_1
user_2.address = address_1
session.add(user_1)
session.add(user_2) for r in session.query(User):
print(r.username, 'living at', r.address.address)
print('Address_id:', r.address_id)

多對多關聯

第 4 種關聯的形式為多對多關聯,先前提到一對一、一對多、多對一的關聯都是屬於直接形式的 A ↔ B 型,然而 SQLAlchemy 的多對多做法是用一個中介的 association table 來多對多映對,就成為一種 A ↔ association table ↔ B 的間接關聯形式。

接下來同樣用一個範例說明:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# -*- coding: utf-8 -*-
import hashlib
import sqlalchemy.orm
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import sessionmaker
from sqlalchemy import desc
from sqlalchemy.orm import aliased
from sqlalchemy import func
from sqlalchemy import Table Base = declarative_base() association_table = Table(
'association',
Base.metadata,
Column('table_a_id', Integer, ForeignKey('table_a.id')),
Column('table_b_id', Integer, ForeignKey('table_b.id'))
) class A(Base):
__tablename__ = 'table_a'
id = Column(Integer, primary_key=True)
children = relationship('B', secondary=association_table) class B(Base):
__tablename__ = 'table_b'
id = Column(Integer, primary_key=True) if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine) Session = sessionmaker(bind=engine)
session = Session() b_list = [B(), B(), B()]
a_1 = A()
a_2 = A()
a_1.children = b_list
a_2.children = b_list
session.add(a_1)
session.add(a_2)
session.commit() for a in session.query(A):
print('A:', a.id, 'has relationship with')
for b in a.children:
print('\tB:', b.id)

以下是執行結果:

1
2
3
4
5
6
7
8
A: 1 has relationship with
B: 1
B: 2
B: 3
A: 2 has relationship with
B: 1
B: 2
B: 3

如果有將 sessionmaker() 的 echo=True 選項打開的話,就可以發現 SQLAlchemy 將 A 與 B 的對應 id 存至 association 的訊息。

如下所示:

1
2
2013-09-01 11:46:20,862 INFO sqlalchemy.engine.base.Engine INSERT INTO association (table_a_id, table_b_id) VALUES (?, ?)
2013-09-02 11:46:20,862 INFO sqlalchemy.engine.base.Engine ((1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3))

範例說明:

第 17 行定義了一個 association table 用來做為多對多的間接映射的資料表格,Table() 會用 Metadata 來關聯到 declarative base class ,所以 ForeignKey() 就可以分別建立連結到 table_a , table_b 。

第 25 - 33 行定義了 2 個類別 A, B,其中第 28 行以 relationship('B', secondary=association_table) 指明與 B 之間有關聯存在,並且以 secondary=association_table 指明透過 association_table 建立多對多的關係。 第 43 - 50 行則是建立多對多的資料。

最後在第 52 - 55 行將資料庫內的結果列印出來。

值得注意的是在範例中並沒有建立雙向的關係,如果要查詢 B 時也能夠得知與 A 的關聯,就得在 A 類別的 relationship() 加上 backref=backref('children') 指明雙向關係,如此一來 B 就可以透過存取 children 屬性取得相關聯的 A 。

多對多關聯的刪除

在多對多的關聯下,若刪除一筆資料,並不需要手動更新 association table 內的資料,SQLAlchemy 會自動進行更新。 除了多對多的資料關聯是自動 cascade 刪除之外,其他的關聯就得自行在 relationship() 指明使用 cascade 刪除,例如:

1
addresses = relationship('Address', backref='user', cascade='all, delete, delete-orphan')

詳見 Configuring delete/delete-orphan Cascade

結語

至此,已解說完大部份的 SQLAlchemy ORM 的功能。剩下的功能就得靠各位自行探索囉!

Python SQLAlchemy --3的更多相关文章

  1. python SQLAlchemy

    这里我们记录几个python SQLAlchemy的使用例子: 如何对一个字段进行自增操作 user = session.query(User).with_lockmode('update').get ...

  2. Python SQLAlchemy --2

    本文為 Python SQLAlchemy ORM 一系列教學文: 接下來會更深入地探討查詢的使用. 查詢的基本使用法為 session.query(Mapped Class),其後可加 .group ...

  3. Python SQLAlchemy --1

    本文為 Python SQLAlchemy ORM 一系列教學文: SQLAlchemy 大概是目前 Python 最完整的資料庫操作的套件了,不過最令人垢病的是它的文件真的很難閱讀,如果不搭配個實例 ...

  4. Python SqlAlchemy使用方法

    1.初始化连接 from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create ...

  5. python+SQLAlchemy+爬虫

    python+SQLAlchemy+爬虫 前面分享了SQLAlchemy的知识,这次我共享一下学习用python开发爬虫再把爬出来的数据放到用SQLAlchemy的数据库上面的知识,当然我这个是带测试 ...

  6. Python.SQLAlchemy.0

    1. SQLAlchemy and You http://lucumr.pocoo.org/2011/7/19/sqlachemy-and-you/ 2. Overview http://docs.s ...

  7. Python SQLAlchemy基本操作和常用技巧包含大量实例,非常好python

    http://www.makaidong.com/%E8%84%9A%E6%9C%AC%E4%B9%8B%E5%AE%B6/28053.shtml "Python SQLAlchemy基本操 ...

  8. Python SQLAlchemy入门教程

    本文将以Mysql举例,介绍sqlalchemy的基本用法.其中,Python版本为2.7,sqlalchemy版本为1.1.6. 一. 介绍 SQLAlchemy是Python中最有名的ORM工具. ...

  9. Python—sqlalchemy

    SQLAlchemy SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作. #Dialect用于和数据API进行交流,根据配置文 ...

随机推荐

  1. 解决VML遭遇IE8和XHTML DOCTYPE时不能运行的问题(转)

    原文网址:http://blog.csdn.net/cuixiping/article/details/4227283  以下代码在IE运行正常 <!DOCTYPE HTML PUBLIC &q ...

  2. UEFI+GPT引导实践篇(一):切换到UEFI启动,准备安装介质

    如果只单纯比较UEFI引导和BIOS引导,那么毫无疑问UEFI引导要简单很多.不过现在的主板大都是同时兼容BIOS和UEFI引导方式,所以在实际操作前还需要确认一些东西.详见下文. 1.我的电脑支不支 ...

  3. Entity framework code first

    EF Code First 不便之处,数据库结构改变时,需要利用程序包管理器控制台生成代码,再用update-database -Verbose更新数据库,这样的做法在很多时候不容易部署.下面介绍一种 ...

  4. Qt 环境下MAPX组件的编程

    使用mapx打包文件可以方便的迅速开发,今天介绍一种不使用打包文件,直接使用mapx组件的编程方法. 就像之前介绍flash控件编程的方法,首先建立一个qt demo.基于那个的窗口都可以. 本den ...

  5. 关于数组Arry的一些基本认识

    认识数组主要从以下几个方面去认识,一:数组具备什么样的特性,二:它能做什么,三:它具备哪些常用的api方法 //数组的原始表示方式 /* 数组的特性:1有长度,2以0开头 */ var arr1 = ...

  6. 23. Can't connect to X11 window server using '127.0.0.1:0.0' as the value of the DISPLAY variable.解决办法

    在终端里 以root用户执行 #xhost + 然后su - oracle 执行#export DISPLAY=:0 运行runinstaller

  7. python Tab自动补全命令设置

    Mac/Windows下需要安装模块儿 pip install pyreadline pip install rlcompleter pip install readline 注意,需要先安装pyre ...

  8. 运行R 报错R cannot R_TempDir, 继而发现/dev/mapper/VG00-LV01 磁盘空间已满

    今天在运行R脚本的时候报了个错:Fatal error: cannot create 'R_TempDir'.排除了是自己写的代码的问题,想着应该是某个没见过的原因,google之,发现网上的说法是/ ...

  9. Google Protocol Buffer 的使用

    简介 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 ...

  10. 账号控管:NIS服务器

    NIS服务器   sun最开始命名为 Yellow pages ,黄页, NIS = YP;   14.1 NIS的由来与功能   NIS--Network information services( ...