
數據模型通常是Web應用程序的基礎,從這里開始探索Django開發(fā)的細節(jié)。
Django的數據庫模型層使用了大量的 ORM(對象關系映射),這一節(jié)
先解釋下 Django的ORM,
 然后深入講解模型變量 model field,
 模型類model class之間可能的關系,
 以及通過模型類元數據model class metadata來定義特定行為或者是激活和自定義 Django的 admin應用。
Django和其他大多數現(xiàn)代Web框架一樣,依賴一個強大的DAO,數據訪問層,試圖將底層的關系數據庫和Python的面向對象的特質聯(lián)系起來
使用ORM有4個充分的理由:
Django模型對象是定義變量的首選方式,而變量通常對應的是數據庫的列。
你可以請求一個 id 為 5 的Author對象并檢查其 author.name, 而不必去寫SELECT name from authors WHERE id=5這樣的SQL語句, 這樣要 Pythonic的多。
而且,模型對象能給那個簡陋的例子增加許多額外的價值,Django的ORM可以讓你定義任何實例方法:
data aggregation或者計算屬性calculated attributeORM作為應用程序和數據庫之間的代碼層,通常具有很好的可移植性
使用ORM之后很少有機會需要自己執(zhí)行SQL查詢,所以不必擔心保護性很差的查詢字符串導致的問題,如SQL注入攻擊等
而且ORM還提供一個智能化的引用和轉義輸入變量的核心機制,讓你不在花很多時間來處理這種細枝末節(jié)
跟直接編寫SQL相比,ORM最大的好處就是從數據庫中獲取記錄時使用的查詢語法。高級一點的語法不僅更容易編寫,而且這種帶入到Python領域的查詢機制更帶來了一系列有用的技巧和方法。比如,直接循環(huán)一個數據結構,原本在SQL中是相當笨拙的,現(xiàn)在卻簡潔的多,還可以回避本來是無可避免的討厭的字符串操作。
Django的模型擁有多種不同變量類型,一些跟它們在數據庫里的實現(xiàn)比較接近,一些則是為Web表單界面而考慮設計
首先來看一個Django模型定義
from django.db import modelsclass Book(models.Model):   title = models.CharField(max_length=100)   author = models.ForeignKey(Author)   length = models.IntergerField()看來, Django用Python的類來表示對象,而對象則通常映射到SQL中的表,屬性對應列;
這些屬性本身也是對象, 是 Field 類的子類; 如上提到過的,
這些子類有的跟SQL列類型很相似, 其他的則都提供了某種程度的抽象。比如:
主鍵和唯一性
如果你沒有明確指定,Django會自動生成主鍵 id(AutoField,自增整數)
如果你希望有更多的控制主鍵,只需要在某個變量上指定 PRimary_key = True,
這個變量會取代 id 成為這個表的主鍵
類似于SQL中的UNIQUE索引,Django也提供了unique=True的參數
定義模型對象之間關系的能力通常是關系數據庫最大的賣點之一, 同時也是ORM之間相互競爭的區(qū)域。Django目前的實現(xiàn)還是圍繞著數據庫展開,確保關系在數據庫層而不是在應用層上定義;然而,由于SQL提供了一種顯式組織關系的方法(外鍵),我們有必要再加入一些抽象來表示更復雜的關系。class Author(models.Model):    name = models.CharField(max_length=100)class Book(models.Model):    title = models.CharField(max_length=100)	author = models.ForeignKey(Author)Django的外鍵表現(xiàn)很直觀,其主要參數就是它要引用的模型類;但是注意要把被引用的類放在前面。不過,如果不想留意順序,也可以用字符串代替。
class Book(models.Model):    title = models.CharField(max_length=100)	author = models.ForeignKey("Author")	#if Author class is defined in another file myapp/models.py	author = models.ForeignKey("myapp.Author")class Author(models.Model):   name = models.CharField(max_length=100)如果要引用自己為外鍵,可以設置 models.ForeignKey("self"),
這在定義層次結構等類似場景很常用,比如Employee類可以具有類似supervisor或是hired_by這樣的屬性
外鍵ForeignKey只定義了關系的一端,但是另一端可以根據關系追溯回來,因為這是一種多對一的關系,多個子對象可以引用同一個父對象,而父對象可以訪問到一組子對象。看下面的例子:
#取一本書“Moby Dick”book = Book.objects.get(title="Moby Dick")#取作者名字author = Book.author#獲取這個作者所有的書books = author.book_set.all()這里從Author到Book的反向關系式通過Author.book_set屬性來表示的(這是一個manager對象),是由ORM自動添加的,
可以通過在 ForeignKey里指定 related_name參數來改變它的名字。比如:
class Book(models.Model):    ... ...	... ...	author = models.ForeignKey("Author", related_name = "books")... ...... ...#獲取這個作者所有的書books = author.books.all()對簡單的對象層次來說,
related_name不是必需的,但是更復雜的關系里,比如當有多個ForeignKey的時候就一定要指定了。
上面的例子假設的是一本書只有一個作者,一個作者有多本書,所以是多對一的關系;但是如果一本書也有多個作者呢?
這就是多對多的關系;由于SQL沒有定義這種關系,必須通過外鍵用它能理解的方式實現(xiàn)多對多
這里Django提供了第二種關系對象映射變量ManyToManyField
語法上來講, 這和 ForeignKey是一模一樣的,你在關系的一端定義,把要關聯(lián)的類傳遞進來,ORM會自動為另一端生成使用這個關系必要的方法和屬性
不過由于 ManyToManyField的特性,在哪一端定義它通常都沒有關系,因為這個關系是對稱的
class Author(models.Model):    name = models.CharField(max_length=100)class Book(models.Model):    title = models.CharField(max_length=100)	authors = models.ManyToManyField(Author)#獲取一本書book = Book.objects.get(title="Python Web Dev Django")#獲取該書所有的作者authors = Book.author_set.all()#獲取第三個作者出版過的所有的書books = authors[2].book_set.all()ManyToManyField的秘密在于它在背后創(chuàng)建了一張新的表來滿足這類關系的查詢的需要,而這張表用的則是SQL外鍵,其中每一行都代表了兩個對象的一個關系,同時包含了兩端的外鍵
這張查詢表在Django
ORM中一般是隱藏的,不可以單獨查詢,只能通過關系的某一端查詢;不過可以在
MTMField上指定一個特殊的選項through來指向一個顯式的中間模型類,更方便你的手動管理關系的兩端
class Author(models.Model):    name = models.CharField(max_length=100)class Book(models.Model):    title = models.CharField(max_length=100)	authors = models.ManyToManyField(Author, through = "Authoring")class Authoring(models.Model):    collaboration_type = models.CharField(max_length=100)	book = model.ForeignKey(Book)	author = model.ForeignKey(Author)查詢Author和Book的方法和之前完全一樣,另外還能構造對authoring的查詢
chan_essay_compilations = Book.objects.filter(    author__name__endswith = 'Chun'	authoring__collaboration_type = 'essays')如此,Django在創(chuàng)建關系的能力上就顯得更靈活了
類似的,Django提供了OneToOneField屬性,幾乎和ForeignKey一樣,接受一個參數(要關聯(lián)的類或者"self"),同樣也接受一個可選參數related_name,這樣就可以在兩個相同的類里區(qū)分出多個這樣的關系來。
不同的是,OTOField沒有在反向關系中添加reverse manager,而只是增加了一個普通屬性而已,因為關系的另一端一定只有一個對象。
這種關系最常用的是用來支持對象組合或者是擁有關系,所以相比現(xiàn)實世界,它更加面向對象一點。
在Django直接支持模型繼承(model inheritance)之前,OTOField主要是用來實現(xiàn)模型繼承, 而現(xiàn)在,則是轉向對這個特性的幕后支持了。
限制關系
關于定義關系的最后一點, ForeignKey 和 MTMField 都可以指定一個>limit_choices_to參數,這個參數接受一個字典,鍵值對是查詢的關鍵字和值
class Author(models.Model):    name = models.CharField(max_length=100)class SmithBook(models.Model):    title = models.CharField(max_length=100)	authors = models.ManyToManyField(Author, limit_choices_to={	    'name__endswith' : 'Smith'	})這個例子中,Book模型就只能和姓Smith的Authors類一起工作。當然這個問題最好用另一種解決方案--ModelChoiceField,ModelMultipleChoiceField
Django 的ORM中一個相對新的特性就是 模型繼承 model inheritance;
兩個模型之間除了外鍵以及其他關系之外,還可以和普通的、非ORM的Python類一樣,通過從另一個模型繼承來定義模型。
模型繼承,子類通過添加或者是重寫變量來和父類區(qū)分開來,而不需要重寫整個類的定義。
Django目前支持2種不同的繼承方式,每種都有自身的優(yōu)缺點:
class Author(models.Model):    name = models.CharField(max_length=100)class Book(models.Model):    title = models.CharField(max_length=100)	genre = models.CharField(max_length=100)	num_pages = models.IntergerField()	authors = models.ManyToManyField(Author)	def __unicode__(self):	    return self.title    class Meta:	    abstract = Trueclass SmithBook(Book):    authors = models.ManyToManyField(Author, limit_choices_to = {	    'name_endswith': 'Smith'	})這個例子,抽象基礎類方式繼承,純Python的繼承, 允許重構Python模型的定義,這樣變量和方法都可以從基類中繼承下來。然而在數據庫和查詢層上并沒有基類的概念,子類在數據庫的表其實還是復制了基類的一個變量,而不是為基類創(chuàng)建一張額外的數據表
另外,這里代碼的關鍵是 abstract = True設置, 指明了Book是一個抽象基礎類,只是用來為它實際的模型子類提供屬性而存在的。
再說說多表繼承, 同樣,還是會用到Python的 類繼承, 但是不再需要 abstract = True這個 Meta類選項了。
在檢查模型實例或是查詢的時候,多表繼承和前面看到的一樣,子類會從父類中繼承所有的屬性和方法
主要的區(qū)別在于,底層的機制。在這兒,父類是擁有自己數據表的完整的Django模型,可以正常的實例化,同時還能把自己的屬性借給子類,其實,這是通過自動在子類和父類之間設置了一個OneToOneField,以及幕后一些小手段把兩個對象連在一起實現(xiàn)的,所以,子類才能繼承父類的屬性。
因此,多表繼承其實就是對普通的has-a關系(或者說對象組合)的一個方便的包裝
class Author(models.Model):    name = models.CharField(max_length=100)class Book(models.Model):    title = models.CharField(max_length=100)	genre = models.CharField(max_length=100)	num_pages = models.IntegerField()	authors = models.ManyToManyField(Author)	def __unicode__(Book):	    return self.titleclass SmithBook(Book):    authors = models.ManyToManyField(Author, limit_choices_to={	    'name_endswith':'Smith'	})這里唯一的不同是,沒有了Meta abstract 選項。那結果是什么呢?
在一個空數據庫和這個models.py文件上運行 manage.py syncdb會創(chuàng)建三張表 Author, Book, SmithBook
而抽象基礎類的情況下,只創(chuàng)建了 Author, SmithBook 兩張表。
注意 SmithBook 實例得到的 book_ptr屬性會指向和他們組合的Book實例,而屬于SmithBook的Book實例會得到一個 smithbook屬性。
之所以說多表繼承更適合我們這個Smithbook的例子,因為我們可以同時實例化普通的Book對象以及SmithBook對象。而實際應用過程中,也是多表繼承要比抽象基礎類好用的多。
模型里定義的變量fields和關系relationships提供了數據庫的布局以及稍后查詢模型時要用的變量名--經常你還需要添加__unicode__ 和 get_absolute_url 方法或是重寫 內置的 save 和 delete方法。
然而,模型的定義還有第三個方面--告知Django關于這個模型的各種元數據信息的嵌套類Meta
Meta類處理的是模型的各種元數據的使用和顯示:
class Person(models.Model):    first = models.CharField(max_length=100)	last = models.CharField(max_length=100)	middle = models.CharField(max_length=100, blank=True)	class Meta:	    ordering = ['last', 'first', 'middle']	    unique_together = ['first', 'last', 'middle']	    #Django默認的復數形式是加 s,這里不適用	    verbose_name_plural = "people"創(chuàng)建所有的應用程序所需要的數據表
并不是對整個DB進行一次完整的同步,只是保證所有的模型類都有對應的數據表,在必要的時候創(chuàng)建為模型創(chuàng)建新的表,但是不會修改已經存在的表
因此,如果你創(chuàng)建模型,運行syncdb將它載入DB,然而改變這個模型時,syncdb不會試圖去和DB協(xié)調這些改動,需要程序員自己去
原創(chuàng)文章:http://m.survivalescaperooms.com/ganiks
載入初始數據, 和sqlcustom類似,但是沒有原始SQL
把現(xiàn)有數據庫里的數據輸出為JSON、xml格式
總是附在模型類里,除非有特別指定,每個模型類都會展示一個objects屬性,它構成了這個模型在數據庫所有的基本查詢
Manager是從數據庫獲取信息的門戶
模型類實例的列表,或者說是數據庫行記錄的列表
books_about_trees = Book.objects.filter(title__contain="Trees")SELECT * FROM myapp_book WHERE title LIKE "%Tree%" john_does = Person.objects.filter(last="Doe", first="John")everyone = Person.objects.all()之前提到的Meta嵌套類里定義的哪些各種和查詢相關的選項都會影響所生成的SQL
ordering->ORDER BY
QuerySet被當做一個數據庫查詢的始發(fā)端,接受動態(tài)的關鍵字參數然后轉化成合適的SQL
QuerySet像一個列表,實現(xiàn)了一部分列表的接口
最好不要把QuerySet通過list函數轉換成一個真正的列表,因為如果QuerySet很大的話,會導致內存或者數據庫很大的負擔
QuerySet是懶惰的,只有在必要時才會去執(zhí)行數據庫查詢
組合QuerySet查詢
Person.objects.filter(last="Doe").filter(first="John").filter(middle="Quincy")
overdue_books = book_queryset.filter(due_data_lt=datetime.now())
nonfictionsmithBook.objects.fitler(author__last="Smith").exclude(genre="Fiction")
all_sorted_first = Person.objects.all().order_by('first')all_sorted_first_five = Person.objects.all().order_by('first')[:5]sorted_by_state = Person.objects.all().order_by('address__state', 'last')Person有一個FK包含了一個Address, Address中包含一個state變量,這里希望按照state,再按照姓氏排序
用Extra調整SQL
通過initial SQL,將模式定義命令存放在Django項目中,當Django的工具來創(chuàng)建或者重新創(chuàng)建DB時,*.sql會被包含近來
myproject/myapp/sql/triggers.sql類似initial SQL,每個Django項目有一個fixtures目錄,里面存放XML YAML 或者JSON數據文件,在運行數據庫創(chuàng)建或者重設命令的時候會運行
這個特性的主要作用在于比如要將數據從PostgreSQL導出數據和導入數據
myproject/myapp/fixtures/initial_data.json導入django.db中定義的connection對象,獲取數據庫游標進行查詢
cursor = connection.cursor()cursor.execute("SELECT first, last FROM myapp_person WHERE last='Doe'")doe_rows = cursor.fetchall()本文結束。
附件:
思維導圖全圖:http://images.VEVb.com/VEVb_com/ganiks/618830/o_%E6%A8%A1%E5%9E%8B.JPG
新聞熱點
疑難解答