SQLAlchemy進階:Lazy Load 加載參數

參考: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'方法

下面用SchoolStudents的實例來看各種方法的不同。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章