評論(0

《我為WIKI狂》第11期

標籤: 暫無標籤

我也要加入《我為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 referenceDatabase API reference的文檔。

4   修改 settings.py, 安裝 app

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

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

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

  1. 執行(在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.batrun 兩個文件。分別用於 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')

    是表示取出pagenameFrontPage 的頁面。你可能要說,為什麼沒有異常保護,是的,這也就是為什麼我們要在前面先要插條記錄在裡面的原因。這樣就不會出錯了。再加上我要做的 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、寫出自我介紹,推銷你自己或者你喜歡的東西。我們的目的是讓每個人都成為作者和明星,並與他人形成交友圈子。在此基礎上大家共創最大的百科全書,共享屬於全人類的知識!

上一篇[先驗論]    下一篇 [岳畫殺]

相關評論

同義詞:暫無同義詞