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
參數專用的值,在這裏面可以指定反向引用的加載方法。