1 自省(運行時自動識別數據庫)會導致過載和有數據完整性問題
2 把數據模型用代碼的方式表述來讓你可以容易對它們進行版本控制。 這樣,你可以很容易瞭解數據層 的變
動情況。
3 SQL只能描述特定類型的數據字段。 例如,大多數數據庫都沒有專用的字段類型來描述Email地址
而用Django的模型可以做到這一點。 好處就是高級的數據類型帶來更高的效率和更好的代碼複用
4 SQL還有在不同數據庫平臺的兼容性問題。 發佈Web應用的時候,使用Python模塊描述數據庫結構信息可
以避免爲MySQL, PostgreSQL, and SQLite編寫不同的CREATE TABLE。
要解決的問題:
Python代碼和數據庫表的同步問題。 如果你修改了一個Django模型, 你要自己來修改數據庫來保證和模型同步
一 創建MSSQL數據庫 bookdb
建立project
django-admin.py startproject mysite
二 配置settings.py的DATABASES配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'bookdb', # Or path to database file if using sqlite3.
'USER': 'root', # Not used with sqlite3.
'PASSWORD': '423423', # Not used with sqlite3.
'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
一旦在輸入了那些設置並保存之後應當測試一下你的配置。 我們可以在`` depot`` 項目目錄下執行`
python manage.py shell
來進行測試。(是以正確Django配置啓用Python交互解釋器的一種方法。 這個方法在這裏是很有必要的,
因爲Django需要知道加載 哪個配置文件來獲取數據庫連接信息。)
輸入下面這些命令來測試你的數據庫配置:
>>> from django.db import connection
>>> cursor = connection.cursor()
三 創建模型
1 在項目中創建一個app
系統對app有一個約定: 如果你使用了Django的數據庫層(模型),你 必須創建一個Django app。
模型必須存放在apps中。 因此,爲了開始建造 我們的模型,我們必須創建一個新的app
python manage.py startapp books
2 創建module.py:
1)首先要注意的事是每個數據模型都是 django.db.models.Model 的子
類。它的父類 Model 包含了所有必要的和數據庫交互的方法,並提供了一個簡潔漂亮的定義數據庫字段的語
法。
2)每個模型相當於單個數據庫表,每個屬性也是這個表中的一個字段。 屬性名就是字段名,它的類型(例如
CharField )相當於數據庫的字段類型 (例如 varchar )
3)“每個數據庫表對應一個類”這條規則的例外情況是多對多關係。 在我們的範例模型中, Book 有一個
多對多字段 叫做 authors 。 該字段表明一本書籍有一個或多個作者,但 Book 數據庫表卻並沒有 authors 字
段。 相反,Django創建了一個額外的表(多對多連接表)來處理書籍和作者之間的映射關係
###自定義表屬性相關
4)我們並沒有顯式地爲這些模型定義任何主鍵。 除非你單獨指明,否則Django會自動爲每
個模型生成一個自增長的整數主鍵字段每個Django模型都要求有單獨的主鍵。id
5)默認的字段名與屬性名相同,可以通過字段的db_column屬性自定義,如
where = models.CharField(max_length=400, db_column='place')
6)除了ID字段,其他字段默認不建立索引,可以通過字段的db_index屬性自定義,如
publish_date= models.DateField(db_index=True)
7)自定義ID
如果不指定ID,默認創建一個類型爲IntegerField的id字段,可以通過字段的primary_key屬性指定自己的ID字段。如:
key = models.IntegerField(primary_key=True)
8)自定義表名
默認的表名是appName_modelName,在Model類的Meta中可以通過db_table屬性改變默認的表名。
9) 自定義表空間
對於索引字段,默認在settings的DEFAULT_INDEX_TABLESPACE設定的表空間中建立索引,可以通過字段的db_tablespace屬性指定,如
publish_date= models.DateField(db_index=True,db_tablespace='another_tbs')
對於Model類,通過Meta中的db_tablespace屬性指定特定的表空間。
10)自定義關聯關係
1)外鍵默認關聯到對方的主鍵字段,可以通過外鍵的to_field指定關聯到的字段,如
to_model = models.ForeighKey(ToModel,to_field='key')
2)對於many-to-many關聯,Django會創建一個關聯表,默認表名是兩個表名通過下劃線連接起來。
可以通過ManyToManyField的db_table指定關聯表的表名。
3)對於many-to-many關聯,如果不想使用Django創建的關聯表,可以通過ManyToManyField的through屬性指定到一個已存在的Model類。
3 模型安裝(生成數據庫表)
完成這些代碼之後,現在讓我們來在數據庫中創建這些表。 要完成該項工作,
1)第一步是在 Django 項目中 激活這些模型。 將 books app 添加到配置文件的已安裝應用列表中即可完成此步驟。
2)驗證模型的有效性:validate 命令檢查你的模型的語法和邏輯是否正確。
python manage.py validate
3)運行下面的命令來生成 CREATE TABLE 語句
python manage.py sqlall books
sqlall 命令並沒有在數據庫中真正創建數據表,只是把SQL語句段打印出來,這樣你可以看到Django究竟會做
些什麼。 如果你想這麼做的話,你可以把那些SQL語句複製到你的數據庫客戶端執行,或者通過Unix管道直接
進行操作(例如,`` python manager.py sqlall books | psql mydb`` )。
4)Django提供了一種更爲簡易的提交SQL語句至數據庫的方法: `` syncdb`` 命令
python manage.py syncdb
執行這個命令後,將看到類似以下的內容:
Creating table books_publisher
Creating table books_author
Creating table books_book
Installing index for books.Book model
syncdb 命令是同步你的模型到數據庫的一個簡單方法。 它會根據 INSTALLED_APPS 裏設置的app來檢查數據庫,
如果表不存在,它就會創建它。 需要注意的是, syncdb 並 不能將模型的修改或刪除同步到數據庫;
如果你修改或刪除了一個模型,並想把它提交到數據庫,syncdb並不會做出任何處理。
如果你再次運行 python manage.py syncdb ,什麼也沒發生,因爲你沒有添加新的表,運行python manage.py syncdb總是安全的,因爲它不會重複執行SQL語句。
如果你有興趣,花點時間用你的SQL客戶端登錄進數據庫服務器看看剛纔Django創建的數據表。 你可以手動啓
動命令行客戶端(例如,執行PostgreSQL的`` psql`` 命令),也可以執行 `` python manage.py dbshell`` ,
這個命令將依據`` DATABASE_SERVER`` 的裏設置自動檢測使用哪種命令行客戶端。
5)基本數據訪問
python manage.py shell 並輸入
下面的內容試試看:
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
... city='Berkeley', state_province='CA', country='U.S.A.',
... website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
這短短几行代碼幹了不少的事。 這裏簡單的說一下:
1)首先,導入Publisher模型類, 通過這個類我們可以與包含 出版社 的數據表進行交互。
2)接着,創建一個`` Publisher`` 類的實例並設置了字段`` name, address`` 等的值。
3)調用該對象的 save() 方法,將對象保存到數據庫中。 Django 會在後臺執行一條 INSERT 語句。
4)最後,使用`` Publisher.objects`` 屬性從數據庫取出出版商的信息,這個屬性可以認爲是包含出版商的記
錄集。 這個屬性有許多方法, 這裏先介紹調用`` Publisher.objects.all()`` 方法獲取數據庫中`` Publisher``
類的所有對象。這個操作的幕後,Django執行了一條SQL `` SELECT`` 語句。
這裏有一個值得注意的地方,在這個例子可能並未清晰地展示。 當你使用Django modle API創建對象時
Django並未將對象保存至數據庫內,除非你調用`` save()`` 方法:
如果需要一步完成對象的創建與存儲至數據庫,就使用`` objects.create()`` 方法
6)添加模塊的字符串表現
爲Publisher 對象添加一個方法 __unicode__() 。 __unicode__() 方法告
訴Python如何將對象以unicode的方式顯示出來。
爲以上三個模型添加__unicode__()方法後,就可以看到效
果了:
爲了讓我們的修改生效,先退出Python Shell,然後再次運行 python manage.py shell 進入。(這是保證代碼
修改生效的最簡單方法。)現在`` Publisher``對象列表容易理解多了。
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]
請確保你的每一個模型裏都包含 __unicode__() 方法,這不只是爲了交互時方便,也是因爲 Django會在其他
一些地方用 __unicode__() 來顯示對象。
最後, __unicode__() 也是一個很好的例子來演示我們怎麼添加 行爲 到模型裏。 Django的模型不只是爲對象
定義了數據庫表的結構,還定義了對象的行爲。 __unicode__() 就是一個例子來演示模型知道怎麼顯示它們自
己。
四 模型操作(一般在view.py進行)
1 插入數據
先使用一些關鍵參數創建對象實例,如下:
>>> p = Publisher(name='Apress',
... address='2855 Telegraph Ave.',
... city='Berkeley',
... state_province='CA',
... country='U.S.A.',
website='http://www.apress.com/')
>>> p.save()
因爲 Publisher 模型有一個自動增加的主鍵 id ,所以第
並把它賦值給這個對象實例:
>>> p.id
52 # this will differ based on your own data
2 查詢數據
1)filter:獲取列表
你可以傳遞多個參數到 filter() 來縮小選取範圍:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]
在 name 和 contains 之間有雙下劃線。和Python一樣,Django也使用雙下劃線來表明會進行一些魔術般的
操作。這裏,contains部分會被Django翻譯成LIKE語句:
>>> Publisher.objects.filter(name__contains="press")
[<Publisher: Apress>]
其他的一些查找類型有:icontains(大小寫無關的LIKE),startswith和endswith, 還有range(SQLBETWEEN查
詢)。
2)get:獲取單個對象
上面的例子中`` filter()`` 函數返回一個記錄集,這個記錄集是一個列表。 相對列表來說,有些時候我們更需要
獲取單個的對象, `` get()`` 方法就是在此時使用的:(一般是主鍵作爲查詢)
>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>
這樣,就返回了單個對象,而不是列表(更準確的說,QuerySet)。 所以,如果結果是多個對象,會導致拋出
異常:
3)數據排序
如果需要以多個字段爲標準進行排序(第二個字段會在第一個字段的值相同的情況下被使用到),使用多個參
數就可以了,如下:
>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]
我們還可以指定逆向排序,在前面加一個減號 ‐ 前綴:
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
缺省排序
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
現在,讓我們來接觸一個新的概念。 class Meta,內嵌於 Publisher 這個類的定義中(如果 class Publisher
是頂格的,那麼 class Meta 在它之下要縮進4個空格--按 Python 的傳統 )。你可以在任意一個 模型 類中
使用 Meta 類,來設置一些與特定模型相關的選項。 在 附錄B 中有 Meta 中所有可選項的完整參考,現在,我
關注 ordering 這個選項就夠了。 如果你設置了這個選項,那麼除非你檢索時特意額外地使用了 order_by(),
否則,當你使用 Django 的數據庫 API 去檢索時,Publisher對象的相關返回值默認地都會按 name 字段排序。
4)我們已經知道如何對數據進行過濾和排序。 當然,通常我們需要同時進行過濾和排序查詢的操作。 因此,你可
以簡單地寫成這種“鏈式”的形式:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
5)限制返回的數據
另一個常用的需求就是取出固定數目的記錄。 想象一下你有成千上萬的出版商在你的數據庫裏, 但是你只想顯
示第一個。 你可以使用標準的Python列表裁剪語句:
>>> Publisher.objects.order_by('name')[0]
<Publisher: Apress>
類似的,你可以用Python的range-slicing語法來取
>>> Publisher.objects.order_by('name')[0:2]
3 更新數據
我們可以看到Django的save()方法更新了不僅僅是name列的值,還有更新了所有的列。 若
name以外的列有可能會被其他的進程所改動的情況下,只更改name列顯然是更加明智的。 更改某一指定的
列,我們可以調用結果集(QuerySet)對象的update()方法: 示例如下:
>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
與之等同的SQL語句變得更高效,並且不會引起競態條件。
UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;
update()方法對於任何結果集(QuerySet)均有效,這意味着你可以同時更新多條記錄。 以下示例演示如何
將所有Publisher的country字段值由’U.S.A’更改爲’USA’:
>>> Publisher.objects.all().update(country='USA')
2
update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2。
4 刪除數據
>>> Publisher.objects.filter(name='Apress Publishing').delete()
>>> Publisher.objects.filter(country='USA').delete()
>>> Publisher.objects.all().delete()
>>> Publisher.objects.all()
[]
刪除數據時要謹慎! 爲了預防誤刪除掉某一個表內的所有數據,Django要求在刪除表內所有數據時顯示使用
all()。
五 數據模型高級進階
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
1 訪問外鍵(Foreign Key)值
當你獲取一個ForeignKey 字段時,你會得到相關的數據模型對象。 例如:
>>> b = Book.objects.get(id=1)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
對於用`` ForeignKey`` 來定義的關係來說,在關係的另一端也能反向的追溯回來,
。通過一個`` publisher`` 對象,直接獲取 books ,用 publisher.book_set.all() ,如
>>> p = Publisher.objects.get(name='O\'Reilly')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
實際上,book_set 只是一個 QuerySet(參考第5章的介紹),所以它可以像QuerySet一樣,能實現數據過濾和分
切,例如:
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.filter(name__icontains='django')
[<Book: The Django Book>, <Book: Pro Django>]
屬性名稱book_set是由模型名稱的小寫(如book)加_set組成的。
2 訪問多對多值(Many-to-Many Values)
多對多和外鍵工作方式相同,只不過我們處理的是QuerySet而不是模型實例。
>>> b = Book.objects.get(id=1)
>>> b.authors.all()
[<Author: Apress Holovaty>]
>>> b.authors.filter(first_name='Apress')
[<Author: Adrian Holovaty>]
>>> b.authors.filter(first_name='Adam')
[]
反向查詢也可以。 要查看一個作者的所有書籍,使用author.book_set ,就如這樣:
>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>]
這裏,就像使用 ForeignKey字段一樣,屬性名book_set是在數據模型(model)名後追加_set。
3 更改數據庫模式(Database Schema)
在我們在第5章介紹 syncdb 這個命令時, 我們注意到 syncdb僅僅創建數據庫裏還沒有的表,它 並不 對你數據模
型的修改進行同步,也不處理數據模型的刪除。 如果你新增或修改數據模型裏的字段,或是刪除了一個數據模型,
你需要手動在數據庫裏進行相應的修改。
當處理模型修改的時候,將Django的數據庫層的工作流程銘記於心是很重要的。
1)如果模型包含一個未曾在數據庫裏建立的字段,Django會報出錯信息。 當你第一次用Django的數據庫
API請求表中不存在的字段時會導致錯誤(就是說,它會在運行時出錯,而不是編譯時)。
2) Django不關心數據庫表中是否存在未在模型中定義的列。
3)Django不關心數據庫中是否存在未被模型表示的表格。
改變模型的模式架構意味着需要按照順序更改Python代碼和數據庫。
A 添加字段
當要向一個產品設置表(或者說是model)添加一個字段的時候,要使用的技巧是利用Django不關心表裏是否包
含model裏所沒有的列的特性。 策略就是現在數據庫里加入字段,然後同步Django的模型以包含新字段。
首先,進入開發環境(也就是說,不是在發佈環境裏):
1. 在你的模型裏添加字段。
2. 運行 manage.py sqlall [yourapp] 來測試模型新的 CREATE TABLE 語句。 注意爲新字段的列定義。
3. 開啓你的數據庫的交互命令界面(比如, psql 或mysql , 或者可以使用 manage.py dbshell )。 執行
ALTER TABLE 語句來添加新列。
4. 使用Python的manage.py shell,通過導入模型和選中表單(例如, MyModel.objects.all()[:5] )來驗證
新的字段是否被正確的添加 ,如果一切順利,所有的語句都不會報錯。
然後在你的產品服務器上再實施一遍這些步驟。
1. 啓動數據庫的交互界面。
2. 執行在開發環境步驟中,第三步的ALTER TABLE語句。
3. 將新的字段加入到模型中。 如果你使用了某種版本控制工具,並且在第一步中,已經提交了你在開發環境
上的修改,現在,可以在生產環境中更新你的代碼了(例如,如果你使用Subversion,執行svn update。
4. 重新啓動Web server,使修改生效。
讓我們實踐下,比如添加一個num_pages字段到第五章中Book模型。首先,我們會把開發環境中的模型改成
如下形式:
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
def __unicode__(self):
return self.title
(注意 閱讀第六章的“設置可選字段”以及本章下面的“添加非空列”小節以瞭解我們在這裏添
加blank=True和null=True的原因。)
然後,我們運行命令manage.py sqlall books 來查看CREATE TABLE語句。 語句的具體內容取決與你所使用
的數據庫, 大概是這個樣子:
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
"publication_date" date NOT NULL,
"num_pages" integer NULL
);
新加的字段被這樣表示:
"num_pages" integer NULL
接下來,我們要在開發環境上運行數據庫客戶端,如果是PostgreSQL,運行 psql,,然後,我執行如下語句。
ALTER TABLE books_book ADD COLUMN num_pages integer NULL;
添加 NULL 字段
>>> from mysite.books.models import Book
>>> Book.objects.all()[:5]
如果沒有異常發生,我們將切換到生產服務器,然後在生產環境的數據庫中執行命令ALTER TABLE 然後我們更新
生產環境中的模型,最後重啓web服務器。
B 刪除字段
刪除字段,然後重新啓動你的web服務器。
用以下命令從數據庫中刪除字段:
ALTER TABLE books_book DROP COLUMN num_pages;
C 刪除多對多關聯字段
從你的模型中刪除ManyToManyField,然後重啓web服務器。
用下面的命令從數據庫刪除關聯表:
DROP TABLE books_book_authors;
D 刪除模型
從文件中刪除你想要刪除的模型,然後重啓web 服務器models.py
然後用以下命令從數據庫中刪除表:
DROP TABLE books_book;
當你需要從數據庫中刪除任何有依賴的表時要注意(也就是任何與表books_book有外鍵的表 )。
4 Managers
在語句Book.objects.all()中,objects是一個特殊的屬性,需要通過它查詢數據庫。在第5章,我們只是簡要地說這是模塊的manager 。
模塊manager是一個對象,Django模塊通過它進行數據庫查詢。
每個Django模塊至少有一個manager,你可以創建自定義manager以定製數據庫訪問。
下面是你創建自定義manager的兩個原因: 增加額外的manager方法,和/或修改manager返回的初始QuerySet。
1)增加額外的Manager方法
增加額外的manager方法是爲模塊添加表級功能的首選辦法。 (至於行級功能,也就是隻作用於模型對象實例
的函數,一會兒將在本章後面解釋。)
例如,我們爲Book模型定義了一個title_count()方法,它需要一個關鍵字,返回包含這個關鍵字的書的數量。
# models.py
from django.db import models
# ... Author and Publisher models here ...
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
objects = BookManager()
def __unicode__(self):
return self.title
有了這個manager,我們現在可以這樣做:
>>> Book.objects.title_count('Django')
4
下面是編碼該注意的一些地方:
A 我們建立了一個BookManager類,它繼承了django.db.models.Manager。這個類只有一個title_count()方法,用來做統計。
注意,這個方法使用了self.filter(),此處self指manager本身。
B 我們把BookManager()賦值給模型的objects屬性。 它將取代模型的默認manager(objects)如果我們
沒有特別定義,它將會被自動創建。 我們把它命名爲objects,這是爲了與自動創建的manager保持一致。
C 爲什麼我們要添加一個title_count()方法呢?是爲了將經常使用的查詢進行封裝,這樣我們就不必重複編碼了。
2)修改初始Manager QuerySets
manager的基本QuerySet返回系統中的所有對象。 例如,`` Book.objects.all()`` 返回數據庫book中的所有書本。
我們可以通過覆蓋Manager.get_query_set()方法來重寫manager的基本QuerySet。 get_query_set()按照你的要求返回一個QuerySet。
例如,下面的模型有兩個manager。一個返回所有對像,另一個只返回作者標題The Django Book的書。
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
class DahlBookManager(models.Manager):
def get_query_set(self):
return super(DahlBookManager, self).get_query_set().filter(title='The Django Book')
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
objects = BookManager()
dahl_objects = DahlBookManager()
def __unicode__(self):
return self.title
在這個示例模型中,Book.objects.all()返回了數據庫中的所有書本,而Book.dahl_objects.all()只返回了一本.
注意我們明確地將objects設置成manager的實例,因爲如果我們不這麼做,那麼唯一可用的manager就將是dah1_objects。
當然,由於get_query_set()返回的是一個QuerySet對象,所以我們可以使用filter(),exclude()和其他一切
QuerySet的方法。 像這些語法都是正確的:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='The Django Book')
Book.dahl_objects.count()
這個例子也指出了其他有趣的技術: 在同一個模型中使用多個manager。
只要你願意,你可以爲你的模型添加多個manager()實例。
這是一個爲模型添加通用濾器的簡單方法。
class MaleManager(models.Manager):
def get_query_set(self):
return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager):
def get_query_set(self):
return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
這個例子允許你執行`` Person.men.all()`` ,`` Person.women.all()`` ,`` Person.people.all()`` 查詢,生成你
想要的結果。
如果你使用自定義的Manager對象,請注意,Django遇到的第一個Manager(以它在模型中被定義的位置爲準)
會有一個特殊狀態。 Django將會把第一個Manager 定義爲默認Manager ,Django的許多部分(但是不包括
admin應用)將會明確地爲模型使用這個manager。
結論是,你應該小心地選擇你的默認manager。因爲覆蓋get_query_set() 了,你可能接受到一個無用的返回對像,你必須避免這種情況。
5 模型方法
爲了給你的對像添加一個行級功能,那就定義一個自定義方法。
有鑑於manager經常被用來用一些整表操作(table-wide),模型方法應該只對特殊模型實例起作用。
這是一項在模型的一個地方集中業務邏輯的技術。
from django.contrib.localflavor.us.models import USStateField
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
state = USStateField() # Yes, this is U.S.‐centric...
def baby_boomer_status(self):
"Returns the person's baby‐boomer status."
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Post‐boomer"
def is_midwestern(self):
"Returns True if this person is from the Midwest."
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self):
"Returns the person's full name."
return u'%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
例子中的最後一個方法是一個property。 想了解更多關於屬性的信息請訪
問http://www.python.org/download/releases/2.2/descrintro/#property
這是用法的實例:
>>> p = Person.objects.get(first_name='Barack', last_name='Obama')
>>> p.birth_date
datetime.date(1961, 8, 4)
>>> p.baby_boomer_status()
'Baby boomer'
>>> p.is_midwestern()
True
>>> p.full_name # Note this isn't a method ‐‐ it's treated as an attribute
u'Barack Obama'
6 執行原始SQL查詢
有時候你會發現Django數據庫API帶給你的也只有這麼多,那你可以爲你的數據庫寫一些自定義SQL查詢。
你可以通過導入django.db.connection對像來輕鬆實現,它代表當前數據庫連接。
要使用它,需要通過connection.cursor()得到一個遊標對像。 然後,使用cursor.execute(sql, [params])來執行SQL語句,使
用cursor.fetchone()或者cursor.fetchall()來返回記錄集。
connection和cursor幾乎實現了標準Python DB-API,
你可以訪問` http://www.python.org/peps/pep-0249.html <http://www.python.org/peps/pep-0249.html>`__來獲取更多信息。
如果你對Python DB-API不熟悉,請注意在cursor.execute() 的SQL語句中使用`` “%s”`` ,而不要在SQL內直接添加參數。 如果你使
用這項技術,數據庫基礎庫將會自動添加引號,同時在必要的情況下轉意你的參數。
不要把你的視圖代碼和django.db.connection語句混雜在一起,
把它們放在自定義模型或者自定義manager法中是個不錯的主意。 比如,上面的例子可以被整合成一個自定義manager方法,就像這樣:
from django.db import connection, models
def get_first_name(self, last_name):
cursor = connection.cursor()
cursor.execute("""
SELECT DISTINCT first_name
FROM books_author
WHERE last_name = %s""", [last_name])
return [cursor.fetchone()]
然後這樣使用:
>>> Author.objects.get_first_name('Holovaty')
[(u'Apress',)]
7 與遺留數據庫整合
Django的數據庫層從Python代碼生成SQL schemas—但是對於遺留數據庫,你已經擁有SQL schemas.
這種情況,你需要爲已經存在的數據表創建model. 爲此,Django自帶了一個可以通過讀取您的數據表結構來生成
model的工具. 該輔助工具稱爲inspectdb,你可以通過執行manage.py inspectdb來調用它.
使用 inspectdb
python manage.py inspectdb
inspectdb工具自省你配置文件指向的數據庫,針對每一個表生成一個Django模型,然後將這些Python模型的
代碼顯示在系統的標準輸出裏面。
下面是一個從頭開始的針對一個典型的遺留數據庫的整合過程。 兩個前提條件是安裝了Django和一個傳統數
據庫。
1 通過運行django-admin.py startproject mysite (這裏 mysite 是你的項目的名字)建立一個Django項目。
2 編輯項目中的配置文件, mysite/settings.py ,告訴Django你的數據庫連接參數和數據庫名。
3 通過運行 python mysite/manage.py startapp myapp (這裏 myapp 是你的應用的名字)創建一個Django應
用。 這裏我們使用myapp 做爲應用名。
4 運行命令 python mysite/manage.py inspectdb。這將檢查DATABASE_NAME 數據庫中所有的表並打印出爲每
張表生成的模型類。 看一看輸出結果以瞭解inspectdb能做些什麼。
5 將標準shell的輸出重定向,保存輸出到你的應用的 models.py 文件裏:
python mysite/manage.py inspectdb > mysite/myapp/models.py
6 編輯 mysite/myapp/models.py 文件以清理生成的 models 並且做一些必要的自定義。
清理生成的Models
如你可能會預料到的,數據庫自省不是完美的,你需要對產生的模型代碼做些許清理。 這裏提醒一點關於處理生成 models 的要點:
1) 數據庫的每一個表都會被轉化爲一個model類 (也就是說,數據庫的表和model 類之間是一對一的映射)。
這意味着你需要爲多對多連接的表,重構其models 爲 ManyToManyField 的對象。
2)所生成的每一個model中的每個字段都擁有自己的屬性,包括id主鍵字段。 但是,請注意,如果某個model
沒有主鍵的話,那麼Django會自動爲其增加一個id主鍵字段。 這樣一來,你也許希望移除這樣的代碼行。
id = models.IntegerField(primary_key=True)
這樣做並不是僅僅因爲這些行是冗餘的,而且如果當你的應用需要向這些表中增加新記錄時,這些行會導
致某些問題。
3)每一個字段類型,如CharField、DateField, 是通過查找數據庫列類型如VARCHAR,DATE來確定的。如果
inspectdb無法把某個數據庫字段映射到model字段上,它會使用TextField字段進行代替,並且會在所生成
model字段後面加入Python註釋“該字段類型是猜的”。 對這要當心,如果必要的話,更改字段類型。
4)如果你的數據庫中的某個字段在Django中找不到合適的對應物,你可以放心的略過它。
Django模型層不要求必須導入你數據庫表中的每個列。
5)如果數據庫中某個列的名字是Python的保留字(比如pass、class或者for等),inspectdb會在每個屬性名
後附加上_field,並將db_column屬性設置爲真實的字段名(也就是pass,class或者for等)。
例如,某張表中包含一個INT類型的列,其列名爲for,那麼所生成的model將會包含如下所示的一個字段:
for_field = models.IntegerField(db_column='for')
inspectdb 會在該字段後加注 ‘字段重命名,因爲它是一個Python保留字’ 。
6)如果數據庫中某張表引用了其他表(正如大多數數據庫系統所做的那樣),你需要適當的修改所生成model
的順序,以使得這種引用能夠正確映射。
例如,model Book擁有一個針對於model Author的外鍵,那麼後
者應該先於前者被定義。如果你想創建一個指向尚未定義的model的關係,那麼可以使用包含model名的字
符串,而不是model對象本身。
7)對於PostgreSQL,MySQL和SQLite數據庫系統,inspectdb能夠自動檢測出主鍵關係。 也就是說,它會在合
適的位置插入primary_key=True。
而對於其他數據庫系統,你必須爲每一個model中至少一個字段插入這樣的語句,
因爲Django的model要求必須擁有一個primary_key=True的字段。
8)外鍵檢測僅對PostgreSQL,還有MySQL表中的某些特定類型生效。
至於其他數據庫,外鍵字段將在假定其爲INT列的情況下被自動生成爲IntegerField。
附錄:利用MYSQL數據庫的實驗結果
Microsoft Windows XP [版本 5.1.2600]
(C) 版權所有 1985-2001 Microsoft Corp.
E:\ChromeDown\depot>E:\ChromeDown\depot>cd E:\CODE_SVN\pysrc
E:\CODE_SVN\pysrc>django-admin.py startproject mysite
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> exit()
E:\CODE_SVN\pysrc\mysite>python manage.py startapp books
E:\CODE_SVN\pysrc\mysite>python manage.py validate
0 errors found
E:\CODE_SVN\pysrc\mysite>python manage.py sqlall books
BEGIN;
CREATE TABLE `books_publisher` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` varchar(30) NOT NULL,
`address` varchar(50) NOT NULL,
`city` varchar(60) NOT NULL,
`state_province` varchar(30) NOT NULL,
`country` varchar(50) NOT NULL,
`website` varchar(200) NOT NULL
)
;
CREATE TABLE `books_author` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`first_name` varchar(30) NOT NULL,
`last_name` varchar(40) NOT NULL,
`email` varchar(75) NOT NULL
)
;
CREATE TABLE `books_book_authors` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`book_id` integer NOT NULL,
`author_id` integer NOT NULL,
UNIQUE (`book_id`, `author_id`)
)
;
ALTER TABLE `books_book_authors` ADD CONSTRAINT `author_id_refs_id_9e7e386` FOREIGN KEY (`author_id`) REFERENCES `books_author` (`id`);
CREATE TABLE `books_book` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`title` varchar(100) NOT NULL,
`publisher_id` integer NOT NULL,
`publication_date` date NOT NULL
)
;
ALTER TABLE `books_book` ADD CONSTRAINT `publisher_id_refs_id_3a4d8b45` FOREIGN KEY (`publisher_id`) REFERENCES `books_publisher` (`id`);
ALTER TABLE `books_book_authors` ADD CONSTRAINT `book_id_refs_id_30430d9e` FOREIGN KEY (`book_id`) REFERENCES `books_book` (`id`);
CREATE INDEX `books_book_22dd9c39` ON `books_book` (`publisher_id`);
COMMIT;
E:\CODE_SVN\pysrc\mysite>python manage.py syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table books_publisher
Creating table books_author
Creating table books_book_authors
Creating table books_book
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): no
Installing custom SQL ...
Installing indexes ...
No fixtures found.
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',city='Berkeley', state_province='CA', country='U.S.A.',website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> >>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
>>> exit()
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]
>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>
>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
>>> Publisher.objects.order_by('name')[0:2]
[<Publisher: Apress>, <Publisher: O'Reilly>]
>>> Publisher.objects.order_by('name')[0]
<Publisher: Apress>
>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
1L
>>> Publisher.objects.all().update(country='USA')
2L
>>> Publisher.objects.filter(name='Apress Publishing').delete()
>>> Publisher.objects.all()
[<Publisher: O'Reilly>]
>>> from books.models import Publisher
>>> from books.models import Author
>>> p=Author(first_name='Apress', last_name='Holovaty',email='[email protected]')
>>> p.save()
>>> p.id
1L
>>> p1 = Author(first_name='Jacob', last_name='Kaplan-Moss',email='[email protected]')
>>> p1.save()
>>> p1.id
2L
>>> Author.objects.all()
[<Author: Apress Holovaty>, <Author: Jacob Kaplan-Moss>]
>>> from books.models import Book
>>> p1=Publisher.objects.get(id=2)
>>> a1=Author.objects.get(first_name='Apress')
>>> b1=Book(title='The Django Book',publisher=p1,publication_date='2010-5-5')
>>> b1.save()
>>> b1.authors.add(a1)
>>> b1.save()
>>> p1=Publisher.objects.get(id=2)
>>> a1=Author.objects.get(first_name='Jacob')
>>> b1=Book(title='Dive into Python',publisher=p1,publication_date='2010-5-5')
>>> b1.save()
>>> b1.authors.add(a1)
>>> b1.save()
>>> b = Book.objects.get(id=1)
>>> b.publisher
<Publisher: O'Reilly>
>>> p = Publisher.objects.get(name='O\'Reilly')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive into Python>]
>>> b = Book.objects.get(id=1)
>>> b.authors.all()
[<Author: Apress Holovaty>]
>>> b.authors.filter(first_name='Adrian')
[]
>>> b.authors.filter(first_name='Apress')
[<Author: Apress Holovaty>]
>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>]
>>> exit()
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Author
>>> Author.objects.get_first_name('Apress')
[None]
>>> Author.objects.get_first_name('Holovaty')
[(u'Apress',)]
>>>