書籍出處:https://www.packtpub.com/web-development/django-example
原做者:Antonio Melécss
2016年12月10日發佈(沒有進行校對,有不少錯別字以及模糊不清的語句,請你們見諒)html
2017年2月7日精校完成(斷斷續續的終於完成了第一章精校,感受比直接翻譯還要累,繼續加油)python
2017年2月10日再次進行精校(感謝大牛@kukoo的精校!)nginx
(譯者注:本人目前在杭州某家互聯網公司工做,崗位是測試研發,很是喜歡python,目前已經使用Django爲公司內部搭建了幾個自動化平臺,由於沒人教沒人帶,基本靠野路子自學,走過好多彎路,磕磕碰碰一路過來,前段時間偶爾看到《Django By Example》這本書,瞬間淚流滿面,當初怎麼沒有找到這麼好的Django教程。在看書的過程當中不知道怎麼搞的忽然產生了翻譯全書的想法,正好網上找了下也沒有漢化的版本,因此準備踏上這條不歸路。鑑於本人英文水平極低(四級都沒過),單純靠着有道詞典和本身對上下文的理解以及對書中每行代碼都保證敲一遍並運行的狀況下,請各位在讀到語句不通的時候或看不懂的地方請告訴我,我會及時進行改正。翻譯全書,主要也是爲了培養本身的英文閱讀水平(口語就算了),誰叫好多最新最有用的計算機文檔都是用英文寫的,另外也能夠培養本身的耐心,還能夠分享給其餘人,就這樣。)git
在這本書中,你將學習如何建立完整的Django項目,能夠在生產環境中使用。假如你尚未安裝Django,在本章的第一部分你將學習如何安裝。本章會覆蓋如何使用Django去建立一個簡單的blog應用。本章的目的是使你對該框架的工做有個基本概念,瞭解不一樣的組件之間是如何產生交互,而且教你一些技能經過使用一些基本功能方便地建立Djang項目。你會被引導建立一個完整的項目可是不會對全部的細節都進行詳細說明。不一樣的框架組件將在本書接下來的章節中進行介紹。
本章會覆蓋如下幾點:github
若是你已經安裝好了Django,你能夠直接略過這部分跳到建立你的第一個項目。Django是一個Python包所以能夠安裝在任何的Python的環境中。若是你尚未安裝Django,這裏有一個快速的指南幫助你安裝Django用來本地開發。web
Django須要在Python2.7或者3版本上才能更好的工做。在本書的例子中,咱們將使用Python 3。若是你使用Linux或者Max OSX,你可能已經有安裝好的Python。若是你不肯定你的計算機中是否安裝了Python,你能夠在終端中輸入 python 來肯定。若是你看到如下相似的提示,說明你的計算機中已經安裝好了Python:正則表達式
Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>>
若是你計算機中安裝的Python版本低於3,或者沒有安裝,下載並安裝Python 3.5.0 從http://www.python.org/download/ (譯者注:最新已是3.6.0了,Django2.0將再也不支持pytyon2.7,因此你們都從3版本以上開始學習吧)。sql
因爲你使用的是Python3,因此你不必再安裝一個數據庫。這個Python版本自帶SQLite數據庫。SQLLite是一個輕量級的數據庫,你能夠在Django中進行使用用來開發。若是你準備在生產環境中部署你的應用,你應該使用一個更高級的數據庫,好比PostgreSQL,MySQL或Oracle。你能獲取到更多的信息關於數據庫和Django的集成經過訪問 https://docs.djangoproject.com/en/1.8/topics/install/#database-installation 。shell
強烈建議你使用virtualenv來建立獨立的Python環境,這樣你可使用不一樣的包版本對應不一樣的項目,這比直接在真實系統中安裝Python包更加的實用。另外一個高級之處在於當你使用virtualenv你不須要任何管理員權限來安裝Python包。在終端中運行如下命令來安裝virtualenv:
pip install virtualenv
(譯者注:若是你本地有多個python版本,注意Python3的pip命令多是pip3)
當你安裝好virtualenv以後,經過如下命令來建立一個獨立的環境:
virtualenv my_env
以上命令會建立一個包含你的Python環境的my_env/目錄。當你的virtualenv被激活的時候全部已經安裝的Python庫都會帶入 my_env/lib/python3.5/site-packages 目錄中。
若是你的系統自帶Python2.X而後你又安裝了Python3.X,你必須告訴virtualenv使用後者Python3.X。經過如下命令你能夠定位Python3的安裝路徑而後使用該安裝路徑來建立virtualenv:
zenx\$ *which python3* /Library/Frameworks/Python.framework/Versions/3.5/bin/python3 zenx\$ *virtualenv my_env -p /Library/Frameworks/Python.framework/Versions/3.5/bin/python3*
經過如下命令來激活你的virtualenv:
source my_env/bin/activate
shell提示將會附上激活的virtualenv名,被包含在括號中,以下所示:
(my_evn)laptop:~ zenx$
你可使用deactivate命令隨時停用你的virtualenv。
你能夠獲取更多的信息關於virtualenv經過訪問 https://virtualenv.pypa.io/en/latest/ 。
在virtualenv之上,你可使用virtualenvwrapper工具。這個工具提供一些封裝用來方便的建立和管理你的虛擬環境。你能夠在 http://virtualenvwrapper.readthedocs.org/en/latest/ 下載該工具。
(譯者注:請注意如下的操做都在激活的虛擬環境中使用)
pip是安裝Django的第一選擇。Python3.5自帶預安裝的pip,你能夠找到pip的安裝指令經過訪問 https://pip.pypa.io/en/stable/installing/ 。運行如下命令經過pip安裝Django:
pip install Django==1.8.6
Django將會被安裝在你的虛擬環境的Python的site-packages/目錄下。
如今檢查Django是否成功安裝。在終端中運行python而且導入Django來檢查它的版本:
>>> import django >>> django.VERSION DjangoVERSION(1, 8, 5, 'final', 0)
若是你得到了以上輸出,Django已經成功安裝在你的機器中。
Django也可使用其餘方式來安裝。你能夠找到更多的信息經過訪問 https://docs.djangoproject.com/en/1.8/topics/install/ 。
咱們的第一個項目將會是一個完整的blog站點。Django提供了一個命令容許你方便的建立一個初始化的項目文件結構。在終端中運行如下命令:
django-admin startproject mysite
該命令將會建立一個名爲mysite的項目。
讓咱們來看下生成的項目結構:
mysite/ manage.py mysite/ __init__.py settings.py urls.py wsgi.py
讓咱們來了解一下這些文件:
默認生成的settings.py文件包含一個使用一個SQLite數據庫的基礎配置以及一個Django應用列表,這些應用會默認添加到你的項目中。咱們須要爲這些初始應用在數據庫中建立表。
打開終端執行如下命令:
cd mysite python manage.py migrate
你將會看到如下的相似輸出:
Rendering model states... DONE Applying contenttypes.ooo1_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length...OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying sessions.0001_initial... OK
這些初始應用表將會在數據庫中建立。過一下子你就會學習到一些關於migrate的管理命令。
Django自帶一個輕量級的web服務器來快速運行你的代碼,不須要花費額外的時間來配置一個生產服務器。當你運行Django的開發服務器,它會一直檢查你的代碼變化。當代碼有改變,它會自動重啓,將你從手動重啓中解放出來。可是,它可能沒法注意到一些操做,例如在項目中添加了一個新文件,因此你在某些場景下仍是須要手動重啓。
打開終端,在你的項目主目錄下運行如下代碼來開啓開發服務器:
python manage.py runserver
你會看到如下相似的輸出:
Performing system checks... System check identified no issues (0 silenced). November 5, 2015 - 19:10:54 Django version 1.8.6, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
如今,在瀏覽器中打開 http://127.0.0.1:8000/ ,你會看到一個告訴你項目成功運行的頁面,以下圖所示:
你能夠指定Django在定製的host和端口上運行開發服務,或者告訴它你想要運行你的項目經過讀取一個不一樣的配置文件。例如:你能夠運行如下 manage.py命令:
python manage.py runserver 127.0.0.1:8001 \ --settings=mysite.settings
這個命令早晚會對處理須要不一樣設置的多套環境啓到做用。記住,這個服務器只是單純用來開發,不適合在生產環境中使用。爲了在生產環境中部署Django,你須要使用真實的web服務讓它運行成一個WSGI應用例如Apache,Gunicorn或者uWSGI(譯者注:強烈推薦 nginx+uwsgi+Django)。你可以獲取到更多關於如何在不一樣的web服務中部署Django的信息,訪問 https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 。
本書外額外的須要下載的章節第十三章,Going Live包含爲你的Django項目設置一個生產環境。
讓咱們打開settings.py文件來看看你的項目的配置。在該文件中有許多設置是Django內置的,但這些只是全部Django可用配置的一部分。你能夠經過訪問 https://docs.djangoproject.com/en/1.8/ref/settings/ 看到全部的設置和它們默認的值。
如下列出的設置很是值得一看:
不要擔憂你目前還看不懂這些設置的含義。你將會在以後的章節中熟悉這些設置。
貫穿全書,你會反覆的讀到項目和應用的地位。在Django中,一個項目被認爲是一個安裝了一些設置的Django;一個應用是一個包含模型(models),視圖(views),模板(templates)以及URLs的組合。應用之間的交互經過Django框架提供的一些特定功能,而且應用可能被各類各樣的項目重複使用。你能夠認爲項目就是你的網站,這個網站包含多個應用,例如blog,wiki或者論壇,這些應用均可以被其餘的項目使用。(譯者注:我去,我居然漏翻了這一節- -|||,罪過罪過,阿米頭髮)
如今讓咱們建立你的第一個Django應用。咱們將要建立一個勉強湊合的blog應用。在你的項目主目錄下,運行如下命令:
python manage.py startapp blog
這個命令會建立blog應用的基本目錄結構,以下所示:
blog/ __init__.py admin.py migrations/ __init__.py models.py tests.py views.py
這些文件的含義:
咱們將要開始爲你的blog設計初始的數據模型(models)。一個模型(model)就是一個Python類,該類繼承了django.db.models.model,在其中的每個屬性表示一個數據庫字段。Django將會爲models.py中的每個定義的模型(model)建立一張表。當你建立好一個模型(model),Django會提供一個很是實用的API來方便的查詢數據庫。
首先,咱們定義一個POST模型(model)。在blog應用下的models.py文件中添加如下內容:
from django.db import models from django.utils import timezone from django.contrib.auth.models import User class Post(models.Model): STATUS_CHOICES = ( ('draft', 'Draft'), ('published', 'Published'), ) title = models.CharField(max_length=250) slug = models.SlugField(max_length=250, unique_for_date='publish') author = models.ForeignKey(User, related_name='blog_posts') body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft') class Meta: ordering = ('-publish',) def __str__(self): return self.title
這就是咱們給blog帖子使用的基礎模型(model)。讓咱們來看下剛纔在這個模型(model)中定義的各個字段含義:
(1,2)
,那麼該字段只能選擇1或者2,沒有其餘值能夠選擇)就像你所看到的的,Django內置了許多不一樣的字段類型給你使用,這樣你就可以定義你本身的模型(models)。經過訪問 https://docs.djangoproject.com/en/1.8/ref/models/fields/ 你能夠找到全部的字段類型。
在模型(model)中的類Meta包含元數據。咱們告訴Django查詢數據庫的時候默認返回的是根據publish字段進行降序排列過的結果。咱們使用負號來指定進行降序排列。
str()方法是當前對象默認的可讀表現。Django將會在不少地方用到它例如管理站點中。
若是你以前使用過Python2.X,請注意在Python3中全部的strings都使用unicode,所以咱們只使用str()方法。unicode()方法已經廢棄。(譯者注:Python3大法好,Python2別再學了,直接學Python3吧)
在咱們處理日期以前,咱們須要下載pytz模塊。這個模塊給Python提供時區的定義而且SQLite也須要它來對日期進行操做。在終端中輸入如下命令來安裝pytz:
pip install pytz
Django內置對時區日期處理的支持。你能夠在你的項目中的settings.py文件中經過USE_TZ來設置激活或停用對時區的支持。當你經過startproject命令來建立一個新項目的時候這個設置默認爲True。
爲了讓Django能保持跟蹤你的應用而且根據你的應用中的模型(models)來建立數據庫表,咱們必須激活你的應用。所以,編輯settings.py文件,在INSTALLED_APPS設置中添加blog。看上去以下所示:
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', )
(譯者注:該設置中應用的排列順序也會對項目的某些方面產生影響,具體狀況後幾章會有介紹,這裏提醒下)
如今Django已經知道在項目中的咱們的應用是激活狀態而且將會對其中的模型(models)進行自審。
讓咱們爲咱們的模型(model)在數據庫中建立一張數據表格。Django自帶一個數據庫遷移(migration)系統來跟蹤你對模型(models)的修改,而後會同步到數據庫。migrate命令會應用到全部在INSTALLED_APPS中的應用,它會根據當前的模型(models)和數據庫遷移(migrations)來同步數據庫。
首先,咱們須要爲咱們剛纔建立的新模型(model)建立一個數據庫遷移(migration)。在你的項目主目錄下,執行如下命令:
python manage.py makemigrations blog
你會看到如下輸出:
Migrations for 'blog': 0001_initial.py; - Create model Post
Django在blog應用下的migrations目錄中建立了一個0001——initial.py文件。你能夠打開這個文件來看下一個數據庫遷移的內容。
讓咱們來看下Django根據咱們的模型(model)將會爲在數據庫中建立的表而執行的SQL代碼。sqlmigrate命令帶上數據庫遷移(migration)的名字將會返回它們的SQL,但不會當即去執行。運行如下命令來看下輸出:
python manage.py sqlmigrate blog 0001
輸出相似以下:
BEGIN; CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id")); CREATE INDEX "blog_post_2dbcba41" ON "blog_post" ("slug"); CREATE INDEX "blog_post_4f331e2f" ON "blog_post" ("author_id"); COMMIT;
Django會根據你正在使用的數據庫進行以上精準的輸出。以上SQL語句是爲SQLite數據庫準備的。如你所見,Django生成的表名前綴爲應用名以後跟上模型(model)的小寫(blog_post),可是你也能夠經過在模型(models)的Meta類中使用db_table屬性來指定表名。Django會自動爲每一個模型(model)建立一個主鍵,可是你也能夠經過在模型(model)中的某個字段上設置primarry_key=True來指定主鍵。
讓咱們根據新模型(model)來同步數據庫。運行如下的命令來應用已存在的數據遷移(migrations):
python manage.py migrate
你應該會看到如下行跟在輸出的末尾:
Applying blog.0001_initial... OK
咱們剛剛爲INSTALLED_APPS中全部的應用進行了數據庫遷移(migrations),包括咱們的blog應用。在進行了數據庫遷移(migrations)以後,數據庫會反映咱們模型的當前狀態。
若是爲了添加,刪除,或是改變了存在的模型(models)中字段,或者你添加了新的模型(models)而編輯了models.py文件,你都須要經過使用makemigrations命令作一次新的數據庫遷移(migration)。數據庫遷移(migration)容許Django來保持對模型(model)改變的跟蹤。以後你必須經過migrate命令來保持數據庫與咱們的模型(models)同步。
如今咱們已經定義好了Post模型(model),咱們將要建立一個簡單的管理站點來管理blog帖子。Django內置了一個管理接口,該接口對編輯內容很是的有用。這個Django管理站點會根據你的模型(model)元數據進行動態構建而且提供一個可讀的接口來編輯內容。你能夠對這個站點進行自由的定製,配置你的模型(models)在其中如何進行顯示。
請記住,django.contrib.admin已經被包含在咱們項目的INSTALLED_APPS設置中,咱們不須要再額外添加。
首先,咱們須要建立一名用戶來管理這個管理站點。運行如下的命令:
python manage.py createsuperuser
你會看下如下輸出。輸入你想要的用戶名,郵箱和密碼:
Username (leave blank to use 'admin'): admin Email address: admin@admin.com Password: ******** Password (again): ******** Superuser created successfully.
如今,經過python manage.py runserver
命令來啓動開發服務器,以後在瀏覽器中打開 http://127.0.0.1:8000/admin/ 。你會看到管理站點的登陸頁面,以下所示:
使用你在上一步中建立的超級用戶信息進行登陸。你將會看到管理站點的首頁,以下所示:
Group和User 模型(models) 位於django.contrib.auth,是Django權限管理框架的一部分。若是你點擊Users,你將會看到你以前建立的用戶信息。你的blog應用的Post模型(model)和User(model)關聯在了一塊兒。記住,它們是經過author字段進行關聯的。
讓咱們在管理站點中添加你的blog模型(models)。編輯blog應用下的admin.py文件,以下所示:
from django.contrib import admin from .models import Post admin.site.register(Post)
如今,在瀏覽器中刷新管理站點。你會看到你的Post模型(model)已經在頁面中展現,以下所示:
這很簡單,對吧?當你在Django的管理頁面註冊了一個模型(model),Django會經過對你的模型(models)進行內省而後提供給你一個很是友好有用的接口,這個接口容許你很是方便的排列,編輯,建立,以及刪除對象。
點擊Posts右側的Add連接來添加一篇新帖子。你將會看到Django根據你的模型(model)動態生成了一個表單,以下所示:
Django給不一樣類型的字段使用了不一樣的表單控件。即便是複雜的字段例如DateTimeField也被展現成一個簡單的接口相似一個JavaScript日期選擇器。
填寫好表單而後點擊Save按鈕。你會被重定向到帖子列頁面而且獲得一條帖子成功建立的提示,以下所示:
如今咱們來看下如何定製管理站點。編輯blog應用下的admin.py文件,使之以下所示:
from django.contrib import admin from .models import Post class PostAdmin(admin.ModelAdmin): list_display = ('title', 'slug', 'author', 'publish', 'status') admin.site.register(Post, PostAdmin)
咱們使用繼承了ModelAdmin的定製類來告訴Django管理站點中須要註冊咱們本身的模型(model)。在這個類中,咱們能夠包含一些關於如何在管理站點中展現模型(model)的信息以及如何與該模型(model)進行交互。list_display屬性容許你在設置一些你想要在管理對象列表頁面顯示的模型(model)字段。
讓咱們經過更多的選項來定製管理模型(model),如使用如下代碼:
class PostAdmin(admin.ModelAdmin): list_display = ('title', 'slug', 'author', 'publish', 'status') list_filter = ('status', 'created', 'publish', 'author') search_fields = ('title', 'body') prepopulated_fields = {'slug': ('title',)} raw_id_fields = ('author',) date_hierarchy = 'publish' ordering = ['status', 'publish']
回到瀏覽器刷新管理站點頁面,如今應該以下所示:
你能夠看到帖子列頁面中展現的字段都是你在list-dispaly屬性中指定的。這個列頁面如今包含了一個右側邊欄容許你根據list_filter屬性中指定的字段來過濾返回結果。一個搜索框也應用在頁面中。這是由於咱們還經過使用search_fields屬性定義了一個搜索字段列。在搜索框的下方,有個能夠經過時間層快速導航的欄,該欄經過定義date_hierarchy屬性出現。你還能看到這些帖子默認的經過Status和Publish列進行排序。這是由於你經過使用ordering屬性指定了默認排序。
如今,點擊Add post連接。你還會在這兒看到一些改變。當你輸入完成新帖子的標題,slug字段將會自動填充。咱們經過使用prepoupulated_fields屬性告訴Django經過輸入的標題來填充slug字段。同時,如今的author字段展現顯示爲了一個搜索控件,這樣當你的用戶量達到成千上萬級別的時候比再使用下拉框進行選擇更加的人性化,以下圖所示:
經過短短的幾行代碼,咱們就在管理站點中自定義了咱們的模型(model)的展現形式。還有更多的方式能夠用來定製Django的管理站點。在這本書的後面,咱們還會進一步講述。
如今,你已經有了一個完整功能的管理站點來管理你的blog內容,是時候學習如何從數據庫中檢索信息而且與這些信息進行交互了。Django自帶了一個強大的數據庫抽象API可讓你輕鬆的建立,檢索,更新以及刪除對象。Django的Object-relational Mapper(ORM)能夠兼容MySQL,PostgreSQL,SQLite以及Oracle。請記住你能夠在你項目下的setting.py中編輯DATABASES設置來指定數據庫。Django能夠同時與多個數據庫進行工做,這樣你能夠編寫數據庫路由經過任何你喜歡的方式來操做數據。
一旦你建立好了你的數據模型(models),Django會提供你一個API來與它們進行交互。你能夠找到數據模型(model)的官方參考文檔經過訪問 https://docs.djangoproject.com/en/1.8/ref/models/ 。
打開終端運行如下命令來打開Python shell:
python manage.py shell
而後依次輸入如下內容:
>>> from django.contrib.auth.models import User >>> from blog.models import Post >>> user = User.objects.get(username='admin') >>> post = Post.objects.create(title='One more post', slug='one-more-post', body='Post body.', author=user) >>> post.save()
讓咱們來研究下這些代碼作了什麼。首先,咱們取回了一個username是admin的用戶對象:
user = User.objects.get(username='admin')
get()
方法容許你從數據庫取回一個單獨的對象。注意這個方法只但願在查詢中有惟一的一個匹配。若是在數據庫中沒有返回結果,這個方法會拋出一個DoesNotExist異常,若是數據庫返回多個匹配結果,將會拋出一個MultipleObjectsReturned異常。當查詢執行的時候,全部的異常都是模型(model)類的屬性。
接着,咱們來建立一個擁有定製標題標題,slug和內容的Post實例,而後咱們設置以前取回的user胃這篇帖子的做者以下所示:
post = Post(title='Another post', slug='another-post', body='Postbody.', author=user)
這個對象只是存在內存中不會執行到數據庫中
最後,咱們經過使用save()方法來保存該對象到數據庫中:
post.save()
這步操做將會執行一段SQL的插入語句。咱們已經知道如何在內存中建立一個對象而且以後纔在數據庫中進行插入,可是咱們也能夠經過使用create()方法直接在數據庫中建立對象,以下所示:
Post.objects.create(title='One more post', slug='one-more-post',body='Post body.', author=user)
如今,改變這篇帖子的標題而且再次保存對象:
>>> post.title = 'New title' >>> post.save()
這一次,save()方法執行了一條更新語句。
你對對象的改變一直存在內存中直到你執行到save()方法。
Django的Object-relational mapping(ORM)是基於查詢集(QuerySet)。查詢集(QuerySet)是從你的數據庫中根據一些過濾條件範圍取回的結果對象進行的採集。你已經知道如何經過get()方法從數據庫中取回單獨的對象。如你所見:咱們經過Post.objects.get()
來使用這個方法。每個Django模型(model)至少有一個管理器(manager),默認管理器(manager)叫作objects。你經過使用你的模型(models)的管理器(manager)就能得到一個查詢集(QuerySet)對象。獲取一張表中的全部對象,你只須要在默認的objects管理器(manager)上使用all()方法便可,以下所示:
>>> all_posts = Post.objects.all()
這就是咱們如何建立一個用於返回數據庫中全部對象的查詢集(QuerySet)。注意這個查詢集(QuerySet)並尚未執行。Django的查詢集(QuerySets)是惰性(lazy)的,它們只會被動的去執行。這樣的行爲能夠保證查詢集(QuerySet)很是有效率。若是咱們沒有把查詢集(QuerySet)設置給一個變量,而是直接在Python shell中編寫,由於咱們迫使它輸出結果,這樣查詢集(QuerySet)的SQL語句將立馬執行:
>>> Post.objects.all()
爲了過濾查詢集(QuerySet),你能夠在管理器(manager)上使用filter()
方法。例如,咱們能夠返回全部在2015年發佈的帖子,以下所示:
Post.objects.filter(publish__year=2015)
你也可使用多個字段來進行過濾。例如,咱們能夠返回2015年發佈的全部做者用戶名爲admin的帖子,以下所示:
Post.objects.filter(publish__year=2015, author__username='admin')
上面的寫法和下面的寫法產生的結果是一致的:
Post.objects.filter(publish__year=2015).filter(author__username='admin')
咱們構建了字段的查找方法,經過使用兩個下劃線
(publish__year)
來查詢,除此之外咱們也能夠經過使用兩個下劃線(author__username)
訪問關聯的模型(model)字段。
你能夠在管理器(manager)上使用exclude()方法來排除某些返回結果。例如:咱們能夠返回全部2015年發佈的帖子可是這些帖子的題目開頭不能是Why:
Post.objects.filter(publish__year=2015).exclude(title__startswith='Why')
經過在管理器(manager)上使用order_by()方法來對不一樣的字段進行排序,你能夠對結果進行排序。例如:你能夠取回全部對象並經過它們的標題進行排序:
Post.objects.order_by('title')
默認是升序。你能夠經過負號來指定使用降序,以下所示:
Post.objects.order_by('-title')
若是你想刪除一個對象,你能夠對對象實例進行下面的操做:
post = Post.objects.get(id=1) post.delete()
請注意,刪除對象也將刪除任何的依賴關係
只要你喜歡,你能夠鏈接許多的過濾給查詢集(QuerySet)並且不會立馬在數據庫中執行直到這個查詢集(QuerySet)被執行。查詢集(QuerySet)只有在如下狀況中才會執行:
* 在你第一次迭代它們的時候
* 當你對它們的實例進行切片:例如Post.objects.all()[:3]
* 當你對它們進行了打包或緩存
* 當你對它們調用了repr()
或len()
方法
* 當你明確的對它們調用了list()
方法
* 當你在一個聲明中測試它,例如bool(), or, and, or if
咱們以前提到過, objects是每個模型(models)的默認管理器(manager),它會返回數據庫中全部的對象。可是咱們也能夠爲咱們的模型(models)定義一些定製的管理器(manager)。咱們準備建立一個定製的管理器(manager)來返回全部狀態爲已發佈的帖子。
有兩種方式能夠爲你的模型(models)添加管理器(managers):你能夠添加額外的管理器(manager)方法或者繼承管理器(manager)的查詢集(QuerySets)進行修改。第一種方法相似Post.objects.my_manager()
,第二種方法相似Post.my_manager.all()
。咱們的管理器(manager)將會容許咱們返回全部帖子經過使用Post.published
。
編輯你的blog應用下的models.py文件添加以下代碼來建立一個管理器(manager):
class PublishedManager(models.Manager): def get_queryset(self): return super(PublishedManager, self).get_queryset().filter(status='published') class Post(models.Model): # ... objects = models.Manager() # The default manager. published = PublishedManager() # Our custom manager.
get_queryset()
是返回執行過的查詢集(QuerySet)的方法。咱們經過使用它來包含咱們定製的過濾到完整的查詢集(QuerySet)中。咱們定義咱們定製的管理器(manager)而後添加它到Post 模型(model)中。咱們如今能夠來執行它。例如,咱們能夠返回全部標題開頭爲Who的而且是已經發布的帖子:
Post.published.filter(title__startswith='Who')
如今你已經學會了一些如何使用ORM的基本知識,你已經準備好爲blog應用建立視圖(views)了。一個Django視圖(view)就是一個Python方法,它能夠接收一個web請求而後返回一個web響應。在視圖(views)中經過全部的邏輯處理返回指望的響應。
首先咱們會建立咱們的應用視圖(views),而後咱們將會爲每一個視圖(view)定義一個URL模式,咱們將會建立HTML模板(templates)來渲染這些視圖(views)生成的數據。每個視圖(view)都會渲染模板(template)傳遞變量給它而後會返回一個通過渲染輸出的HTTP響應。
讓咱們開始建立一個視圖(view)來展現帖子列。編輯你的blog應用下中views.py文件,以下所示:
from django.shortcuts import render, get_object_or_404 from .models import Post def post_list(request): posts = Post.published.all() return render(request, 'blog/post/list.html', {'posts': posts})
你剛建立了你的第一個Django視圖(view)。post_list視圖(view)將request對象做爲惟一的參數。記住全部的的視圖(views)都有須要這個參數。在這個視圖(view)中,咱們獲取到了全部狀態爲已發佈的帖子經過使用咱們以前建立的published管理器(manager)。
最後,咱們使用Django提供的快捷方法render()經過給予的模板(template)來渲染帖子列。這個函數將request對象做爲參數,模板(template)路徑以及變量來渲染的給予的模板(template)。它返回一個渲染文本(通常是HTML代碼)HttpResponse對象。render()方法考慮到了請求內容,這樣任何模板(template)內容處理器設置的變量均可以帶入給予的模板(template)中。你會在第三章,擴展你的blog應用學習到如何使用它們。
讓咱們建立第二個視圖(view)來展現一篇單獨的帖子。添加以下代碼到views.py文件中:
def post_detail(request, year, month, day, post): post = get_object_or_404(Post, slug=post, status='published', publish__year=year, publish__month=month, publish__day=day) return render(request, 'blog/post/detail.html', {'post': post})
這是一個帖子詳情視圖(view)。這個視圖(view)使用year,month,day以及post做爲參數經過給予slug和日期來獲取到一篇已經發布的帖子。請注意,當咱們建立Post模型(model)的時候,咱們給slgu字段添加了unique_for_date參數。這樣咱們能夠確保在給予的日期中只有一個帖子會帶有一個slug,所以,咱們能經過日期和slug取回單獨的帖子。在這個詳情視圖(view)中,咱們經過使用get_object_or_404()快捷方法來檢索指望的Post。這個函數能取回匹配給予的參數的對象,或者當沒有匹配的對象時返回一個HTTP 404(Not found)異常。最後,咱們使用render()快捷方法來使用一個模板(template)去渲染取回的帖子。
一個URL模式是由一個Python正則表達,一個視圖(view),一個全項目範圍內的命名組成。Django在運行中會遍歷全部URL模式直到第一個匹配的請求URL才中止。以後,Django導入匹配的URL模式中的視圖(view)並執行它,使用關鍵字或指定參數來執行一個HttpRequest類的實例。
若是你以前沒有接觸過正則表達式,你須要去稍微瞭解下,經過訪問 https://docs.python.org/3/howto/regex.html 。
在blog應用目錄下建立一個urls.py文件,輸入如下代碼:
from django.conf.urls import url from . import views urlpatterns = [ # post views url(r'^$', views.post_list, name='post_list'), url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/'\ r'(?P<post>[-\w]+)/$', views.post_detail, name='post_detail'), ]
第一條URL模式沒有帶入任何參數,它映射到post_list視圖(view)。第二條URL模式帶上了如下4個參數映射到post_detail視圖(view)中。讓咱們看下這個URL模式中的正則表達式:
爲每個應用建立單獨的urls.py文件是最好的方法,能夠保證你的應用能給別的項目再度使用。
如今你須要將你blog中的URL模式包含到項目的主URL模式中。編輯你的項目中的mysite文件夾中的urls.py文件,以下所示:
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')), ]
經過這樣的方式,你告訴Django在blog/路徑下包含了blog應用中的urls.py定義的URL模式。你能夠給它們一個命名空間叫作blog,這樣你能夠方便的引用這個URLs組。
你可使用以前定義的post_detail URL給Post對象構建標準URL。Django的慣例是給模型(model)添加get_absolute_url()方法用來返回一個對象的標準URL。在這個方法中,咱們使用reverse()方法容許你經過它們的名字和可選的參數來構建URLS。編輯你的models.py文件添加以下代碼:
from django.core.urlresolvers import reverse Class Post(models.Model): # ... def get_absolute_url(self): return reverse('blog:post_detail', args=[self.publish.year, self.publish.strftime('%m'), self.publish.strftime('%d'), self.slug])
請注意,咱們經過使用strftime()方法來保證個位數的月份和日期須要帶上0來構建URL(譯者注:也就是01,02,03)。咱們將會在咱們的模板(templates)中使用get_absolute_url()方法。
咱們爲咱們的應用建立了視圖(views)和URL模式。如今該添加模板(templates)來展現界面友好的帖子了。
在你的blog應用目錄下建立如下目錄結構和文件:
templates/ blog/ base.html post/ list.html detail.html
以上就是咱們的模板(templates)的文件目錄結構。base.html文件將會包含站點主要的HTML結構以及分割內容區域和一個導航欄。list.html和detail.html文件會繼承base.html文件來渲染各自的blog帖子列和詳情視圖(view)。
Django有一個強大的模板(templates)語言容許你指定數據的如何進行展現。它基於模板標籤(templates tags), 例如 {% tag %}
, {{ variable }}
以及模板過濾器(templates filters),能夠對變量進行過濾,例如 {{ variable|filter }}
。你能夠經過訪問 https://docs.djangoproject.com/en/1.8/ ref/templates/builtins/ 找到全部的內置模板標籤(templates tags)和過濾器(filters)。
讓咱們來編輯base.html文件並添加以下代碼:
{% load staticfiles %} <!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> <link href="{% static "css/blog.css" %}" rel="stylesheet"> </head> <body> <div id="content"> {% block content %} {% endblock %} </div> <div id="sidebar"> <h2>My blog</h2> <p>This is my blog.</p> </div> </body> </html>
{% load staticfiles %}
告訴Django去加載django.contrib.staticfiles應用提供的staticfiles 模板標籤(temaplate tags)。經過加載它,你能夠在這個模板(template)中使用{% static %}
模板過濾器(template filter)。經過使用這個模板過濾器(template filter),你能夠包含一些靜態文件好比說blog.css文件,你能夠在本書的範例代碼例子中找到該文件,在blog應用的static/目錄中(譯者注:給你們個地址去拷貝 https://github.com/levelksk/django-by-example-book )拷貝這個目錄到你的項目下的相同路徑來使用這些靜態文件。
你能夠看到有兩個{% block %}
標籤(tags)。這些是用來告訴Django咱們想在這個區域中定義一個區塊(block)。繼承這個模板(template)的其餘模板(templates)可使用自定義的內容來填充區塊(block)。咱們定義了一個區塊(block)叫作title,另外一個區塊(block)叫作content。
讓咱們編輯post/list.html文件使它以下所示:
{% extends "blog/base.html" %} {% block title %}My Blog{% endblock %} {% block content %} <h1>My Blog</h1> {% for post in posts %} <h2> <a href="{{ post.get_absolute_url }}"> {{ post.title }} </a> </h2> <p class="date"> Published {{ post.publish }} by {{ post.author }} </p> {{ post.body|truncatewords:30|linebreaks }} {% endfor %} {% endblock %}
經過{% extends %}
模板標籤(template tag),咱們告訴Django須要繼承blog/base.html 模板(template)。而後咱們在title和content區塊(blocks)中填充內容。咱們經過循環迭代帖子來展現它們的標題,日期,做者和內容,在標題中還集成了帖子的標準URL連接。在帖子的內容中,咱們應用了兩個模板過濾器(template filters): truncatewords用來縮短內容限制在必定的字數內,linebreaks用來轉換內容中的換行符爲HTML的換行符。只要你喜歡你能夠鏈接許多模板標籤(tempalte filters),每個都會應用到上個輸出生成的結果上。
打開終端執行命令python manage.py runserver
來啓動開發服務器。在瀏覽器中打開 http://127.0.0.1:8000/blog/ 你會看到運行結果。注意,你須要添加一些發佈狀態的帖子才能在這兒看到它們。你會看到以下圖所示:
這以後,讓咱們來編輯post/detail.html文件使它以下所示:
{% extends "blog/base.html" %} {% block title %}{{ post.title }}{% endblock %} {% block content %} <h1>{{ post.title }}</h1> <p class="date"> Published {{ post.publish }} by {{ post.author }} </p> {{ post.body|linebreaks }} {% endblock %}
如今,你能夠在瀏覽器中點擊其中一篇帖子的標題來看帖子的詳細視圖(view)。你會看到相似如下頁面:
當你開始給你的blog添加內容,你很快會意識到你須要將帖子分頁顯示。Django有一個內置的Paginator類容許你方便的管理分頁。
編輯blog應用下的views.py文件導入Django的頁碼類修改post_list以下所示:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger def post_list(request): object_list = Post.published.all() paginator = Paginator(object_list, 3) # 3 posts in each page page = request.GET.get('page') try: posts = paginator.page(page) except PageNotAnInteger: # If page is not an integer deliver the first page posts = paginator.page(1) except EmptyPage: # If page is out of range deliver last page of results posts = paginator.page(paginator.num_pages) return render(request, 'blog/post/list.html', {'page': page, 'posts': posts})
Paginator是如何工做的:
如今,咱們必須建立一個模板(template)來展現分頁處理,它能夠被任意的模板(template)包含來使用分頁。在blog應用的templates文件夾下建立一個新文件命名爲pagination.html。在該文件中添加以下HTML代碼:
<div class="pagination"> <span class="step-links"> {% if page.has_previous %} <a href="?page={{ page.previous_page_number }}">Previous</a> {% endif %} <span class="current"> Page {{ page.number }} of {{ page.paginator.num_pages }}. </span> {% if page.has_next %} <a href="?page={{ page.next_page_number }}">Next</a> {% endif %} </span> </div>
爲了渲染上一頁與下一頁的連接而且展現當前頁面和全部頁面的結果,這個分頁模板(template)指望一個Page對象。讓咱們回到blog/post/list.html模板(tempalte)中將pagination.html模板(template)包含在{% content %}
區塊(block)中,以下所示:
{% block content %} ... {% include "pagination.html" with page=posts %} {% endblock %}
咱們傳遞給模板(template)的Page對象叫作posts,咱們將分頁模板(tempalte)包含在帖子列模板(template)中指定參數來對它進行正確的渲染。這種方法你能夠反覆使用,用你的分頁模板(template)對不一樣的模型(models)視圖(views)進行分頁處理。
如今,在你的瀏覽器中打開 http://127.0.0.1:8000/blog/。 你會看到帖子列的底部已經有分頁處理:
由於一個視圖(view)的調用就是獲得一個web請求而且返回一個web響應,你能夠將你的視圖(views)定義成類方法。Django爲此定義了基礎的視圖(view)類。它們都從View類繼承而來,View類能夠操控HTTP方法調度以及其餘的功能。這是一個可替代的方法來建立你的視圖(views)。
咱們準備經過使用Django提供的通用ListView使咱們的post_list視圖(view)轉變爲一個基於類的視圖。這個基礎視圖(view)容許你對任意的對象進行排列。
編輯你的blog應用下的views.py文件,以下所示:
from django.views.generic import ListView class PostListView(ListView): queryset = Post.published.all() context_object_name = 'posts' paginate_by = 3 template_name = 'blog/post/list.html'
這個基於類的的視圖(view)相似與以前的post_list視圖(view)。在這兒,咱們告訴ListView作了如下操做:
model = Post
而後Django將會構建Post.objects.all() 查詢集(QuerySet)給咱們。blog/post_list.html
。如今,打開你的blog應用下的urls.py文件,註釋到以前的post_listURL模式,在以後添加一個新的URL模式來使用PostlistView類,以下所示:
urlpatterns = [ # post views # url(r'^$', views.post_list, name='post_list'), url(r'^$', views.PostListView.as_view(),name='post_list'), url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/'\ r'(?P<post>[-\w]+)/$', views.post_detail, name='post_detail'), ]
爲了保持分頁處理能工做,咱們必須將正確的頁面對象傳遞給模板(tempalte)。Django的ListView經過叫作page_obj的變量來傳遞被選擇的頁面,因此你必須編輯你的post_list_html模板(template)去包含使用了正確的變量的分頁處理,以下所示:
{% include "pagination.html" with page=page_obj %}
在你的瀏覽器中打開 http://127.0.0.1:8000/blog/ 而後檢查每同樣功能是否都和以前的post_list視圖(view)同樣工做。這是一個簡單的,經過使用Django提供的通用類的基於類視圖(view)的例子。你將在第十章,建立一個在線學習平臺以及相關的章節中學到更多的基於類的視圖(views)。
在本章中,你經過建立一個基礎的blog應用學習了Django web框架的基礎。你爲你的項目設計了數據模型(models)而且進行了數據庫遷移。你爲你的blog建立了視圖(views),模板(templates)以及URLs,還包括對象分頁。
在下一章中,你會學習到如何加強你的blog應用,例如評論系統,標籤(tag)功能,而且容許你的用戶經過郵件來分享帖子。
終於將第一章勉強翻譯完成了,不少翻譯的句子我本身都讀不懂 - -|||
你們看到有錯誤有歧義的地方請幫忙指出,以後還會隨時進行修改保證基本能讀懂。
按照第一章的翻譯速度,全書都翻譯下來估計要2,3個月,這是很是很是樂觀的估計,天天只有中午休息和下班後大概有兩三小時的翻譯時間。
2016年12月10日發佈(沒有進行校對,有不少錯別字以及模糊不清的語句,請你們見諒)
2017年2月7日精校完成(斷斷續續的終於完成了第一章精校,感受比直接翻譯還要累,繼續加油)
2017年2月10日再次進行精校(感謝大牛@kukoo的精校!)