本文轉至:http://www.lxweimin.com/p/05810d38f93a
創建第一個django應用
運行以下命令:
python manage.py startapp blog
這個命令會創建應用的基本目錄結構,類似下方結構:
blog/
__init__.py
admin.py
migrations/
__init__.py
models.py
tests.py
views.py
設計博客的數據模型
我們將要為你的博客設計初始的數據模型。每一個model都是一個Python類,繼承了django.db.models.model,其中的每一個屬性對應了一個數據庫字段。Djnago將會為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
這是一個給博客帖子使用的基礎model。讓我們來看下剛才在model中定義的各個字段含義:
- title: 這個字段對應帖子的標題。這是一個CharField,在SQL數據庫中對應VARCHAR類型
- slug:這是一個用在URLs中的字段。一個slug是一個短標簽之包含字母,數字,下劃線或連接線。我們將通過使用slug字段來構建一個漂亮的,友好的URLs給我們的博客帖子。我們可以給每個帖子使用日期和slug來構建URLs通過添加 unique_for_date參數給這個字段。
- author:這是一個 ForeignKey。這個字段定義了一個many-to-one(多對一)的關系。我們告訴Django每一個帖子是被一個用戶編輯而一個用戶又能編輯多個帖子。通過這個字段,Django將會在數據庫中通過有關聯的model主鍵來創建一個外鍵。在這個例子中,我們關聯上了django權限系統的User model。我們指定了User和Post之間關聯的名字,通過related_name屬性。在之后我們將會學習到更多相關的內容。
- body:這是帖子的內容。這個字段是TextField,在SQL數據中對應TEXT類型。
- publish:這個字段存儲了帖子發布的時間。我們使用Djnago的 timezone now方法來設定默認值。
- created:這個字段存儲了帖子創建的時間。因為我們在這兒使用了auto_now_add,當一個對象被創建的時候這個字段會自動保存當前日期。
- updated:這個字段存儲了帖子更新的時間。因為我們在這兒使用了auto_now,當我們保存一個對象的時候當前字段將會自動更新到當前日期。
- status:這個字段是用來展示帖子的狀態。我們使用了一個choices參數,所有這個字段的值只能在給予的選擇內容中選擇。
就像你所看到的的,Django帶來了不同的字段類型所以你能夠定義你的models。你可以找到所有的字段類型通過訪問 Django Model field reference
在model中的Meta類包含元數據。我們告訴Django查詢數據庫的時候默認返回的是根據publish字段進行降序排列過的結果。我們使用負號來指定降序排列。
str()方法是對象默認的可讀表現。Django將會在很多地方用到它例如管理平臺。
如果你之前使用過Python2.X,請注意在Python2中所有的strings都使用unicode,因為我們的環境是python3,所以使用
str()
方法。unicode()
方法已經廢棄。
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自帶一個遷移系統來跟蹤每一次models的變化并且會同步到數據庫。migrate
命令會應用到所有在INSTALLED_APPS
中的應用,它會根據models來同步數據庫。
python manage.py makemigrations blog
你會看到以下輸出:
Migrations for 'blog':
0001_initial.py;
- Create model Post
Django已經在blog應用下的migrations目錄中創建了一個0001——initial.py文件。你可以打開這個文件來看下。
讓我們來看下Django根據我們的model將會為在數據庫中創建的表格而執行的SQL代碼。sqlmigrate
命令會返回一串SQL而不會去執行。執行以下命令來看下輸出:
python manage.py sqlmigrate blog 0001
輸出類似如下:
BEGIN;
--
-- Create model Post
--
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 N
ULL, "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;
這些輸出是根據你正在使用的數據庫。這些SQL語句是為SQLite準備的。就像你所看見的,Django生成的表名前綴為應用名之后跟上modle的小寫(blog_post),但是你也可以自定義表名在models的Meta
類中通過db_table
屬性。Django會自動為每個model創建一個主鍵,但是你可以自己指定主鍵通過設置primarry_key=True
在期中一個model的字段你上。
讓我們根據新的model來同步數據庫。運行以下的命令來應用存在的數據遷移:
python manage.py migrate
我們剛剛為INSTALLED_APPS中所有的應用進行了數據遷移,包括我們的blog應用。在進行了數據遷移之后,數據庫將會和我們的models對應。
如果你編輯了models.py文件無論你是添加,刪除,還是改變了之前存在的字段,或者你添加了一個新的models,你將必須做一次新的遷移通過使用makemigrations
命令。數據遷移允許Django來保持對model改變的跟蹤。然后你必須通過migrate
命令來保持數據庫與我們的models同步。
為你的models創建一個管理站點
如今,我們已經定義好了Post model,我們將要創建一個簡單的管理站點來管理博客的帖子。Django內置了一個非常有用的管理接口來編輯內容。這個Django管理站點會根據你的model元數據進行動態構建并且提供可讀的接口來編輯內容。你可以對這個站點進行自由的定制,配置你的models在其中的展示方式。
請記住,django.contrib.admin
已經被包含在我們項目的INSTALLED_APPS
設置中,我們不需要再額外添加。
創建一個超級用戶
首先,我們需要創建一個用戶來管理這個管理站點。運行以下的命令:
python manage.py createsuperuser
Django管理站點
現在,通過python manage.py runserver
命令來啟動開發服務,之后在瀏覽器中打開 http://127.0.0.1:8000/admin/ 。你會看到管理站點的登錄頁面;
使用你之前創建的超級用戶信息登錄。你將會看到管理站點的首頁
這很簡單,對吧?當你在Django的管理頁面注冊了一個model,你會獲得一個非常友好有用的接口通過簡單的方式允許你在列表中編輯,創建,刪除對象操作。點擊Posts
右側的Add
鏈接來添加一個新帖子。你將會看到Django為你的model動態生成了一個可編輯輸入頁面。
自定義models的展示形式
現在我們來看下如何自動管理站點。編輯博客應用下的admin.py文件,我們通過使用繼承ModelAdmin
的自定義類來告訴Django管理站點注冊了哪些我們自己的model。在這個類中,我們可以包含一些信息來定制管理頁面中如何展示和交互model。list_display
屬性允許你在管理對象的列表頁面展示你想展示的model中的字段。
from django.contrib import admin
from .models import Post
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']
admin.site.register(Post, PostAdmin)
重新刷新管理站點的頁面,現在應該如下所示:
你可以看到在帖子列表頁中展示的字段都是你在list-dispaly
屬性中指定的。列表頁面現在包含了一個右邊欄允許你根據list_filter
屬性中指定的字段來過濾返回結果。在頁面還出現了一個搜索框。這是因為我們還定義了一個搜索字段通過使用search_fields
屬性。在搜索框的下方,有個可以通過時間層快速操作的欄。這是通過定義date_hierarchy
屬性。你還能看到這些帖子可以通過Status
和Publish
列進行了默認排序。那是因為你指定了默認排序通過使用ordering
屬性。
現在,點擊Add post
鏈接。你還會在這兒看到更多的改變。當你輸入完成新帖子的標題,slug
字段將會自動填充。我們告訴Django通過輸入的標題來填充slug
字段是通過使用prepoupulated_fields
屬性。同時,如今的author
字段展示變為了一個搜索控件,這樣當你的用戶達到成千上萬的時候比使用下拉的選擇更加的人性化,如下圖所示:
通過短短的幾行代碼,我們就在管理站點中自定義了我們的model的展示形式。還有更多的方式可以用來自定義Django的管理站點。在這本書的后面,我們還會進一步講述。
使用QuerySet和managers
現在,你已經有個完整的管理站點來管理你的博客內容,是時候學習如何從數據庫中檢索信息和操作了。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.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
實例,該實例中author關聯上我們之前去除的user,如下所示:
post = Post(title='Another post', slug='another-post', body='Postbody.', author=user)
# 這個對象只是存在內存中,并沒有執行到數據庫中
最后,我們通過使用save()方法來保存該對象到數據庫中:
post.save()
這步操作將會在之后執行一段SQL的插入語句。我們已經知道如何在內存中創建一個對象并且之后才在數據庫中進行插入,但是我們也可以直接在數據庫中創建對象通過使用create()
方法,如下所示:
post = Post(title='Another post', slug='another-post', body='Postbody.', author=user)
更新對象
現在,改變這篇帖子的標題并且再次保存對象:
>>> post.title = 'New title'
>>> post.save()
這一次,save()方法執行了一條更新語句。
# 你對對象的改變一直存在內存中直到你執行到save()
方法。
取回對象
Django的Object-relational mapping(ORM)
是以QuerySet
為基礎。QuerySet是從你的數據庫中根據一些過濾條件范圍取回的結果進行采集產生的對象。你已經知道通過get()
方法可以從數據庫中取回單獨的對象。就像你所看到的:Post.objects.get()
。每一個Django model至少有一個manager,默認叫做objects
。你能獲得一個QuerySet對象就是通過你的models的manager。獲取table中所有的對象,你只需要在默認的objects manager上使用all()
方法即可:
>>> all_posts = Post.objects.all()
我們創建一個返回數據庫中所有對象的QuerySet。注意這個QuerySet并還沒有執行。Django的QuerySets是惰性的,他們只會被動的去執行。這樣可以保證QuerySet非常效率。如果沒有把QuerySet賦值給一個變量,直接在Python shell中編寫,這樣QuerySet的SQL語句將立馬執行因為我們迫使它在輸出中返回結果:
>>> Post.objects.all()
使用filter()方法
過濾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')
使用exclude()
你可以在manager上使用exclude()
方法來排除某些返回結果。例如:我們可以返回所有2015年發布的帖子但是這些帖子的題目開頭不能是Why
:
Post.objects.filter(publish__year=2015).exclude(title__startswith='Why')
使用order_by()
你可以對結果進行排序通過在manager上使用order_by()
方法來對不同的字段進行排序。例如:你可以取回所有對象通過它們的標題進行排序:
Post.objects.order_by('title')
默認是升序。你可以通過負號來指定使用降序:
Post.objects.order_by('-title')
刪除對象
如果你想刪除一個對象,你可以對對象實例進行下面的操作:
post = Post.objects.get(id=1)post.delete()
# 請注意,刪除對象也將刪除其中的依賴關系
QuerySet什么時候會執行
你可以連接許多過濾給QuerySet只要你喜歡而且不會在數據庫中執行直到這個QuerySet被執行。QuerySet只有在以下情況中才會執行:
- 在你第一次迭代它們的時候
- 當你對它們的實例進行切片:
Post.objects.all()[:3]
- 當你對它們進行了打包或緩存
- 當你對它們調用了
repr()
或len()
方法 - 當你明確的對它們調用了
list()
方法 - 當你再一些聲明中測試它們,例如
bool()
, or, and, or if
創建model manager
我們之前提到過, objects是每一個models的默認manager,會返回數據庫中所有的結果。但是我們也可以我們的models定義一些自定義的manager。我們準備創建一個自定義的manager來返回所有狀態為已發布的帖子。
有兩種方式來為你的models添加managers:你可以添加臨時的manager方法或者繼承manager的QuerySets進行修改。
第一種方法類似Post.objects.my_manager(),
第二種方法類似Post.my_manager.all()。我們的manager將會允許我們返回所有帖子通過使用Post.published。
編輯你的博客應用下的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')
構建列表和詳情views
現在你學會了一些ORM的基本使用方法,你已經準備好為博客應用創建views了。一個Django view 就是一個Python方法可以接收一個web請求然后返回一個web響應。在vies中通過邏輯處理返回期望的響應。
首先我們會創建一個應用view,然后我們將會為每個view定義一個URL,最后,我們將會為每個views生成的數據通過創建HTML templates來進行渲染。每個view將會通過一個變量來渲染template并且會返回一個經過渲染輸出的HTTP響應。
創建列表和詳情views
讓我們開始創建一個視圖來展示帖子的列表。編輯你的博客應用下中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
對象作為唯一的參數。記住所有的的view都有需要這個參數。在這個view中,我們獲取到了所有狀態為已發布的帖子通過使用我們之前創建的published
manager。
最后,我們使用Django提供的快捷方法render()
來渲染帖子列表通過給予的template。這個函數將request
對象,template路徑,和給予渲染的變量最為參數。它返回一個渲染文本(一般是HTML代碼)HttpResponse
對象。render()
方法將許多變量投入template環境中,以便template環境中進行調用。你會在第三章,擴展你的博客應用學習到。
讓我們創建第二個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()
快捷方法來檢索期望的帖子。這個函數能取回匹配給予的參數的對象,或者返回一個HTTP 404異常當沒有匹配的對象找到。最后,我們使用render()
快捷方法來使用template去渲染取回的帖子。
為你的views添加URL patterns
一個URL pattern 是由一個Python正則表達,一個view,一個全項目范圍內的命名組成。Django在運行中會遍歷所有URL pattern直到第一個匹配的請求URL才停止。之后,Django導入匹配的URL pattern中的view并且執行它,使用關鍵字或指定參數來執行一個HttpRequest
類的實例。如果你之前沒有接觸過正則表達式,你需要去稍微了解下,通過訪問 https://docs.python.org/3/howto/regex.html 。
在博客應用目錄下創建一個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 pattern并沒有帶入任何參數,它映射post_list
view。第二條URL pattern帶上了一下4個參數映射到post_detail
view。讓我們看下這個URL pattern中的正則表達式:
* year:需要四位數
* month:需要兩位數。不及兩位數,開頭帶上0,比如 01,02
* day:需要兩位數。不及兩位數開頭帶上0
* post:可以由單詞和連字符組成
為每一個應用創建單獨的urls.py文件是最好的方法來保證你的應用可以為別的項目再度使用。
現在你需要將你博客中的URL patterns包含到項目的主URL patterns中。編輯你的項目中的mysite文件夾中urls.py文件,如下所示:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')),
]
通過這樣的方式,你告訴Django在blog/
路徑下包含了博客應用中的urls.py定義的URL patterns。你可以給他們一個命名空間叫做blog
這樣你可以方便的引用這個URLs組。
models的標準URLs
你可以使用之前定義的post_detil
URL對Post對象構建標準URL。Django的慣例是添加get_absolute_url()
方法給model用來返回一個對象的標準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.我們將會在我們的templates中使用get_absolute_url()
方法。
為你的views創建templates
我們為我們的應用創建了views和URL patterns。現在該添加模板來展示人性化的帖子了。在你的博客應用目錄下創建一下目錄結構和文件:
templates/
blog/
base.html
post/
list.html
detail.html
以上就是我們的templates的文件目錄結構。base.html文件將會包含站點主要的HTML結構,分割內容區域和導航欄。list.html和detail.html文件會繼承base.html文件來渲染各自的博客帖子列表和詳情view。
Django有一個強大的templates語言允許你指定數據的展示形式。它立足于templates tags, 例如{% tag %},{{ variable }}以及templates filters,可以對變量進行過濾,例如{{ variable|gilter }}。你可以找到所有的內置templates tags和filters通過訪問 https://docs.djangoproject.com/en/1.8/ ref/templates/builtins/ 。
讓我們來編輯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文件,你可以在本書的范例代碼例子中知道,在博客應用的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。你會看到類似的以下輸出:
添加頁碼
當你開始給你的博客添加內容,你很快會意識到你需要將帖子分頁顯示。Django有一個內置的pagination類允許你方便的管理分頁。
編輯博客應用下的views.py文件導入Django的paginator類修改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})
pagination是如何工作的:
- 我們通過希望在每頁中顯示的對象的數量實例化了
Paginator
類
- 我們獲取到
page
GET參數來獲取頁碼 - 我們通過調用Paginator的
page()
方法在期望的頁面中獲得了對象 - 如果
page
參數不是一個整形數字,我們就返回第一頁的結果。如果這個參數的數字超出了最后的頁數,我們就展示最后一頁的結果 - 我們傳遞頁數并且取到對象給template。
現在,我們必須創建一個template來展示分頁處理,它可以被任意的template包含來使用頁碼。在博客應用的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.htm
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/ 你會看到帖子列表的底部有分頁信息:
使用基于類的views
當一個view被調用通過web請求并且返回一個web響應,你還可以將你的views定義成類方法。Django為此定義了基礎的view類。它們都從View類繼承而來,View類可以操控HTTP方法分派和其他的功能。這是一個可代替的方法來創建你的views。
我們準備使我們的post_list
view轉變為一個基于類的視圖通過使用Django提供的通ListView
。這個基礎view允許你對任意的對象進行排列。
編輯你的博客應用下的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做了以下操作:
使用一個特定的QuerySet代替取回所有的對象。代替定義一個
queryset
屬性,我們可以指model = Post,并且Django將會構建一個通過的Post.objects.all() QuerySet給我們。
使用環境變量posts給查詢結果。如果我們不指定任意的context_object_name默認的變量將會是object_list。
對結果進行分頁處理每頁只顯示3個對象。
使用自定義的template來渲染頁面。如果我們不設置默認的template,ListView將會使用blog/post_list.html。
現在,打開你的博客應用下的urls.py文件,在post_list
URL pattern之前添加一個新的URL pattern使用PostlistView
類,如下所示:
from django.conf.urls import url
from . import views
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})/(?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一樣工作。這是一個簡單的基于類的view例子通過使用Django提供的通用類。