Python SQLAlchemy --3

本文為 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 的功能。剩下的功能就得靠各位自行探索囉!

转载于:https://www.cnblogs.com/mosson/p/6257181.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#DateTime.ToString格式化时间字符串

DateTime.ToString(string format) 中 format 参数(区分大小写): 参数 含义 d 月中的某一天。一位数的日期没有前导零。 dd 月中的某一天。一位数的日期有一个前导零。 ddd 周中某天的缩写名称,在 AbbreviatedDayNam…

Wordpress中显示页面当前位置

Wordpress中显示页面当前位置 在浏览网站的时候,很多网站都提供了页面定位的功能,一般在文章的上方以这样的形式显示: 首页 >> 分类 >> 子分类 >> „ >> 阅读正文 那如何在wordpress中实现这样一个功能呢&#xff1…

CentOS8下 nginx+php7.4安装pdo_sqlsrv 连接 SQL SERVER2012数据库

参考自:https://blog.csdn.net/lwprain/article/details/109598560 关键步骤是一二三,结合 pecl install 正常安装(先安装dnf install unixODBC-devel)即可搞定 ------------------------------------------------------------------------------------…

ArcGIS Engine 编辑- ITask

转自原文ArcGIS Engine 编辑- ITask 下面的代码是我们定制的一个工作流-给等高线赋值 namespace EngineApplication { [Guid("5b0c0692-eaf7-4d64-9cee-c8c1afaf06f4")] [ClassInterface(ClassInterfaceType.None)] [ProgId("EditeTest.ContourTask")] pu…

Linux 系统应用编程——网络编程(TCP 协议三次握手过程)

TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 位码即tcp标志位,有6种标示: SYN ( synchronous 建立联机 ) ACK ( acknowledgement 确认 ) PSH ( push 传送…

知识管理在客户关系管理中的应用领域

CRM中的知识获取 CRM前端业务系统(包括销售管理、营销管理、客户服务三个模块)为与客户互动、获取客户知识提供了各种途径,如销售自动化系统、电子商务网站、呼叫中心、网络社区、博客等等,另外,企业还直接从外部环境、…

基于ELK的简单数据分析

原文链接: http://www.open-open.com/lib/view/open1455673846058.html 环境 CentOS 6.5 64位JDK 1.8.0_20Elasticsearch 1.7.3LogStash 1.5.6Kibana 4.1.4介绍 ElasticSearch是有名的开源搜索引擎,现在很多公司使用ELK技术栈做日志分析,比如…

Linux 系统应用编程——网络编程(高级篇)

一、网络超时检测 在网络通信过程中,经常会出现不可预知的各种情况。例如网络线路突发故障、通信一方异常结束等。一旦出现上述情况,很可能长时间都不会收到数据,而且无法判断是没有数据还是数据无法到达。如果使用的是TCP协议,可…

[win10] 在桌面上显示计算机、控制面板、网络

1. 右击桌面,选择个性化 2. 选择”主题", 点击“桌面图标设置” 3. 把想要放桌面的图标给钩上

linux下共享文件夹(windows可访问,linux也可访问)

2019独角兽企业重金招聘Python工程师标准>>> 本文是转字网上的两段,如果是菜鸟,想懂有点难度,我这里给点注释 在linux上共享文件夹windows下看 ******************************************* 首先给linux设一个ip,要和…

用nginx做了一下反向代理,得到的ip地址都是127.0.0.1

nginx反向代理配置时,一般会添加下面的配置: proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; loc…

【windows】 设置开机启动程序

开始 -> 运行,输入:shell:startup 将要启动的“快捷方式”放入此目录下。重启生效!

ssh时,密码输入框(密码输入提示)很晚才出现的解决方法

新配置了一台server(centos6.2),发现从windows使用putty登录时,在输入用户名后总是很慢才提示输入密码[rootFDFS-1 ~]# ssh -V OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 Mar 2010发现ssh的版本较高了使用另一台linux服务器连接&…

Linux time()函数解析

time() 函数语法如下&#xff1a; 所需头文件#include <time.h>函数原型time_t time(time_t * timer)参数说明timerNULL时得到机器日历时间&#xff1b; timer时间数值时&#xff0c;用于设置日历时间&#xff1b;函数返回值机器日历时间 功能: 获取当前的系统时间&#…

开源|LightGBM:三天内收获GitHub 1000+ 星

原创 2017-01-05 LightGBM 微软研究院AI头条【导读】不久前微软DMTK(分布式机器学习工具包)团队在GitHub上开源了性能超越其他boosting工具的LightGBM&#xff0c;在三天之内GitHub上被star了1000次&#xff0c;fork了200次。知乎上有近千人关注“如何看待微软开源的LightGBM&a…

【phpstudy】在windows上开启后访问php页面显示502

phpstudy在windows上安装好后&#xff0c;会默认创建一个网站&#xff0c;而且使用了php7.3.4版本&#xff0c;但是当你把这个网站删除&#xff0c;再自己去手动(不利用它这个工具)创建一个php网站&#xff0c;会发现访问php页面会出现502错误&#xff0c;纠其原因&#xff0c;…

重设MYSQL数据库ROOT用户的密码

一、 在已知MYSQL数据库的ROOT用户密码的情况下&#xff0c;修改密码的方法&#xff1a;1、 在SHELL环境下&#xff0c;使用mysqladmin命令设置&#xff1a;mysqladmin –u root –p password “新密码” 回车后要求输入旧密码2、 在mysql>环境中,使用update命令&#xff0c…

Linux---进程调度相关命令解析

进程相关命令 1、ps 查看系统中的进程 使用方式&#xff1a;ps [options] [--help] 说明&#xff1a;显示瞬间进程 (process) 的动态 参数&#xff1a;ps的参数非常多, 在此仅列出几个常用的参数并大略介绍含义 ps命令常用用法&#xff08;方便查看系统进程&#xff09; 1&a…

随机数的扩展--等概率随机函数的实现

【转载】 Hackbuteer1的专栏 思想&#xff1a;就是先生成范围足够容纳扩展后随机数的一个随机数&#xff0c;然后忽略扩展过头的部分。 比如&#xff0c;已知random_n&#xff0c;要求random_m&#xff0c;最简单方法可以先生成random_n2()n*(random_n()-1)random_n(); 至于怎么…

js前台数据校验

1.脚本 Validator {exp:{numWord:/^[A-Za-z0-9]$/, //数字或字母numWordM:/^[\-]*[A-Za-z0-9]$/, //数字或字母可为负数letter:/^[A-Za-z]$/, //字母chinese:/^[\u4E00-\u9FA5\uF900-\uFA2D]$/,numberReg:/^[0-9]$/,//数字格式 只能是0到9的数字 eg:92wordReg:/^(\w|[\u4E00-\…