《我為WIKI狂》第11期

pagen pagen pagen

我也要加入《我為WIKI狂》,萬人共寫一本書!

[本期維客]看美女

django開發wiki
文/ 看美女

  • 做一個簡單的wiki,要可以修改當前頁面,即在頁面下面提供一個編輯的按鈕。然後還要識別頁面中的兩個開頭大寫的單詞為頁面切換點,可以進入一個已經生成好的頁面,或提示創建一個新頁面。

下面我們將開始創建 Django 中的 app 了。

先說一下。如果你看過官方版的教程,它就是講述了一個 Poll 的 app 的生成過程。那么一個 app 就是一個功能的集合,它有自已的 model ,view 和相應的模板,還可以帶自已的 urls.py 。那么它也是一個獨立的目錄,這樣一個 app 就可以獨立地進行安裝,你可以把它安裝到其它的 Django 伺服器中去。因此採用 app 的組織形式非常有意義。而且 adango-admin.py 也提供了一個針對 app 的命令,一會我們就會看到。而且 Django 提供一些自動功能也完全是針對於 app 這種結構的。Model, Template, View 就合成了 MTV 這幾個字母。 Model 是用來針對資料庫,同時它可以用來自動生成管理界面, View 在前面我們一直都用它,用來處理請求和回響的相當於MVC框架中的 Controller 的作用, Template 用來生成界面。

2創建 wiki app

manage.py startapp wiki

Note

在 0.92 版之前, app 都是放在 apps 目錄下的。不過到了 0.92 版,apps 目錄不自動創建了。因此你就可以直接放在項目目錄下了。

這樣在 wiki 子目錄下有以下檔案:

__init__.py
表示 wiki 目錄是一個包。
views.py
用來放它的 view 的代碼。
models.py
用來放 model 代碼。

3編輯 wiki/models.py

from django.db import models# Create your models here.class Wiki(models.Model): pagename = models.CharField(maxlength=20, unique=True) content = models.TextField()

每個 model 其實在 Django 中就是一個表,你將用它來保存數據。在實際的套用中,一般都要與資料庫打交道,如果你不想用資料庫,那么原因可能就是運算元據庫麻煩,創建資料庫環境也麻煩。但通過 Django 的 model 處理,它是一種 ORM (Object Relation Mapping, 對象與關係的映射),可以禁止掉底層資料庫的細節,同時提供以對象的形式來處理數據。非常方便。而且 Django 的 model 層支持多種資料庫,如果你改變資料庫也不是什麼問題,這也為以後的資料庫遷移帶來好處。總之,好處多多,大家多多體會吧。

Wiki 是 model 的名字,它需要從 models.Model 派生而來。它定義了兩個欄位,一個是欄位是 pagename , 用來保存 wiki 頁面的名字,它有兩個參數,一個是最大長度(不過從這點上不如 SQLAlchemy 方便, SQLAlchemy並不需要長度,它會根據有無長度自動轉為 TEXT 類型),目前 CharField 需要這個參數;另一個是 unique 表示這個欄位不能有重複值。還有一個欄位是 content ,用來保存 wiki 頁面的內容,它是一個 TextField 類型,它不需要最大長度。

Note

models.Model 在 0.92 版以前是 meta.Model 。而 django.db 在 0.92 版之前是 django.core 。

現在不太了解 model 沒有關係,關鍵是看整個生成過程。

一旦你定義好了 model ,在運行時, Django 會自動地為這個 model 增加許多數據操作的方法。關於 model 和 資料庫操作API的詳細內容參見 Model reference 和 Database API reference 的文檔。

4修改 settings.py, 安裝 app

雖然我們的其它工作沒有做完,但我還是想先安裝一下 app 吧。每個一 app 都需要安裝一下。安裝一般有兩步:

  • 修改settings.py
  • INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'newtest.wiki',)

    這個在檔案的最後,前4個是預設定義的。給出指定 wiki 包的引用名來。這一步是為了以後方便地導入所必須的。因為我們的目錄都是包的形式,因此這裡就是與目錄相對應的。

  • 執行(在newtest目錄下)
  • manage.py install wiki

    如果沒有報錯就是成功了。這一步 Django 將根據 model 的信息在資料庫中創建相應的表。表就是這樣創建出來的。

    5在命令行下加入首頁(FrontPage)

    我們假設首頁的名字為 FrontPage ,並且我們將在命令行下增加它,讓我們熟悉一下命令行的使用

    進入 newtest 目錄,然後:

    set PYTHONPATH=d:\testset DJANGO_SETTINGS_MODULE=newtest.settings

    Note

    在 Linux 環境下要相應的使用 export 和與你相配置的目錄。

    注意 PYTHONPATH 需要設為 newtest 的父目錄,我的 newtest 的全路徑為: d:\test\newtest 。你的可能與我不同,請注意修改。不這樣做不行, manage.py 只是在運行它的時候才起作用,但在命令行下使用 Python 則需要手工設定。你可以把這種設定寫成一個批處理,以後就方便了。

    Note

    如果使用 NewEdit 來創建 Django 工程,則會自動生成 run.bat 和 run 兩個檔案。分別用於 Windows 和 Linux 平台下設定環境變數。

    Note

    還可以使用:

    manage.py shell

    來進入命令行,並且不需要設定上面的環境變數。感謝xlp223提醒。

    進入 python

    >>> from newtest.wiki.models import Wiki>>> page = Wiki(pagename="FrontPage", content="Welcome to Easy Wiki")>>> page.save()>>> Wiki.objects.all()[<Wiki object>]>>> p = Wiki.objects.all()[0]>>> p.pagename'FrontPage'>>> p.content'Welcome to Easy Wiki'

    Note

    因為在寫這篇教程時是在magic-removal分枝下進行的操作,因此有些 API 並不穩定。象 objects 的方法以前是沿用model的方法,但後來進行了簡化,比如 get_list() 變為 all() 。還有一系統的變化。具體的可以參見 Removing The Magic 文檔中關於 Descriptor fields 的說明。

    在 Django 中,對於資料庫的記錄有兩種操縱方式,一種是集合方式,一種是對象方式。集合方式相當於表級操作,在新版的 0.92 中可以使用 model.objects 來處理。 objects 對象有一些集合方式的操作,如 all() 會返回全部記錄, filter() 會根據條件返回部分記錄。而象插入新記錄則需要使用記錄方式來操作,些時要直接使用 model 類。

    Note

    在 0.92 版之前要比這麻煩得多,也不容易理解。好在情況已經發生了變化。

    6修改 wiki/views.py

    #coding=utf-8from newtest.wiki.models import Wikifrom django.template import loader, Contextfrom django.http import HttpResponse, HttpResponseRedirectfrom django.shortcuts import render_to_responsedef index(request, pagename=""): """顯示正常頁面,對頁面的文字做 #查找是否已經存在頁面# pages = Wiki.objects.get_list(pagename__exact=pagename) pages = Wiki.objects.filter(pagename=pagename) if pages: #存在則調用頁面模板進行顯示 return process('wiki/page', pages[0]) else: #不存在則進入編輯畫面 return render_to_response('wiki/edit', {'pagename':pagename}) else:# page = Wiki.objects.get_object(pagename__exact="FrontPage") page = Wiki.objects.get(pagename="FrontPage") return process('wiki/page', page)def edit(request, pagename): """顯示編輯存在頁面"""# page = Wiki.objects.get_object(pagename__exact=pagename) page = Wiki.objects.get(pagename=pagename) return render_to_response('wiki/edit', {'pagename':pagename, 'content':page.content})def save(request, pagename): """保存頁面內容,老頁面進行內容替換,新頁面生成新記錄""" content = request.POST['content']# pages = Wiki.objects.get_list(pagename__exact=pagename) pages = Wiki.objects.filter(pagename=pagename) if pages: pages[0].content = content pages[0].save() else: page = Wiki(pagename=pagename, content=content) page.save() return HttpResponseRedirect("/wiki/%s" % pagename)import rer = re.compile(r'\b([A-Z][a-z]+[A-Z][a-z]+)\b')def process(template, page): """處理頁面連結,並且將回車符轉為<br>""" t = loader.get_template(template) content = r.sub(r'<a href="/wiki/\1">\1</a>', page.content) content = re.sub(r'[\n\r]+', '<br>', content) c = Context({'pagename':page.pagename, 'content':content}) return HttpResponse(t.render(c))

    Note

    將原來老的 model 方法加了注釋,目前改用最新的 API 了。

    代碼有些長,有些地方已經有說明和注釋了。簡單說一下:

    • index() 用來顯示一個 wiki 頁面。它需要一個參數就是頁面的名稱。如果在資料庫中找得到,則調用 process() 方法(process() 方法是一個自定義方法,主要用來對頁面的文本進行處理,比如查找是否有滿足 wiki 命名規則的單詞,如果有則替換成連結。再有就是將回車轉為 <br> )。如果沒有找到,則直接調用編輯模板顯示一個編程頁面。當然,這個頁面的內容是空的。只是它的頁面名字就是 pagename 。如果 pagename 為空,則進入 FrontPage 頁面。 Wiki.objects 對象有 filter() 方法和 get() 方法,一個返回一個結果集,一個返回指定的對象。這裡為什麼使用 filter() 呢,因為一旦指定檔案不存在,它並不是返回一個 None 對象,而是拋出異常,而我沒有使用異常的處理方式。通過 filter() 如果存在則結果中應有一個元素,如果不存在則應該是一個 [] 。這樣就知道是否有返回了。

    Note

    filter() 中使用的參數與一般的 db-api 是一樣的,但如果是比較相等,可以為: pagename__exact=pagename 也可以簡化為 pagename=pagename 。

    Note

    在 Django 中,一些欄位的比較操作比較特殊,它是在欄位名後加 __ 然後是比較條件。這樣看上去就是一個字元串。具體的參見 The Database API 。

    Note

    回車轉換的工作其實可以在模板中使用 filter 來完成。

    • 從對模板的使用 (wiki/edit) 可以猜到在後面我們要在 templates 中創建子目錄了。的確,對於不同的 app ,我們可以考慮將所有的模板都放在統一的 templates 目錄下,但為了區分方便,一般都會針對 app 創建不同的子目錄。當然也可以不這樣,可以放在其它的地方,只要修改 settings.py ,將新的模板目錄加進去就行了。

    因為我們在設計 model 時已經設定了 pagename 必須是唯一的,因此一旦 filter() 有返回值,那它只能有一個元素,而 pages[0] 就是我們想要的對象。

    • page = wikis.get(pagename="FrontPage")

      是表示取出 pagename 為 FrontPage 的頁面。你可能要說,為什麼沒有異常保護,是的,這也就是為什麼我們要在前面先要插條記錄在裡面的原因。這樣就不會出錯了。再加上我要做的 wiki 不提供刪除功能,因此不用擔心會出現異常。

    • edit() 用來顯示一個編輯頁面,它直接取出一個頁面對象,然後調用 wiki/edit 模板進行顯示。也許你還是要問,為什麼不考慮異常,因為這裡不會出現。為什麼?因為 edit() 只用在已經存在的頁面上,它將用於存在頁面的修改。而對於不存在的頁面是在 index() 中直接調用模板來處理,並沒有直接使用這個 edit() 來處理。也許你認為這樣可能不好,但由於在 edit() 要重新檢索資料庫,而在 index() 已經檢索過一次了,沒有必要再次檢索,因此象我這樣處理??人意見。

    • save() 用來在編輯頁面時用來保存內容的。它先檢查頁面是否在資料庫中存在,如果不存在則創建一個新的對象,並且保存。注意,在 Django 中,對對象處理之後只有調用它的 save() 方法才可以真正保存到資料庫中去。如果頁面已經存在,則更新頁面的內容。處理之後再重定向到 index() 去顯示這個頁面。

    7在 templates 中創建 wiki 子目錄

    8編輯 templates/wiki/page.html

    <h2>{{ pagename }}</h2><p>{{ content }}</p><hr/><p><form method="POST" action="/wiki/{{ pagename }}/edit/"><input type="submit" value="編輯"></form></p>

    它用來顯示頁面,同時提供一個“編輯”按鈕。當點擊這個按鈕時將調用 view 中的 edit() 方法。

    9編輯 templates/wiki/edit.html

    <h2>編輯:{{ pagename }}</h2><form method="POST" action="/wiki/{{pagename}}/save/"><textarea name="content" rows="10" cols="50">{{ content }}</textarea><br/><input type="submit" value="保存"></form>

    它用來顯示一個編輯頁面,同時提供“保存”按鈕。點擊了保存按鈕之後,會調用 view 中的 save() 方法。

    10修改 urls.py

    from django.conf.urls.defaults import *urlpatterns = patterns('', # Example: # (r'^testit/', include('newtest.apps.foo.urls.foo')), (r'^$', 'newtest.helloworld.index'), (r'^add/$', 'newtest.add.index'), (r'^list/$', 'newtest.list.index'), (r'^csv/(?P<filename>\w+)/$', 'newtest.csv_test.output'), (r'^login/$', 'newtest.login.login'), (r'^logout/$', 'newtest.login.logout'), (r'^wiki/$', 'newtest.wiki.views.index'), (r'^wiki/(?P<pagename>\w+)/$', 'newtest.wiki.views.index'), (r'^wiki/(?P<pagename>\w+)/edit/$', 'newtest.wiki.views.edit'), (r'^wiki/(?P<pagename>\w+)/save/$', 'newtest.wiki.views.save'), # Uncomment this for admin:#(r'^admin/', include('django.contrib.admin.urls')),)

    增加了 wiki 等4個 url 映射。

    這裡要好好講一講 URL 的設計(個人所見)。

    一般一個 wiki ,我們訪問它的一個頁面可能為:wiki/pagename。因此我設計對 index() 方法的調用的 url 為:

    r'^wiki/(?P<pagename>\w+)/$'

    也就是把 wiki/後面的解析出來作為 pagename 參數。但這樣就帶來一個問題,如果我想實現 wiki/edit 表示修改, pagename 作為一個參數通過 POST 來提交好象就不行了。因為上面的解析規則會“吃”掉這種情況。因此我採用 Zope 的表示方法:把對象的方法放在對象的後面。我可以把 pagename 看成為一個對象, edit , save 是它的方法,放在它的後面,也簡單,也清晰。當然如果我們加強上面的正則表達式,也可以解析出 wiki/edit 的情況,但那就是你設計的問題了。這裡就是我的設計。

    因此 wiki/pagename 就是顯示一個頁面,wiki/pagename/edit 就是編輯這個頁面, wiki/pagename/save 就是保存頁面。而 pagename 解析出來後就是分別與 index() , edit() , save() 的 pagename 參數相對應。

    下面你可以運行了。

    11啟動 server

    進入 http://localhost:8000/wiki

    首先進入這個頁面:

    然後你點編輯,則進入FrontPage的編輯界面:

    然後我們加上一個 TestPage ,它符合 wiki 的名字要求,兩個首字母大寫的單詞連在一起。然後點擊保存。

    看見了吧。頁面上的 TestPage 有了連結。點擊它將進入:

    這是 TestPage 的編輯頁面。讓我們輸入中文,然後輸入 FrontPage 。然後保存。好了,剩下的你來玩吧。點擊 FrontPage 將回到首頁。

    --敬請期待下期《我為WIKI狂》

    加入《我為WIKI狂》賺取我的稿費!

    查看《我為WIKI狂》所有精彩內容!

    加入《我為Wiki狂》的步驟如下:

    1、先加入這個小組,成為我們的一員;

    2、寫出你與WIKI的故事,範圍可以很廣,只要牽扯到WIKI都行;或者寫出你對WIKI的認識、使用心得,甚至批評。具體先看這兒,例子請參考這兒

    3、創建幾個屬於你的條目,這個很自由,比如條目可以是百科知識,可以是你的某篇blog,可以是你的原創文章,也可以是日記。如不了解創建規則,請看新手幫助;

    4、寫出自我介紹,推銷你自己或者你喜歡的東西。我們的目的是讓每個人都成為作者和明星,並與他人形成交友圈子。在此基礎上大家共創最大的百科全書,共享屬於全人類的知識!

    相關詞條

    相關搜尋

    熱門詞條

    聯絡我們