前面介绍了关于用户账户的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集合方被作为普通列表加载了。

Python和C 各有优势,选择应基于项目需求。1)Python适合快速开发和数据处理,因其简洁语法和动态类型。2)C 适用于高性能和系统编程,因其静态类型和手动内存管理。

选择Python还是C 取决于项目需求:1)如果需要快速开发、数据处理和原型设计,选择Python;2)如果需要高性能、低延迟和接近硬件的控制,选择C 。

通过每天投入2小时的Python学习,可以有效提升编程技能。1.学习新知识:阅读文档或观看教程。2.实践:编写代码和完成练习。3.复习:巩固所学内容。4.项目实践:应用所学于实际项目中。这样的结构化学习计划能帮助你系统掌握Python并实现职业目标。

在两小时内高效学习Python的方法包括:1.回顾基础知识,确保熟悉Python的安装和基本语法;2.理解Python的核心概念,如变量、列表、函数等;3.通过使用示例掌握基本和高级用法;4.学习常见错误与调试技巧;5.应用性能优化与最佳实践,如使用列表推导式和遵循PEP8风格指南。

Python适合初学者和数据科学,C 适用于系统编程和游戏开发。1.Python简洁易用,适用于数据科学和Web开发。2.C 提供高性能和控制力,适用于游戏开发和系统编程。选择应基于项目需求和个人兴趣。

Python更适合数据科学和快速开发,C 更适合高性能和系统编程。1.Python语法简洁,易于学习,适用于数据处理和科学计算。2.C 语法复杂,但性能优越,常用于游戏开发和系统编程。

每天投入两小时学习Python是可行的。1.学习新知识:用一小时学习新概念,如列表和字典。2.实践和练习:用一小时进行编程练习,如编写小程序。通过合理规划和坚持不懈,你可以在短时间内掌握Python的核心概念。

Python更易学且易用,C 则更强大但复杂。1.Python语法简洁,适合初学者,动态类型和自动内存管理使其易用,但可能导致运行时错误。2.C 提供低级控制和高级特性,适合高性能应用,但学习门槛高,需手动管理内存和类型安全。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

记事本++7.3.1
好用且免费的代码编辑器

PhpStorm Mac 版本
最新(2018.2.1 )专业的PHP集成开发工具

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境