SQLAlchemy进阶:Lazy Load 加载参数

39次阅读

共计 2262 个字符,预计需要花费 6 分钟才能阅读完成。

参考:flask-sqlalchemy 中的 lazy 的解释
SQLAlchemy 的 relationship(…, lazy=’??’) 方法中的 lazy 参数一直是初学最容易困扰的地方。
Lazy Load Methods 是 SQLAlchemy 为多表关联而定义的一系列加载方法。为 lazy 参数选择什么值,决定了 SQLAlchemy 什么时候从数据库中加载数据。每种方法的对应着 SQL 语句中多表关联的一种写法,所以优缺点、效率高低各有不同。
lazy 参数的可选方法有:

select – (默认) 后台会用 select 语句一次性加载所有数据,即访问到属性的时候,就会全部加载该属性的数据。

joined – 数据会被 JOIN 语句加载,即对关联的两个表进行 join 操作,从而获取到所有相关的对象。

subquery – 数据被用 subquery 子查询 SQL 语句加载

dynamic – 在访问属性的时候,并不在内存中加载数据,而是返回一个 query 对象, 需要执行相应方法才可以获取对象。适用于数据量大的时候。

immediate – items should be loaded as the parents are loaded, using a separate SELECT statement, or identity map fetch for simple many-to-one references.

noload – no loading should occur at any time. This is to support“write-only”attributes, or attributes which are populated in some manner specific to the application.

True – 即 ‘select’ 方法

False – 即 ‘joined’ 方法

None – 即 ’noload’ 方法

下面用 School 和 Students 的实例来看各种方法的不同。
假设定义两个 ORM 类:
class School(..):
id = Column(..)
students = relationship(‘Student’, backref=’school’)

class Student(..):
id = Column(..)
school_id = Column(.., ForeignKey(‘school.id’) )
上例中我们建立了一个普通的两表关联:students = relationship(‘Student’, backref=’school’)。默认情况下,参数 lazy 为 select,我们不写也可以)。也就是说,如果定义 lazy=’select’,那么当我们要进行搜索引用时(假设表中已有数据):
>>> school_01 = School.query.first() # 随便获取一个数据库中已有的 school
>>> school_01.students
[<Student: u’test’>, <Student: u’test2′>, <Student: u’test3′>]
可以看到,lazy=’select’ 会简单直接的返回所有相关联的数据。但是,如果数据量非常大:比如百万级,这种全部返回就不理智了,因为会大量侵占内存。所以我们可以选择 lazy=’dynamic’,即只返回一个 query 查询对象,供你手动加条件查询,比如 query.all() 或 query.filter() 等。
假设我们将之前的定义改为:students = db.relationship(‘Student’, backref=’_class’, lazy=”dynamic”)。那么:
>>> school_01.students
<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x7f007d2e8ed0>

>>> print(school_01.students)
SELECT students.id AS students_id, students.name AS students_name
FROM students, registrations
WHERE :param_1 = registrations.class_id AND students.id = registrations.student_id

>>> school_01.students.all()
[<Student: u’test’>, <Student: u’test2′>, <Student: u’test3′>]
可以看到, 执行 school_01.students 返回的只是一个 query 对象,甚至说只是返回了一条 SQL 语句,就是没有具体数据。可以想像这个消耗的时间相当于 0 了。而如果 lazy=select 或者 joined 均是直接返回结果。
需要注意的是,lazy=”dynamic” 只可以用在一对多和多对对关系中,不可以用在一对一和多对一中。
这样也合理:如果返回结果很少的话,就没必要延迟加载数据了。
backref(…, lazy=…) 反向引用的 lazy 加载
直接给 relationship(.., lazy=’??’),只是给正向引用设置加载方法。实际上反向引用也是可以设置 lazy 加载方法的。做法就是:使用 backref(..) 函数:
students = relationship(…, lazy=’..’, backref=backref(‘Student, lazy=’dynamic’) )
可以看到,backref(..) 函数返回的是一个 backref 参数专用的值,在这里面可以指定反向引用的加载方法。

正文完
 0