研究Python的ORM框架中的SQLAlchemy库的映射关系
作者:王晔 发布时间:2021-05-23 14:31:07
前面介绍了关于用户账户的User表,但是现实生活中随着问题的复杂化数据库存储的数据不可能这么简单,让我们设想有另外一张表,这张表和User有联系,也能够被映射和查询,那么这张表可以存储关联某一账户的任意数量的电子邮件地址。这种联系在数据库理论中是典型的1-N (一对多)关系,用户表某一用户对应N条电子邮件记录。
之前我们的用户表称为users,现在我们再建立一张被称为addresses的表用于存储电子邮件地址,通过Declarative系统,我们可以直接用映射类Address来定义这张表:
>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy.orm import relationship, backref
>>> class Address(Base):
... __tablename__ = 'addresses'
... id = Column(Integer, primary_key=True)
... email_address = Column(String, nullable=False)
... user_id = Column(Integer, ForeignKey('users.id'))
...
... user = relationship("User", backref=backref('addresses', order_by=id))
...
... def __init__(self, email_address):
... self.email_address = email_address
...
... def __repr__(self):
... return "〈Address('%s')〉" % self.email_address
让我们注意一下新出现的东东,首先就是user_id的ForeignKey结构,学过数据库的同学都知道ForeignKey意味着外键,这是关系型数据库的核心理论之一,即该列user_id与其外键引用的列users.id存在引用约束(constrained)关系,在数据库层面上来讲,就是表users的user_id列被表users的id列约束,值得注意的是,外键关联的必定是另外一张表的主键。
其次新出现的就是relationship()函数,这个将会告知ORM通过Address.userAddress类自身必须链接到User类。relationship()使用两个表的外键约束来判定这种链接的性质,比如说判定Address.user将会是多对一(many-to-one)关系。
另外在relationship()内还有另外一个函数称为backref(),它将提供一种用于反向查询的细节,比如说在对象User上的Address对象集是通过User.addresses属性引用,那么多对一的关系(many-to-one)反向总会是一对多关系(one-to-many)。还有对于Address.user和User.addresses的关系来说总是双向的。
假设使用了Declarative系统,那么relationship()的关系到远端类(remote class)的参数能够被指定为字符串。一旦所有的映射都被成功加载,那么这些字符串将会被计算出Python的表达式,再产生实际的参数(上文中User类的情况)。这些可以使用的字符串名字必须通过定义的基类创建好然后才被计算为实际的类参数,说白了,你字符串引用的类必须是ORM映射管理的类,然后这些类被映射完毕后,这些字符串才能被真正翻译为相应类的引用。
接下来我们举个例子同样创建用User取代Address的”addresses/user”双向关系:
class User(Base):
# ....
addresses = relationship("Address", order_by="Address.id", backref="user")
好吧,刚才多是直接翻译的官方文档,比较生硬,接下来我们来了解几个关于外键(Foreign Key)的小知识:
1. FOREIGN KEY 约束是大多数(但不是所有)的关系型数据库中可以链接到主键列,或者拥有UNIQUE约束的列。
2. FOREIGN KEY 能够引用多重列主键,并且其自身拥有多重列,被称为“复合外键”(composite foreign key)。其也能够引用这些列的子集(subset)。(注:这地方不太明白)
3. FOREIGN KEY 列作为对于其引用的列或者行的变化的响应能够自动更新其自身,比如CASCADE引用操作,这些都是内置于关系型数据库的功能之一。
4. FOREIGN KEY 能够引用其自身的表,这个就涉及到“自引用”(self-referential)的外键了。
5. 更多关于外键的资料可以参考Foreign Key – Wikipedia。
最后我们需要在数据库中创建addresses表,所以我们需要通过元数据(metadata)执行我们的CREATE语句,当然会跳过我们已经创建的表(比如users):
>>> Base.metadata.create_all(engine)
PRAGMA table_info("users")
()
PRAGMA table_info("addresses")
()
CREATE TABLE addresses (
id INTEGER NOT NULL,
email_address VARCHAR NOT NULL,
user_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
)
()
COMMIT
到这里我们的ORM关系算是建立完成了,接下来开始新的一部分,就是如何查询关联的对象。
现在如果我们创建一个User,一个空的addresses集合将会被创建,在这里默认情况下addresses集合将会是列表类型。
>>> jack = User('jack', 'Jack Bean', 'gjffdd')
>>> jack.addresses
[]
接下来我们可以自由的添加Address对象到我们的User对象里了,在这里我们直接赋予addresses属性一个完整的列表。
>>> jack.addresses = [
... Address(email_address='jack@google.com'),
... Address(email_address='j25@yahoo.com')]
当我们使用双向关系时,有一点需要注意的是:在任意一端添加的元素将会自动在另外一端可见,属性的获取和改变将不通过任何SQL语句和Python对象使用一样:
>>> jack.addresses[1]
<Address('j25@yahoo.com')>
>>> jack.addresses[1].user
<User('jack','Jack Bean', 'gjffdd')>
让我们添加并提交Jack Bean到数据库中,现在jack对象的addresses集合拥有了两个Address成员,它们将立即被加入会话中:
>>> session.add(jack)
>>> session.commit()
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('jack', 'Jack Bean', 'gjffdd')
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
('jack@google.com', 5)
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
('j25@yahoo.com', 5)
COMMIT
我们来查询关于Jack的信息,但是奇怪的是没有任何关于addresses的SQL语句执行:
>>> jack = session.query(User).\
... filter_by(name='jack').one()
BEGIN (implicit)
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name = ?
('jack',)
>>> jack
<User('jack','Jack Bean', 'gjffdd')>
让我们直接来查询addresses集合吧,这里大家看到有关addresses的SQL语句执行了:
>>> jack.addresses
SELECT addresses.id AS addresses_id,
addresses.email_address AS
addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id ORDER BY addresses.id
(5,)
[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
由上可知,当我们访问addresses集合的时候,相关SQL语句才被执行,这也是延迟加载关系(惰性加载关系, lazy loading relationship)的例子,至此addresses集合方被作为普通列表加载了。
猜你喜欢
- 废话不多说,直接上代码/** * lhgcalendar时间插件限制只能选择三个月 * @d 获取到的开始时间 * @m 要限制的时间的长度
- 代码:--库是否存在if exists(select * from master..sysdatabases where name=N
- 四 PetShop之ASP.NET缓存如果对微型计算机硬件系统有足够的了解,那么我们对于Cache这个名词一定是耳熟能详的。在CPU以及主板
- 1、重装后启动mysql服务,提示 本地计算机无法启动 mysql 服务 错误 1067:进程意外终止。2、查看mysql根目录下有一 计算
- 描述如下: 用mysqldump 导出 Trigger 的时候遇到一个问题,贴出来,以免大家犯错。 在执行下面的操作时: [root@ytt
- 近段时间看了一些论坛上面关于分页的ASP程序依然有许多的关注者,但里面只有代码,没有详细的解释,对于初学者来说,这样总是得不到真正的掌握,此
- 本文实例讲述了Python切片操作。分享给大家供大家参考,具体如下:我们基本上都知道Python的序列对象都是可以用索引号来引用的元素的,索
- 注:对多选按钮操作。 1:批量全选添加、批量移除。 2:行单选添加、移除。 3:分页之后(全选或不选)状态标识依然存在 /* *****&g
- 本文实例为大家分享了JavaScript实现简单计算器的具体代码,供大家参考,具体内容如下代码:<!DOCTYPE html>&
- 本文不涉及分类器、训练识别器等算法原理,仅包含对其应用(未来我也会写自己对机器学习算法原理的一些观点和了解)首先我们需要知道的是利用现有框架
- 综述本系列文档用于对Python爬虫技术进行简单的教程讲解,巩固自己技术知识的同时,万一一不小心又正好对你有用那就更好了。Python 版本
- 一、代码示例 window.open(url,'新窗口','width='+(window.screen.a
- 1.算法:设有一组关键字{ K 1 , K 2 ,…, K n };排序开始就认为 K 1 是一个有序序列;让 K 2 插入上述表长为 1
- 进程池代码演示方式一from multiprocessing import Pooldef deal_task(n): n -= 1 ret
- 前言应用在Django的项目中是一个独立的业务模块,可以包含自己的路由,视图,模板,模型.一 创建应用程序创建步骤用manage.py中的子
- 1、上下文管理的使用场景凡是要在代码块前后插入代码的场景,这点和装饰器类似。资源管理类:申请和回收,包括打开文件、网络连接、数据库连接等;权
- 其实我这样做的本意是为了防止盗链!大家帮忙看一下通过代码能够根治盗链!只要不是HTTP_REFERER来源于(google.com goog
- 一、问题的引入opencv在图像处理方面有着非常强大的功能,当我们需要使用opencv进行一些图像的矫正工作时,我们通常需要找到原图的一些关
- Python中有3种内建的数据结构:列表、元组和字典。参考简明Python教程1. 列表list是处理一组有序项目的数据结构,即你可以在一个
- 问题的起源早些时候使用with实现了一版全局进程锁,希望实现以下效果:with CacheLock("test_lock"