在第三章,咱們講述了用 Django 建造網站的基本途徑: 創建視圖和 URLConf 。 正如咱們所闡述的,視圖負責處理一些主觀邏輯,而後返回響應結果。 做爲例子之一,咱們的主觀邏輯是要計算當前的日期和時間。html
在當代 Web 應用中,主觀邏輯常常牽涉到與數據庫的交互。 數據庫驅動網站 在後臺鏈接數據庫服務器,從中取出一些數據,而後在 Web 頁面用漂亮的格式展現這些數據。 這個網站也可能會向訪問者提供修改數據庫數據的方法。python
許多複雜的網站都提供了以上兩個功能的某種結合。 例如 Amazon.com 就是一個數據庫驅動站點的良好範例。 本質上,每一個產品頁面都是數據庫中數據以 HTML格式進行的展示,而當你發表客戶評論時,該評論被插入評論數據庫中。mysql
因爲先天具有 Python 簡單而強大的數據庫查詢執行方法,Django 很是適合開發數據庫驅動網站。 本章深刻介紹了該功能: Django 數據庫層。web
(注意: 儘管對 Django 數據庫層的使用中並不特別強調這點,可是咱們仍是強烈建議您掌握一些數據庫和 SQL 原理。 對這些概念的介紹超越了本書的範圍,但就算你是數據庫方面的菜鳥,咱們也建議你繼續閱讀。 你也許可以跟上進度,並在上下文學習過程當中掌握一些概念。)sql
正如第三章詳細介紹的那個在視圖中輸出 HTML 的笨方法(經過在視圖裏對文本直接硬編碼HTML),在視圖中也有笨方法能夠從數據庫中獲取數據。 很簡單: 用現有的任何 Python 類庫執行一條 SQL 查詢並對結果進行一些處理。shell
在本例的視圖中,咱們使用了 MySQLdb
類庫(能夠從 http://www.djangoproject.com/r/python-mysql/ 得到)來鏈接 MySQL 數據庫,取回一些記錄,將它們提供給模板以顯示一個網頁:數據庫
from django.shortcuts import render_to_response import MySQLdb def book_list(request): db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost') cursor = db.cursor() cursor.execute('SELECT name FROM books ORDER BY name') names = [row[0] for row in cursor.fetchall()] db.close() return render_to_response('book_list.html', {'names': names})
這個方法可用,但很快一些問題將出如今你面前:django
咱們將數據庫鏈接參數硬行編碼於代碼之中。 理想狀況下,這些參數應當保存在 Django 配置中。編程
咱們不得不重複一樣的代碼: 建立數據庫鏈接、建立數據庫遊標、執行某個語句、而後關閉數據庫。 理想狀況下,咱們所須要應該只是指定所需的結果。安全
它把咱們栓死在 MySQL 之上。 若是過段時間,咱們要從 MySQL 換到 PostgreSQL,就不得不使用不一樣的數據庫適配器(例如 psycopg
而不是 MySQLdb
),改變鏈接參數,根據 SQL 語句的類型可能還要修改SQL 。 理想狀況下,應對所使用的數據庫服務器進行抽象,這樣一來只在一處修改便可變換數據庫服務器。 (若是你正在創建一個開源的Django應用程序來儘量讓更多人使用的話,這個特性是很是適當的。)
正如你所期待的,Django數據庫層正是致力於解決這些問題。 如下提早揭示瞭如何使用 Django 數據庫 API 重寫以前那個視圖。
from django.shortcuts import render_to_response from mysite.books.models import Book def book_list(request): books = Book.objects.order_by('name') return render_to_response('book_list.html', {'books': books})
咱們將在本章稍後的地方解釋這段代碼。 目前而言,僅需對它有個大體的認識。
在鑽研更多代碼以前,讓咱們先花點時間考慮下 Django 數據驅動 Web 應用的整體設計。
咱們在前面章節提到過,Django 的設計鼓勵鬆耦合及對應用程序中不一樣部分的嚴格分割。 遵循這個理念的話,要想修改應用的某部分而不影響其它部分就比較容易了。 在視圖函數中,咱們已經討論了經過模板系統把業務邏輯和表現邏輯分隔開的重要性。 在數據庫層中,咱們對數據訪問邏輯也應用了一樣的理念。
把數據存取邏輯、業務邏輯和表現邏輯組合在一塊兒的概念有時被稱爲軟件架構的 Model-View-Controller (MVC)模式。 在這個模式中, Model 表明數據存取層,View 表明的是系統中選擇顯示什麼和怎麼顯示的部分,Controller 指的是系統中根據用戶輸入並視須要訪問模型,以決定使用哪一個視圖的那部分。
爲何用縮寫?
像 MVC 這樣的明肯定義模式的主要用於改善開發人員之間的溝通。 比起告訴同事,「讓咱們採用抽象的數據存取方式,而後單獨劃分一層來顯示數據,而且在中間加上一個控制它的層」,一個通用的說法會讓你收益,你只須要說:「咱們在這裏使用MVC模式吧。」。
Django 牢牢地遵循這種 MVC 模式,能夠稱得上是一種 MVC 框架。 如下是 Django 中 M、V 和 C 各自的含義:
M ,數據存取部分,由django數據庫層處理,本章要講述的內容。
V ,選擇顯示哪些數據要顯示以及怎樣顯示的部分,由視圖和模板處理。
C ,根據用戶輸入委派視圖的部分,由 Django 框架根據 URLconf 設置,對給定 URL 調用適當的 Python 函數。
因爲 C 由框架自行處理,而 Django 裏更關注的是模型(Model)、模板(Template)和視圖(Views),Django 也被稱爲 MTV 框架 。在 MTV 開發模式中:
M 表明模型(Model),即數據存取層。 該層處理與數據相關的全部事務: 如何存取、如何驗證有效性、包含哪些行爲以及數據之間的關係等。
T 表明模板(Template),即表現層。 該層處理與表現相關的決定: 如何在頁面或其餘類型文檔中進行顯示。
V 表明視圖(View),即業務邏輯層。 該層包含存取模型及調取恰當模板的相關邏輯。 你能夠把它看做模型與模板之間的橋樑。
若是你熟悉其它的 MVC Web開發框架,比方說 Ruby on Rails,你可能會認爲 Django 視圖是控制器,而 Django 模板是視圖。 很不幸,這是對 MVC 不一樣詮釋所引發的錯誤認識。 在 Django 對 MVC 的詮釋中,視圖用來描述要展示給用戶的數據;不是數據 如何展示 ,並且展示 哪些 數據。 相比之下,Ruby on Rails 及一些同類框架提倡控制器負責決定向用戶展示哪些數據,而視圖則僅決定 如何 展示數據,而不是展示 哪些 數據。
兩種詮釋中沒有哪一個更加正確一些。 重要的是要理解底層概念。
記住這些理念以後,讓咱們來開始 Django 數據庫層的探索。 首先,咱們須要作些初始配置;咱們須要告訴Django使用什麼數據庫以及如何鏈接數據庫。
咱們假定你已經完成了數據庫服務器的安裝和激活,而且已經在其中建立了數據庫(例如,用 CREATE DATABASE
語句)。 若是你使用SQLite,不須要這步安裝,由於SQLite使用文件系統上的獨立文件來存儲數據。
象前面章節提到的 TEMPLATE_DIRS
同樣,數據庫配置也是在Django的配置文件裏,缺省 是 settings.py
。 打開這個文件並查找數據庫配置:
DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = ''
配置綱要以下。
DATABASE_ENGINE
告訴Django使用哪一個數據庫引擎。 若是你在 Django 中使用數據庫,DATABASE_ENGINE
必須是 Table 5-1 中所列出的值。表 5-1. 數據庫引擎設置
設置 數據庫 所需適配器 `` postgresql`` PostgreSQL psycopg
1.x版, http://www.djangoproject.com/r/python-pgsql/1/。postgresql_psycopg2
PostgreSQL psycopg
2.x版, http://www.djangoproject.com/r/python-pgsql/。mysql
MySQL MySQLdb
, http://www.djangoproject.com/r/python-mysql/.sqlite3
SQLite 若是使用Python 2.5+則不須要適配器。 不然就使用 pysqlite
, http://www.djangoproject.com/r/python-sqlite/。oracle
Oracle cx_Oracle
, http://www.djangoproject.com/r/python-oracle/.要注意的是不管選擇使用哪一個數據庫服務器,都必須下載和安裝對應的數據庫適配器。 訪問表 5-1 中「所需適配器」一欄中的連接,可經過互聯網免費獲取這些適配器。 若是你使用Linux,你的發佈包管理系統會提供合適的包。 好比說查找`` python-postgresql`` 或者`` python-psycopg`` 的軟件包。
配置示例:
DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME
將數據庫名稱告知 Django 。 例如:
DATABASE_NAME = 'mydb'
若是使用 SQLite,請對數據庫文件指定完整的文件系統路徑。 例如:
DATABASE_NAME = '/home/django/mydata.db'
在這個例子中,咱們將SQLite數據庫放在/home/django目錄下,你能夠任意選用最合適你的目錄。
DATABASE_USER
告訴 Django 用哪一個用戶鏈接數據庫。 例如: 若是用SQLite,空白便可。
DATABASE_PASSWORD
告訴Django鏈接用戶的密碼。 SQLite 用空密碼便可。
DATABASE_HOST
告訴 Django 鏈接哪一臺主機的數據庫服務器。 若是數據庫與 Django 安裝於同一臺計算機(即本機),可將此項保留空白。 若是你使用SQLite,此項留空。此處的 MySQL 是一個特例。 若是使用的是 MySQL 且該項設置值由斜槓(
'/'
)開頭,MySQL 將經過 Unix socket 來鏈接指定的套接字,例如:
DATABASE_HOST = '/var/run/mysql'
一旦在輸入了那些設置並保存以後應當測試一下你的配置。 咱們能夠在`` mysite`` 項目目錄下執行上章所提到的`` python manage.py shell`` 來進行測試。 (咱們上一章提到過在,`` manager.py shell`` 命令是以正確Django配置啓用Python交互解釋器的一種方法。 這個方法在這裏是頗有必要的,由於Django須要知道加載哪一個配置文件來獲取數據庫鏈接信息。)
輸入下面這些命令來測試你的數據庫配置:
>>> from django.db import connection >>> cursor = connection.cursor()
若是沒有顯示什麼錯誤信息,那麼你的數據庫配置是正確的。 不然,你就得 查看錯誤信息來糾正錯誤。 表 5-2 是一些常見錯誤。
錯誤信息 | 解決方法 |
---|---|
You haven’t set the DATABASE_ENGINE setting yet. | 不要以空字符串配置`` DATABASE_ENGINE`` 的值。 表格 5-1 列出可用的值。 |
Environment variable DJANGO_SETTINGS_MODULE is undefined. | 使用`` python manager.py shell`` 命令啓動交互解釋器,不要以`` python`` 命令直接啓動交互解釋器。 |
Error loading _____ module: No module named _____. | 未安裝合適的數據庫適配器 (例如, psycopg 或 MySQLdb )。Django並不自帶適配器,因此你得本身下載安裝。 |
_____ isn’t an available database backend. | 把DATABASE_ENGINE 配置成前面提到的合法的數據庫引擎。 也許是拼寫錯誤? |
database _____ does not exist | 設置`` DATABASE_NAME`` 指向存在的數據庫,或者先在數據庫客戶端中執行合適的`` CREATE DATABASE`` 語句建立數據庫。 |
role _____ does not exist | 設置`` DATABASE_USER`` 指向存在的用戶,或者先在數據庫客戶端中執建立用戶。 |
could not connect to server | 查看DATABASE_HOST和DATABASE_PORT是否已正確配置,並確認數據庫服務器是否已正常運行。 |
你如今已經確認數據庫鏈接正常工做了,讓咱們來建立一個 Django app-一個包含模型,視圖和Django代碼,而且形式爲獨立Python包的完整Django應用。
在這裏要先解釋一些術語,初學者可能會混淆它們。 在第二章咱們已經建立了 project , 那麼 project 和 app 之間到底有什麼不一樣呢?它們的區別就是一個是配置另外一個是 代碼:
一個project包含不少個Django app以及對它們的配置。
技術上,project的做用是提供配置文件,比方說哪裏定義數據庫鏈接信息, 安裝的app列表,
TEMPLATE_DIRS
,等等。一個app是一套Django功能的集合,一般包括模型和視圖,按Python的包結構的方式存在。
例如,Django自己內建有一些app,例如註釋系統和自動管理界面。 app的一個關鍵點是它們是很容易移植到其餘project和被多個project複用。
對於如何架構Django代碼並無快速成套的規則。 若是你只是建造一個簡單的Web站點,那麼可能你只須要一個app就能夠了; 但若是是一個包含許多不相關的模塊的複雜的網站,例如電子商務和社區之類的站點,那麼你可能須要把這些模塊劃分紅不一樣的app,以便之後複用。
不錯,你能夠不用建立app,這一點應經被咱們以前編寫的視圖函數的例子證實了 。 在那些例子中,咱們只是簡單的建立了一個稱爲views.py
的文件,編寫了一些函數並在URLconf中設置了各個函數的映射。 這些狀況都不須要使用apps。
可是,系統對app有一個約定: 若是你使用了Django的數據庫層(模型),你 必須建立一個Django app。 模型必須存放在apps中。 所以,爲了開始建造 咱們的模型,咱們必須建立一個新的app。
在`` mysite`` 項目文件下輸入下面的命令來建立`` books`` app:
python manage.py startapp books
這個命令並無輸出什麼,它只在 mysite
的目錄裏建立了一個 books
目錄。 讓咱們來看看這個目錄的內容:
books/ __init__.py models.py tests.py views.py
這個目錄包含了這個app的模型和視圖。
使用你最喜歡的文本編輯器查看一下 models.py
和 views.py
文件的內容。 它們都是空的,除了 models.py
裏有一個 import。這就是你Django app的基礎。
咱們早些時候談到。MTV裏的M表明模型。 Django模型是用Python代碼形式表述的數據在數據庫中的定義。 對數據層來講它等同於 CREATE TABLE 語句,只不過執行的是Python代碼而不是 SQL,並且還包含了比數據庫字段定義更多的含義。 Django用模型在後臺執行SQL代碼並把結果用Python的數據結構來描述。 Django也使用模型來呈現SQL沒法處理的高級概念。
若是你對數據庫很熟悉,你可能立刻就會想到,用Python 和 SQL來定義數據模型是否是有點多餘? Django這樣作是有下面幾個緣由的:
自省(運行時自動識別數據庫)會致使過載和有數據完整性問題。 爲了提供方便的數據訪問API, Django須要以 某種方式 知道數據庫層內部信息,有兩種實現方式。 第一種方式是用Python明確地定義數據模型,第二種方式是經過自省來自動偵測識別數據模型。
第二種方式看起來更清晰,由於數據表信息只存放在一個地方-數據庫裏,可是會帶來一些問題。 首先,運行時掃描數據庫會帶來嚴重的系統過載。 若是每一個請求都要掃描數據庫的表結構,或者即使是 服務啓動時作一次都是會帶來不能接受的系統過載。 (有人認爲這個程度的系統過載是能夠接受的,而Django開發者的目標是儘量地下降框架的系統過載)。第二,某些數據庫,尤爲是老版本的MySQL,並未完整存儲那些精確的自省元數據。
編寫Python代碼是很是有趣的,保持用Python的方式思考會避免你的大腦在不一樣領域來回切換。 儘量的保持在單一的編程環境/思想狀態下能夠幫助你提升生產率。 不得不去重複寫SQL,再寫Python代碼,再寫SQL,…,會讓你頭都要裂了。
把數據模型用代碼的方式表述來讓你能夠容易對它們進行版本控制。 這樣,你能夠很容易瞭解數據層 的變更狀況。
SQL只能描述特定類型的數據字段。 例如,大多數數據庫都沒有專用的字段類型來描述Email地址、URL。 而用Django的模型能夠作到這一點。 好處就是高級的數據類型帶來更高的效率和更好的代碼複用。
SQL還有在不一樣數據庫平臺的兼容性問題。 發佈Web應用的時候,使用Python模塊描述數據庫結構信息能夠避免爲MySQL, PostgreSQL, and SQLite編寫不一樣的
CREATE TABLE
。
固然,這個方法也有一個缺點,就是Python代碼和數據庫表的同步問題。 若是你修改了一個Django模型, 你要本身來修改數據庫來保證和模型同步。 咱們將在稍後講解解決這個問題的幾種策略。
最後,咱們要提醒你Django提供了實用工具來從現有的數據庫表中自動掃描生成模型。 這對已有的數據庫來講是很是快捷有用的。 咱們將在第18章中對此進行討論。
在本章和後續章節裏,咱們把注意力放在一個基本的 書籍/做者/出版商 數據庫結構上。 咱們這樣作是由於 這是一個衆所周知的例子,不少SQL有關的書籍也經常使用這個舉例。 你如今看的這本書也是由做者 創做再由出版商出版的哦!
咱們來假定下面的這些概念、字段和關係:
一個做者有姓,有名及email地址。
出版商有名稱,地址,所在城市、省,國家,網站。
書籍有書名和出版日期。 它有一個或多個做者(和做者是多對多的關聯關係[many-to-many]), 只有一個出版商(和出版商是一對多的關聯關係[one-to-many],也被稱做外鍵[foreign key])
第一步是用Python代碼來描述它們。 打開由`` startapp`` 命令建立的models.py
並輸入下面的內容:
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() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
讓咱們來快速講解一下這些代碼的含義。 首先要注意的事是每一個數據模型都是 django.db.models.Model
的子類。它的父類 Model 包含了全部必要的和數據庫交互的方法,並提供了一個簡潔漂亮的定義數據庫字段的語法。 信不信由你,這些就是咱們須要編寫的經過Django存取基本數據的全部代碼。
每一個模型至關於單個數據庫表,每一個屬性也是這個表中的一個字段。 屬性名就是字段名,它的類型(例如 CharField
)至關於數據庫的字段類型 (例如 varchar
)。例如, Publisher
模塊等同於下面這張表(用PostgreSQL的 CREATE TABLE
語法描述):
CREATE TABLE "books_publisher" ( "id" serial 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 );
事實上,正如過一下子咱們所要展現的,Django 能夠自動生成這些 CREATE TABLE
語句。
「每一個數據庫表對應一個類」這條規則的例外狀況是多對多關係。 在咱們的範例模型中, Book
有一個 多對多字段
叫作 authors
。 該字段代表一本書籍有一個或多個做者,但 Book
數據庫表卻並無 authors
字段。 相反,Django建立了一個額外的表(多對多鏈接表)來處理書籍和做者之間的映射關係。
請查看附錄 B 瞭解全部的字段類型和模型語法選項。
最後須要注意的是,咱們並無顯式地爲這些模型定義任何主鍵。 除非你單獨指明,不然Django會自動爲每一個模型生成一個自增加的整數主鍵字段每一個Django模型都要求有單獨的主鍵。id
完成這些代碼以後,如今讓咱們來在數據庫中建立這些表。 要完成該項工做,第一步是在 Django 項目中 激活 這些模型。 將 books
app 添加到配置文件的已安裝應用列表中便可完成此步驟。
再次編輯 settings.py
文件, 找到 INSTALLED_APPS
設置。 INSTALLED_APPS
告訴 Django 項目哪些 app 處於激活狀態。 缺省狀況下以下所示:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', )
把這四個設置前面加#臨時註釋起來。 (這四個app是常用到的,咱們將在後續章節裏討論如何使用它們)。同時,註釋掉MIDDLEWARE_CLASSES的默認設置條目,由於這些條目是依賴於剛纔咱們剛在INSTALLED_APPS註釋掉的apps。 而後,添加`` ‘mysite.books’`` 到`` INSTALLED_APPS`` 的末尾,此時設置的內容看起來應該是這樣的:
MIDDLEWARE_CLASSES = ( # 'django.middleware.common.CommonMiddleware', # 'django.contrib.sessions.middleware.SessionMiddleware', # 'django.contrib.auth.middleware.AuthenticationMiddleware', ) INSTALLED_APPS = ( # 'django.contrib.auth', # 'django.contrib.contenttypes', # 'django.contrib.sessions', # 'django.contrib.sites', 'mysite.books', )
(就像咱們在上一章設置TEMPLATE_DIRS所提到的逗號,一樣在INSTALLED_APPS的末尾也需添加一個逗號,由於這是個單元素的元組。 另外,本書的做者喜歡在 每個 tuple元素後面加一個逗號,無論它是否是 只有一個元素。 這是爲了不忘了加逗號,並且也沒什麼壞處。)
'mysite.books'
指示咱們正在編寫的books
app。 INSTALLED_APPS
中的每一個app都使用 Python的路徑描述,包的路徑,用小數點「.」間隔。
如今咱們能夠建立數據庫表了。 首先,用下面的命令驗證模型的有效性:
python manage.py validate
validate
命令檢查你的模型的語法和邏輯是否正確。 若是一切正常,你會看到 0 errors found
消息。若是出錯,請檢查你輸入的模型代碼。 錯誤輸出會給出很是有用的錯誤信息來幫助你修正你的模型。
一旦你以爲你的模型可能有問題,運行 python manage.py validate
。 它能夠幫助你捕獲一些常見的模型定義錯誤。
模型確認沒問題了,運行下面的命令來生成 CREATE TABLE
語句(若是你使用的是Unix,那麼能夠啓用語法高亮):
python manage.py sqlall books
在這個命令行中, books
是app的名稱。 和你運行 manage.py startapp
中的同樣。執行以後,輸出以下:
BEGIN; CREATE TABLE "books_publisher" ( "id" serial 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" serial 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" ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED, "publication_date" date NOT NULL ) ; CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY, "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED, "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED, UNIQUE ("book_id", "author_id") ) ; CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id"); COMMIT;
注意:
自動生成的表名是app名稱( books
)和模型的小寫名稱 ( publisher
, book
, author
)的組合。你能夠參考附錄B重寫這個規則。
咱們前面已經提到,Django爲每一個表格自動添加加了一個 id
主鍵, 你能夠從新設置它。
按約定,Django添加 "_id"
後綴到外鍵字段名。 你猜對了,這個一樣是能夠自定義的。
外鍵是用 REFERENCES
語句明肯定義的。
這些 CREATE TABLE
語句會根據你的數據庫而做調整,這樣象數據庫特定的一些字段例如:(MySQL),auto_increment
(PostgreSQL),serial
(SQLite),都會自動生成。integer primary key
一樣的,字段名稱也是自動處理(例如單引號還好是雙引號)。 例子中的輸出是基於PostgreSQL語法的。
sqlall
命令並無在數據庫中真正建立數據表,只是把SQL語句段打印出來,這樣你能夠看到Django究竟會作些什麼。 若是你想這麼作的話,你能夠把那些SQL語句複製到你的數據庫客戶端執行,或者經過Unix管道直接進行操做(例如,`` python manager.py sqlall books | psql mydb`` )。不過,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
,什麼也沒發生,由於你沒有添加新的模型或者 添加新的app。所以,運行python manage.py syncdb
老是安全的,由於它不會重複執行SQL語句。
若是你有興趣,花點時間用你的SQL客戶端登陸進數據庫服務器看看剛纔Django建立的數據表。 你能夠手動啓動命令行客戶端(例如,執行PostgreSQL的`` psql`` 命令),也能夠執行 `` python manage.py dbshell`` ,這個命令將依據`` DATABASE_SERVER`` 的裏設置自動檢測使用哪一種命令行客戶端。 常言說,後來者居上。
一旦你建立了模型,Django自動爲這些模型提供了高級的Python API。 運行 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>]
這短短几行代碼幹了很多的事。 這裏簡單的說一下:
首先,導入Publisher模型類, 經過這個類咱們能夠與包含 出版社 的數據表進行交互。
接着,建立一個`` Publisher`` 類的實例並設置了字段`` name, address`` 等的值。
調用該對象的 save()
方法,將對象保存到數據庫中。 Django 會在後臺執行一條 INSERT
語句。
最後,使用`` Publisher.objects`` 屬性從數據庫取出出版商的信息,這個屬性能夠認爲是包含出版商的記錄集。 這個屬性有許多方法, 這裏先介紹調用`` Publisher.objects.all()`` 方法獲取數據庫中`` Publisher`` 類的全部對象。這個操做的幕後,Django執行了一條SQL `` SELECT`` 語句。
這裏有一個值得注意的地方,在這個例子可能並未清晰地展現。 當你使用Django modle API建立對象時Django並未將對象保存至數據庫內,除非你調用`` save()`` 方法:
p1 = Publisher(...) # At this point, p1 is not saved to the database yet! p1.save() # Now it is.
若是須要一步完成對象的建立與存儲至數據庫,就使用`` objects.create()`` 方法。 下面的例子與以前的例子等價:
>>> p1 = Publisher.objects.create(name='Apress', ... address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p2 = Publisher.objects.create(name="O'Reilly", ... address='10 Fawcett St.', city='Cambridge', ... state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> publisher_list = Publisher.objects.all() >>> publisher_list
固然,你確定想執行更多的Django數據庫API試試看,不過,仍是讓咱們先解決一點煩人的小問題。
當咱們打印整個publisher列表時,咱們沒有獲得想要的有用信息,沒法把````對象區分開來:
System Message: WARNING/2 (<string>
, line 872); backlink
Inline literal start-string without end-string.
System Message: WARNING/2 (<string>
, line 872); backlink
Inline literal start-string without end-string.
[<Publisher: Publisher object>, <Publisher: Publisher object>]
咱們能夠簡單解決這個問題,只須要爲Publisher
對象添加一個方法 __unicode__()
。 __unicode__()
方法告訴Python如何將對象以unicode的方式顯示出來。 爲以上三個模型添加__unicode__()
方法後,就能夠看到效果了:
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**
就象你看到的同樣, __unicode__()
方法能夠進行任何處理來返回對一個對象的字符串表示。 Publisher
和Book
對象的__unicode__()
方法簡單地返回各自的名稱和標題,Author
對象的__unicode__()
方法則稍微複雜一些,它將first_name
和last_name
字段值以空格鏈接後再返回。
對__unicode__()的惟一要求就是它要返回一個unicode對象 若是`` __unicode__()`` 方法未返回一個Unicode對象,而返回好比說一個整型數字,那麼Python將拋出一個`` TypeError`` 錯誤,並提示:」coercing to Unicode: need string or buffer, int found」 。
Unicode對象
什麼是Unicode對象呢?
你能夠認爲unicode對象就是一個Python字符串,它能夠處理上百萬不一樣類別的字符——從古老版本的Latin字符到非Latin字符,再到曲折的引用和艱澀的符號。
普通的python字符串是通過編碼的,意思就是它們使用了某種編碼方式(如 ASCII,ISO-8859-1或者UTF-8)來編碼。 若是你把奇特的字符(其它任何超出標準128個如0-9和A-Z之類的ASCII字符)保存在一個普通的Python字符串裏,你必定要跟蹤你的字符串是 用什麼編碼的,不然這些奇特的字符可能會在顯示或者打印的時候出現亂碼。 當你嘗試要將用某種編碼保存的數據結合到另一種編碼的數據中,或者你想要把它顯示在已經假定了某種編碼的程序中的時候,問題就會發生。 咱們都已經見到過網頁和郵件被???弄得亂七八糟。 ?????? 或者其它出如今奇怪位置的字符:這通常來講就是存在編碼問題了。
可是Unicode對象並無編碼。它們使用Unicode,一個一致的,通用的字符編碼集。 當你在Python中處理Unicode對象的時候,你能夠直接將它們混合使用和互相匹配而沒必要去考慮編碼細節。
Django 在其內部的各個方面都使用到了 Unicode 對象。 模型 對象中,檢索匹配方面的操做使用的是 Unicode 對象,視圖 函數之間的交互使用的是 Unicode 對象,模板的渲染也是用的 Unicode 對象。 一般,咱們沒必要擔憂編碼是否正確,後臺會處理的很好。
注意,咱們這裏只是對Unicode對象進行很是淺顯的概述,若要深刻了解你可能須要查閱相關的資料。 這是一個很好的起點:http://www.joelonsoftware.com/articles/Unicode.html。
爲了讓咱們的修改生效,先退出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__()
就是一個例子來演示模型知道怎麼顯示它們本身。
你已經知道怎麼作了: 先使用一些關鍵參數建立對象實例,以下:
>>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/')
這個對象實例並 沒有 對數據庫作修改。 在調用`` save()`` 方法以前,記錄並無保存至數據庫,像這樣:
>>> p.save()
在SQL裏,這大體能夠轉換成這樣:
INSERT INTO books_publisher (name, address, city, state_province, country, website) VALUES ('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA', 'U.S.A.', 'http://www.apress.com/');
由於 Publisher
模型有一個自動增長的主鍵 id
,因此第一次調用 save()
還多作了一件事: 計算這個主鍵的值並把它賦值給這個對象實例:
>>> p.id 52 # this will differ based on your own data
接下來再調用 save()
將不會建立新的記錄,而只是修改記錄內容(也就是 執行 UPDATE
SQL語句,而不是 INSERT
語句):
>>> p.name = 'Apress Publishing' >>> p.save()
前面執行的 save()
至關於下面的SQL語句:
UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52;
注意,並非只更新修改過的那個字段,全部的字段都會被更新。 這個操做有可能引發競態條件,這取決於你的應用程序。 請參閱後面的「更新多個對象」小節以瞭解如何實現這種輕量的修改(只修改對象的部分字段)。
UPDATE books_publisher SET name = 'Apress Publishing' WHERE id=52;
固然,建立新的數據庫,並更新之中的數據是必要的,可是,對於 Web 應用程序來講,更多的時候是在檢索查詢數據庫。 咱們已經知道如何從一個給定的模型中取出全部記錄:
>>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>]
這至關於這個SQL語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher;
注意
注意到Django在選擇全部數據時並無使用 SELECT*
,而是顯式列出了全部字段。 設計的時候就是這樣: SELECT*
會更慢,並且最重要的是列出全部字段遵循了Python 界的一個信條: 明言勝於暗示。
有關Python之禪(戒律) :-),在Python提示行輸入 import this
試試看。
讓咱們來仔細看看 Publisher.objects.all()
這行的每一個部分:
首先,咱們有一個已定義的模型
Publisher
。沒什麼好奇怪的: 你想要查找數據, 你就用模型來得到數據。而後,是
objects
屬性。 它被稱爲管理器,咱們將在第10章中詳細討論它。 目前,咱們只需瞭解管理器管理着全部針對數據包含、還有最重要的數據查詢的表格級操做。全部的模型都自動擁有一個
objects
管理器;你能夠在想要查找數據時使用它。最後,還有
all()
方法。這個方法返回返回數據庫中全部的記錄。 儘管這個對象 看起來 象一個列表(list),它實際是一個 QuerySet 對象, 這個對象是數據庫中一些記錄的集合。 附錄C將詳細描述QuerySet。 如今,咱們就先當它是一個仿真列表對象好了。
全部的數據庫查找都遵循一個通用模式:
咱們不多會一次性從數據庫中取出全部的數據;一般都只針對一部分數據進行操做。 在Django API中,咱們可使用`` filter()`` 方法對數據進行過濾:
>>> Publisher.objects.filter(name='Apress') [<Publisher: Apress>]
filter()
根據關鍵字參數來轉換成 WHERE
SQL語句。 前面這個例子 至關於這樣:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress';
你能夠傳遞多個參數到 filter()
來縮小選取範圍:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress>]
多個參數會被轉換成 AND
SQL從句, 所以上面的代碼能夠轉化成這樣:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A.' AND state_province = 'CA';
注意,SQL缺省的 =
操做符是精確匹配的, 其餘類型的查找也可使用:
>>> Publisher.objects.filter(name__contains="press") [<Publisher: Apress>]
在 name 和 contains
之間有雙下劃線。和Python同樣,Django也使用雙下劃線來代表會進行一些魔術般的操做。這裏,contains
部分會被Django翻譯成LIKE
語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%';
其餘的一些查找類型有:icontains
(大小寫無關的LIKE
),startswith
和endswith
, 還有range
(SQLBETWEEN
查詢)。 附錄C詳細描述了全部的查找類型。
上面的例子中`` filter()`` 函數返回一個記錄集,這個記錄集是一個列表。 相對列表來講,有些時候咱們更須要獲取單個的對象, `` get()`` 方法就是在此時使用的:
>>> Publisher.objects.get(name="Apress") <Publisher: Apress>
這樣,就返回了單個對象,而不是列表(更準確的說,QuerySet)。 因此,若是結果是多個對象,會致使拋出異常:
>>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {'country': 'U.S.A.'}
若是查詢沒有返回結果也會拋出異常:
>>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist.
這個 DoesNotExist
異常 是 Publisher 這個 model 類的一個屬性,即 Publisher.DoesNotExist
。在你的應用中,你能夠捕獲並處理這個異常,像這樣:
try: p = Publisher.objects.get(name='Apress') except Publisher.DoesNotExist: print "Apress isn't in the database yet." else: print "Apress is in the database."
在運行前面的例子中,你可能已經注意到返回的結果是無序的。 咱們尚未告訴數據庫 怎樣對結果進行排序,因此咱們返回的結果是無序的。
在你的 Django 應用中,你或許但願根據某字段的值對檢索結果排序,好比說,按字母順序。 那麼,使用 order_by()
這個方法就能夠搞定了。
>>> Publisher.objects.order_by("name") [<Publisher: Apress>, <Publisher: O'Reilly>]
跟之前的 all()
例子差很少,SQL語句裏多了指定排序的部分:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name;
咱們能夠對任意字段進行排序:
>>> Publisher.objects.order_by("address") [<Publisher: O'Reilly>, <Publisher: Apress>] >>> Publisher.objects.order_by("state_province") [<Publisher: Apress>, <Publisher: O'Reilly>]
若是須要以多個字段爲標準進行排序(第二個字段會在第一個字段的值相同的狀況下被使用到),使用多個參數就能夠了,以下:
>>> Publisher.objects.order_by("state_province", "address") [<Publisher: Apress>, <Publisher: O'Reilly>]
咱們還能夠指定逆向排序,在前面加一個減號 -
前綴:
>>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
儘管很靈活,可是每次都要用 order_by()
顯得有點囉嗦。 大多數時間你一般只會對某些 字段進行排序。 在這種狀況下,Django讓你能夠指定模型的缺省排序方式:
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
字段排序。
咱們已經知道如何對數據進行過濾和排序。 固然,一般咱們須要同時進行過濾和排序查詢的操做。 所以,你能夠簡單地寫成這種「鏈式」的形式:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
你應該沒猜錯,轉換成SQL查詢就是 WHERE
和 ORDER BY
的組合:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A' ORDER BY name DESC;
另外一個經常使用的需求就是取出固定數目的記錄。 想象一下你有成千上萬的出版商在你的數據庫裏, 可是你只想顯示第一個。 你可使用標準的Python列表裁剪語句:
>>> Publisher.objects.order_by('name')[0] <Publisher: Apress>
這至關於:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1;
相似的,你能夠用Python的range-slicing語法來取出數據的特定子集:
>>> Publisher.objects.order_by('name')[0:2]
這個例子返回兩個對象,等同於如下的SQL語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2;
注意,不支持Python的負索引(negative slicing):
>>> Publisher.objects.order_by('name')[-1] Traceback (most recent call last): ... AssertionError: Negative indexing is not supported.
雖然不支持負索引,可是咱們可使用其餘的方法。 好比,稍微修改 order_by() 語句來實現:
>>> Publisher.objects.order_by('-name')[0]
在「插入和更新數據」小節中,咱們有提到模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。
例如說咱們如今想要將Apress Publisher的名稱由原來的」Apress」更改成」Apress Publishing」。若使用save()方法,如:
>>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save()
這等同於以下SQL語句:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress'; UPDATE books_publisher SET name = 'Apress Publishing', address = '2855 Telegraph Ave.', city = 'Berkeley', state_province = 'CA', country = 'U.S.A.', website = 'http://www.apress.com' WHERE id = 52;
(注意在這裏咱們假設Apress的ID爲52)
在這個例子裏咱們能夠看到Django的save()方法更新了不只僅是name列的值,還有更新了 全部的列。 若name之外的列有可能會被其餘的進程所改動的狀況下,只更改name列顯然是更加明智的。 更改某一指定的列,咱們能夠調用結果集(QuerySet)對象的update()方法: 示例以下:
>>> Publisher.objects.filter(id=52).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。
刪除數據庫中的對象只需調用該對象的delete()方法便可:
>>> p = Publisher.objects.get(name="O'Reilly") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>]
一樣咱們能夠在結果集上調用delete()方法同時刪除多條記錄。這一點與咱們上一小節提到的update()方法類似:
>>> Publisher.objects.filter(country='USA').delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() []
刪除數據時要謹慎! 爲了預防誤刪除掉某一個表內的全部數據,Django要求在刪除表內全部數據時顯示使用all()。 好比,下面的操做將會出錯:
>>> Publisher.objects.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'delete'
而一旦使用all()方法,全部數據將會被刪除:
>>> Publisher.objects.all().delete()
若是隻須要刪除部分的數據,就不須要調用all()方法。再看一下以前的例子:
>>> Publisher.objects.filter(country='USA').delete()