擴展Django的ORM[譯]

注:譯文,原文地址http://blog.safaribooksonline.com/2013/11/06/extending-the-django-orm/ python

Django的對象關係映射(ORM)在代碼和數據庫之間搭起了一架橋樑。這篇文章中我打算簡要的說下什麼是ORM以及該怎麼去使用它。最重要的是怎麼使用專門的SQL定製和擴展ORM。
最後,我會說幾點爲何在不選擇標準ORM時須要特別的當心。如今開始吧。sql

ORM的什麼吸引我?

首先,也是最重要的,ORM會將業務邏輯封裝進應用中,而不是使用數據庫。將應用的業務邏輯放進ORM讓咱們的代碼更容易理解和維護。他讓咱們不用再一直去猜應用究竟是怎麼工做的。 數據庫

ORM幫咱們在數據庫返回和插入數據時進行驗證。在對數據的完整性和安全性很是關心的應用中,這一步是起決定性做用的。雖然數據庫中的類型檢查也很是的棒,可是Django的ORM能幫助
咱們在數據沒有到達數據庫的時候驗證業務規則。此外,也能保證數據庫返回的數據是咱們所指望的。 django

MyModelClass.objects.all() 是ORM的一個很是簡單的使用ORM的例子。這行代碼返回數據表中的全部內容,與select * from MyModelTable結果同樣。還有不少其餘的用法,
這不是這篇文章的關注的。在你開始使用定製的SQL語句以前你將會很是想知道ORM是怎麼工做的。api

爲何我須要定製ORM?

在實踐中,我見過幾個例子。首先是當沒有明顯的查詢方法的時候你會這麼想。然而這對於定製ORM不是一個很好的假定。當你須要使用SQL的時候,試着使用對象和對象關係來思考,而不是使用數據表。
這可能會獲得一個很好的查詢方法。舉個例子,不少的SQL用戶也許想寫下面的語句:安全

SELECT
"user_user"."id", "user_user"."password",  
"user_user"."last_login", "user_user"."name",
"user_user"."date", "user_user"."level"  
FROM  
"user_user"
INSERT JOIN  
"address_address"
ON ("user_user"."id" = "address_address"."user_id")
WHERE ("user_user"."name" IN (hello, world) AND "address_address"."street" = main)

若是這些數據模型建立的正確的話,相應的Python代碼很是簡單:post

User.objects.filter(name__in=['hello', 'world'], address_street='main')

對於傾向使用SQL的用戶,我建議經常往回退一步。對於Python/Django的重度用戶,我建議記住用SQL的思惟來想一想將會建立的是什麼。使用SQL來擴展和定製的緣由更正確的說應該是:
有一個很是特殊的邏輯或者能得到更好的ORM不能提供的性能。性能

我怎麼定製查詢?

第一個定製查詢的選擇是使用extra方法,文檔很詳細。Django文檔中提供了很棒的例子
我將會使用raw來完成一個很好的用例,raw文檔一樣很棒。然而這些例子都不怎麼重要。
我想讓你經過ORM來使用用先進數據庫特性的優點。 大數據

舉個例子,如今你的應用須要展示一個聚合了不少值的大數據,須要建立大量的SQL語句。第一步,你將建立一個存儲過程來封裝這個查詢,而後建立一個SQL function來讓方便調用這個查詢(你一樣須要
建立一個數據表,但那都是標準的SQL)。爲了性能,你也許你將這個邏輯移到了數據庫中去處理。因此,這時候你的應用只能在數據庫完成數據聚合的時候才能處理業務邏輯。 code

咱們一樣假設你有一個名爲aggregate_profile_metrics的SQL方法來幫助你的應用提升性能。使用標準的ORM的話你就沒這麼幸運了。SQL方法只有在使用views和tables的時候才能使用。如今看看raw
查詢的威力。首先定義一個model。

class ProfileMetric(models.Model):
    avg_posts_per_day = models.IntegerField()
    avg_comments_per_day = models.IntegerField()
    avg_respondees_per_day = models.IntegerField()

    class Meta():
        managed = False
        db_table = 'fake_table_name'  # This is the trickier part

注意這個model是徹底與數據相關的,咱們可以在咱們全部不一樣的數據表中進行聚合調用。當數據庫足夠大,你擁有數十個表和數百萬用戶的時候,這個表就有必定規模了。因爲這些數據極可能隨時更新,這時候SQL方法
的效率優點就顯示出來了。如今進入最有趣的部分,使用下面的方法你將很容易達到調用SQL方法的目的。

query = "select avg_posts_per_day, avg_comments_per_day, avg_respondees_per_day 
         from table(aggregate_profile_metrics(%s))"  

profile_metrics = ProfileMetric.objects.raw(query, [283844238])

使用上面的查詢你可以調用SQL方法,可是這太痛苦了。注意,這個SQL方法調用了一個參數user_id來獲取數據,可是咱們沒有將參數直接傳遞進去。若是你的SQL方法不須要調用參數,你應該使用數據庫的view
使用這個方法,你如今可使用profile_metics,除了一些細小的區別外,就像使用一個通常的queryset同樣(查看RawQUeryset和通常Queryset的區別)。

除了這點,Django還容許用戶不經過model進行數據庫操做。我極力反對這麼作。記住不使用model直接進行數據庫操做是沒多少理由的。那些使用了這個的例子都是很是極端的狀況。只要使用了不基於model的raw查詢,
ORM的做用就消失了。從這點來講,就沒有使用Django的必要了。

當使用.raw的時候,須要記住一下幾點。

  1. 確認這些查詢不能使用Django的ORM完成。

  2. 確認須要去建立定製的SQL(好比:爲了得到SQL function的性能或者是在遺留應用中進行的操做)。

  3. 經過QuerySet中的.raw來建立你定製的SQL調用。

  4. 不要直接使用connectioncurser.execute

如今,你知道Django的ORM是很是強大的。當你想使用SQL語句和命令的時候記住這一點。記住下面關於什麼時候使用raw SQL的建議。

  1. 不要使用raw SQL(當你能用其餘辦法解決的時候)。

  2. 使用ORM中的對象關係。

  3. 試着在使用raw前使用extra,

  4. 將raw做爲最後一個選擇。

相關文章
相關標籤/搜索