做者:Vamei 出處:http://www.cnblogs.com/vamei 嚴禁轉載。git
使用Python的Django模型的話,通常都會用它自帶的ORM(Object-relational mapping)模型。這個ORM模型的設計比較簡單,學起來不會特別花時間。不過,Django的ORM模型有本身的一套語法,有時候會以爲彆扭。這裏聊一下我本身的體會。數據庫
這一部分算處理得比較好的部分。Django的數據模型的創建過程很簡單,就是繼承django.db.models中的Model類,而後給它增長屬性。每個屬性能夠對應關係數據庫中的一個字段。好比在一個叫myapp的Django App下,建立models.py文件:django
from django.db import models class Person(models.Model): name = models.CharField(max_length=10)
經過manage.py的makemigrations和migrate命令,就能夠執行數據庫的遷移。上面的name屬性,就對應了生成的myapp_person表中名爲"name"的一列。這裏的max_length=10對應了限制條件:app
VARCHAR(10)
(在MySQL V4中,表明了10個字節;在MySQL V5中,表明了10個字符。)工具
除了上面的字符類型,其餘常見的字段類型,在Django都有對應的*Field來表達,好比TextField、DateField、DateTimeField、IntegerField、DecimalField。此外,還有一些常見的限制條件,除了上面的max_length,還有default、unique、null、primary_key等等。數字類型的限制條件有max、min、max_digits、decimal_places。這些限制條件都經過參數的形式傳給屬性。有一些限制條件是Django提供的,並無數據庫層面的對應物,好比blank。學習
(當blank參數爲真時,對應字段能夠爲留爲空白。)spa
在基本的模型設計上,Django ORM沒有留什麼坑。設計
Django中的一對1、多對1、多對多關係能夠經過下面方式表達:code
from django.db import models class Company(models.Model): name = models.CharField(max_length=10) class Group(models.Model): name = models.CharField(max_length=10) class Person(models.Model): name = models.CharField(max_length=10) class Customer(models.Model):
name = models.CharField(max_length=10) person = models.OneToOneField(Person) company = models.ForeignKey(Company, on_delete=models.CASCADE) groups = models.ManyToManyField(Group)
Customer的定義中,用到一對1、多對1、多對多關係。它們分別經過OneToOneField、ForeignKey和ManyToManyField來實現。對象
須要注意的是,在Django ORM中,只能經過ForeignKey來定義多對一關係,不能顯示地定義一對多關係。但你可使用模型對象的*_set語法來反向調用多對一關係。好比說:
company.customer_set #company是一個Company的實例
就能夠根據一對多關係,調到該公司下的全部客戶。此外,多對多關係也能夠用相似的方式反向調用,好比:
group.customer_set
此外,你還能夠在模型中加入related_name參數,從而在檢討調用時,改用"*_set"以外的其餘名稱,好比:
class Customer(models.Model): person = models.OneToOneField(Person) address = models.CharField(max_length=100) company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="customers")
若是兩個模型之間有多個關係時,related_name能夠防止*_set重名。
總的來講,上面的解決方案能夠實現功能,並不影響使用。但我老是以爲這個解決方案有些醜陋。因爲不能顯式地表達兩個模型之間的關係,模型之間的關係看起來不夠明瞭。特別是讀代碼時,第一個類定義徹底無法提示一對多的關係。我必需要看到了第二個類定義,才能搞明白兩個模型之間的關係。真但願有一種顯式說明關係的辦法,下降讀代碼時的認知負擔。
Django ORM能夠經過一些方法來實現。其中的不少方法返回的是Django自定義的QuerySet類的迭代器。Python看到迭代器時會懶惰求值,因此這些方法返回時並不會真正進行數據庫操做。這樣,多個方法串聯操做時,就避免了重複操做數據庫。返回QuerySet的常見方法包括:
all()
filter()
exclude()
annotate()
order_by()
reverse()
distinct()
...
對於依賴具體數據的操做,QuerySet會求值。好比遍歷QuerySet時,就會先執行數據庫操做。用len()得到QuerySet長度時,也會形成QuerySet估值。此外QuerySet一些方法,比get()、count()、earlist()、exists()等,都會對QuerySet進行求值。所以,在寫程序時,要注意QuerySet求值的時間點,避免重複的數據庫操做。
SQL的WHERE條件能夠經過參數的形式來傳給方法。這些參數通常是"[字段]__[運算符]"的命名方式,好比:
Customer.objects.filter(name__contains="abc")
除了contains,還有in、gt、lt、startswith、date、range等等操做符,能實現的WHERE條件確實夠全的了。
不過,這又是一個有點彆扭的地方,即經過命名方式來控制查詢行爲。我看過有的ORM是用lambda的形式來表達WHERE條件,還有的會作一個相似於contains()的方法,都要比Django ORM的方式好看。若是是跨表查詢,Django的方式就更醜了:
Customer.objects.filter(company__name__contains="xxx")
無限的雙下劃線啊……
Django實現聚合的方式簡直是噩夢。貌似ORM對錶達GROUP BY很無力,源代碼裏的註釋就認輸了:
聚合的aggregate()和annotate()方法能夠實現基本的功能,但稍微複雜一點,代碼就變得魔幻了:
看到一大串values()、annotate()變來變去,有沒有以爲頭暈?我以爲這種狀況下,能夠直接上原始的SQL查詢語句了,不必再本身折騰本身。
F表達式指代了一列,對於update操做時引用列的值有用。Q表達式表明了WHERE的一個條件,能夠用於多個WHERE條件的鏈接。這些都是Django ORM用來彌補缺陷的。就拿Q表達式來講。查詢方法中跟多個參數的話,至關於多個WHERE條件。這些條件會默認爲AND關係。爲了表達OR和NOT關係,Django ORM就造了個Q表達式,好比:
filter(Q(name__contains="abc")|Q(name__startswith("xxx")))
爲了彌補缺陷,Django ORM又增長了一種語法風格。因而,學習路上又多了一個坑……
總的來講,Django ORM在實現基礎的數據庫操做方面沒問題。但若是須要構建複雜的SQL語句,與其在Django ORM裏繞來繞去,還不如直接用原始的SQL語句。這個是我最強烈的一個感覺。固然,Django ORM仍是可用的工具。我寫這篇文章的目的,是提醒你們不要誤把糟糕的設計當作精巧的語法。