這幾天花時間學習了一下python django,正如某人所說,掌握另一門語言是有必要的,一樣我也做出了本身的選擇。從這幾天的學習中,我確實也發現其餘語言及其框架等確實有一種不同凡響的感受。下面我把本身這幾天看到的東西稍微做了一下總結,本文並非django的教程,而是在麥子學院參加學習以後對django的一些本身的理解,可能有些不成熟的地方,但願你們不要吝惜手中的磚頭。
一 django的orm
若是有人問我喜歡django的什麼,我會耗不猶豫的告訴你是django的orm,這個想法的產生徹底來自於我長時間來積累的對hibernate的「不滿」,雖然從理智的角度來看,hibernate作的是很是的正確的,由於它並非只針對互聯網而產生的,它的主要市場應該仍是在企業應用上,不過把它用在互聯網並不是不能夠,只不過你們更多的時候會選擇ibatis之類,由於不知道hibernate的人老是會說hibernate沒有ibatis快(其實我最煩這個,片面的比較是沒有意義的)。正是hibernate的目標是打形成java界一個全方位,全能的orm框架,因此的它學習曲線和使用的複雜度日益的提高,要徹底掌握好hibernate不是一件容易的事情(不要告訴我你會點crud,知道點lazy load你就掌握好hibernate了),再回頭來看django的orm,若是說要把hibernate說清楚須要800頁的書,那麼要把django的orm說清楚,200頁就夠了(事實上它的官方文檔只有十幾頁的樣子)。下面我舉一個我正在作的例子,這裏有一個自關聯的對象(事實上django的orm是基於model,這點和ror不太同樣,有人跟我講過ror是數據庫驅動),這個對象有一個父對象,一般咱們的菜單會定義成這樣的對象,這樣的菜單能夠無限級向下擴展:
java
1. class Category(models.Model): nginx
2. id = models.AutoField('id', primary_key=True) c++
3. name = models.CharField(maxlength=50) 程序員
4. code = models.CharField(maxlength=50) web
5. parentCategory = models.ForeignKey('self', 'id', null=True) 正則表達式
6. enable = models.BooleanField() sql
7. 數據庫
8. def __str__(self): apache
9. return self.name
10.
11. class Admin:
12. list_display = ('id', 'name', 'code', 'parentCategory')
Category中又定義的Admin是爲django的Admin模塊服務的。
瞧,咱們定義的域模型只須要這些代碼就夠了,models.Model是父對象,全部的model對象都須要繼承這個對象,這個對象提供了不少經常使用的數據庫方法,不過不是基於sql的,仍是基於對象的,如同Criteria同樣。下面列出經常使用的一些查詢Category的方法。
1,
引用
Category.objects.get(id = request.POST['category'])
查詢category by id(從頁面上傳過來的)
2
1. categoryList = Category.objects.filter(code__contains = 「a」,enable = True).order_by(「-id」)[0:5]
,按id倒序,查詢enable屬性爲True的,且code中包含a的category,且取前5個,是否是有很強烈的criteria的味道
3
1. category.save()
保存或者更新某個category對象(相似saveOrUpdate操做),充血模型,dao消失了。
4
1. category.delete()
刪除某個category對象,固然delete方法也是支持批量刪除,好比
1. categorys.delete()
5 複雜的查詢可使用extra方法,例如:
1. Category.objects.extra(where=['id IN (3, 4, 5, 20)'])
,還可使用字符替換法,如:
1. Category.objects.extra(where=['code=%s'], params=['a'])
固然django的orm提供了不少很經常使用的功能,這裏不一一舉例了,注意,這裏我說的是提供了不少很經常使用的功能,至於hibenate中比較複雜的映射策略,在django中我並無看到。可是我反而高興我沒有在django中找到這個功能,由於django自己的定位是快速的互連網開發,它不須要太多的關注這個領域不多出現的東西,這樣帶來的優勢是學習曲線的下降和開發效率的提升。
二 django的模板
Django的模板能夠說是很是的簡潔,簡潔到我不知道說什麼好,簡潔到看一下文檔就能上手使用,在java中,freemarker和velocity我都用過,最複雜功能最強大的仍是freemarker,支持jsp tag的嵌入讓咱們能夠重用不少已經存在的組件,這一點我在以前的文章中也有過比較詳細的描述(強強聯手,看freemarker和displaytag的結合),因爲了解,纔有發言權,django的模板能夠說是爲互連網應用而誕生的,簡潔及快速開發的特色讓人不由自主的喜歡。大多數模板語言的基本語法都是相似的,好比在freemarker中顯示值是${},而在django是{{}},freemarker中if判斷爲<#if></#if>,而django中是
1. {% if msg %}
2. Xx
3. {% else %}
4. Xx
5. {% endif%}
再看看在django中渲染模板的方法,有兩種:
第一種
1. def preparePublish(request):
2. t = loader.get_template(publishInfo)
3. return HttpResponse(t.render(Context({'categoryList' : None})))
第二種
1. c = Context({"categoryList":categoryList})
2. return render_to_response(indexPage, c)
render_to_response至關於封裝了loader.get_template方法而已,全部的一切看上去都是那麼的簡單,模板無處不在,今天你模板了嗎?
插一句題外話,關於jsp的題外話,無論是ruby,仍是c++,仍是python,在它們的web框架中都使用了模板,java中也有不少模板,咱們最熟悉的是freemarker和velocity。這從一個側面反映出咱們web開發中的一個模式,那就是咱們的view基本上是基於模板產生的,而jsp這個東西應該來講是時代的產物,在那個混亂的落後的時代產生的,不過很奇怪的是如今還有這麼多人抱着它不放。
三 django的form
Django有兩種form,一種是本身定義form class,還有一種是經過咱們定義的model自動form class。
因爲ahuaxuan只作 了一個信息發佈的小例子,因此並不能全面的瞭解或者理解django中form的全部細節,不過從我涉及到的部分來說,我對django的從模型建立表單的作法確實感到有比較大的侷限性,由於不少時候,model中的數據 並非從頁面上來的,在這種狀況下,form對象被構造出來以後,ahuaxuan尚未找到修改form中值的方法。
而自定義form類也比較麻煩,就是要寫本身的model,這個和咱們以前的作法比較不同,這裏的form表明咱們java中的value object,model是domain object,在咱們的ssh框架中咱們一般把value object繼承咱們的domain object。雖然一堆又一堆的人提出了反對意見,說要把這兩個對象分開,由於他們處在不一樣的層次中,可是從實踐經驗中,咱們能夠看到,這樣作沒有什麼很差。而在django中自定義form和model分開的行爲可能比較符合一些人的心理。
不過自定義forms也有比較讓人稱道的地方,在form中咱們能夠自定義驗證規則,同時咱們能夠根據form對象直接生成頁面中的內容,不過這一點其實也有比較麻煩的地方,就是若是要改變樣式的時候就比較麻煩。不過總的來講django的form仍是比較有特色的,並且必定程度上給咱們帶來了方便。
四 django的url轉發
Django的url轉發是基於正則表達式的,有的人叫好,有的人叫差,我就是叫差的那一撥人之一。url轉發應該是一個很是清楚,很是明亮的事情,但是用上這個正則表達式匹配的東西以後,我鬱悶了,因此我只能回到遙遠的過去去繞過這個東東,我不用總能夠了吧。
從目前目前掌握的知識來看,django的views裏的東西實際上是controller,爲何叫views?不得而知,不過一直這麼沿用下來了,即便是在天然界,不少表面上去不太同樣得東西,其實內部的原理是同樣的,我就以爲django的views就是struts1.x中的action,爲何這樣說呢,讓咱們來看看兩段比較的代碼,第一段是django的,第二段是struts1.x的:
1. def index(request):
2.
3. categoryList = Category.objects.filter(enable = True)
4. for cate in categoryList:
5. informationList = Information.objects.filter(category = cate)[0:5]
6. cate.informationList = informationList
7.
8. c = Context({"categoryList":categoryList})
9. return render_to_response(indexPage, c)
――――――――――分隔線―――――――――――――――
1. public ActionForward getSechandIndex(ActionMapping mapping, ActionForm form,
2. HttpServletRequest request,
3. HttpServletResponse response)
4. throws Exception {
5. setBargainIndex(request);
6. return mapping.findForward("bargainHome");
7. }
從形式上來看,二者出奇的類似,好比說傳入的參數等。咱們知道python是面向對象的語言,可是事實上它也支持函數編程,若是def定義在class內部,那麼就是對象的方法,不然,就能夠認爲是函數編程了,看看,咱們的views裏的東西都是函數,views實際上是一個模塊,這個模塊咱們能夠認爲是struts1.x中的action,而views中的函數能夠認爲是action中的方法。它們是遠房親戚。
那麼說到這裏,曲線救國的線也找到了,就是struts.1x中DispatchAction,咱們只要在url後面追加一個methodName就能夠指定咱們要調用views中的哪一個函數了。代碼以下:
1. def execute(request):
2. methodName = request.GET['methodName']
3. return getattr(mark.views, methodName)(request)
這個execute方法成爲了全部的方法的入口(咱們在urls.py中只須要這樣定義:
1. (r'^$', 'inforplatform.bargin.views.execute'))
接着在execute方法中判斷methodName的值,而後根據這個值找到對應的函數,再調用它,getattr相似於java中的反射,可讓咱們動態調用任何咱們想調用的函數(只要咱們知道函數名的話)
這樣咱們在urls.py中只須要定義不多的值(有幾個模塊就定義幾行就夠了)就能夠完成咱們的項目了,之後維護起來也沒有這麼麻煩和複雜。
一個小小的缺憾是沒有自帶restful,不過據說有一個插件能夠支持。
六 admin
Django的admin功能號稱是django的殺手級特性(killer feature),這一說能夠說是恰如其分,絕不誇張的,從我作的這個例子來看,當我作網站的時候,基本上只須要關注前臺頁面的展現這部分,後臺的功能基本上都自動有了,好比我作的例子是一個二手信息發佈平臺,category是二手信息的類型,還有一個information類,和category是多對一的關係,那麼在後臺,category和information的crud就自動生產了,因爲category自己是一個自關聯,因此在admin中 add category的時候,admin會根據我model的定義,自動要求選擇一個parentCategory,而在add information的頁面上,admin會要求我選擇一個category來完成對一個information的建立,而之前在java中,這些工做都須要本身完成,固然也有不少工具能夠自動生產crud,不過這些開源的工具基本上都是針對單個model的,並且生成的代碼須要很大修改才能真正的把功能跑起來,最重要的一點是不能自動生成關聯關係的管理。固然我也見過有公司作了基於數據庫驅動的代碼生產器,能生成完整可用的代碼和頁面,也包括關聯關係的處理,不過因爲語言特性的區別,在開發的時候咱們仍是要不停的重啓server才能顯示出效果來,雖然在技術上,爲ssh實現這個功能並不難,可是會消耗很多時間在上面,消耗了不少時間的話,不多就有公司將其貢獻出來了。因此我的認爲django在這個功能上作得仍是很是不錯的,尤爲這個功能能夠節省開發者不少的時間。甚至有些時候,項目能夠雙線執行,用戶經過admin輸入數據,程序員開發前臺,這樣,前臺功能作完以後,數據也有了,基本能夠測試上線了。在須要快速開發的小項目上,這個特性顯得尤爲重要,由於django產生得時候就是基於這個場景。
固然有時候後臺也沒有這麼簡單,不過還好,admin提供了擴展的功能,咱們能夠本身寫擴展的代碼,而後集成到admin中去,不過事實上除了能改變admin的模板,咱們不能改變任何admin的代碼,不過我時常在想,若是admin支持代碼自動生成的功能,那豈不是很美妙,咱們能夠隨意的修改後臺的功能了,不然咱們就須要本身寫代碼,不如在生成的代碼上擴展方便。
要使用admin,必須打開django的權限模塊,這裏簡單介紹一下權限模塊,django自帶了一個權限模塊,這個權限模塊中的model對於熟悉權限這塊的人來講再熟悉不過了,user,group,permission,user和group多對多,group和permission多對多,在acegi中,咱們一般這樣定義,user,role,resource,這個和django中的權限是同樣的,不過在django中默認的permission的粒度是很是的粗了,是基於model的,若是咱們要更細的權限模塊,那麼就須要本身擴展了。
總的來講admin給個人驚喜大於失望,雖然有點小小的不滿意,可是整體來講仍是很是讚的
五 部署
在這部分開始以前我也想聊聊以前咱們一直在講,並且未來還一直會講下去的一個話題――狀態。
以前咱們一直在討論,把用戶的狀態保存在一個集中的地方,尤爲是大規模集羣部署的狀況下,一樣,對於django來講亦是如此,能夠說這條金科玉律不僅是針對某種針對某個語言,某個框架,它應該是更高層次的一種理念。那麼咱們能夠把狀態放到什麼地方呢,目前一些流行的選擇是DB(內存表,或實體表),memcached,或者cookie,但這幾種選擇並非能夠隨便互換的,好比業務數據較多的狀況下,放在cookie中不是很合適,由於有可能超出cookie大小的限制,那麼放在memcached中,很遺憾,memcached(使用slab的狀況下)中也有它本身的限制,若是狀態數據大小跨度較大,那麼丟數據的狀況有可能發生,ahuaxuan好久以前在測試環境下就碰到過這種狀況,因爲線上memcached開得較大,因此沒有出現這種狀況,關於這種事件發生得內部緣由在ahuaxuan的另一篇文章中已經有了很是詳細的描述。那麼放在DB上呢,顯然,DB的壓力也是咱們須要考慮的問題之一。固然除了這些主流的選擇以外,咱們其餘選擇還有不少,好比memcachedb,或者timesten,或者其餘等等,可是對於狀態這種東西,尤爲狀態數據比較重要的狀況下,咱們必定要深刻研究並理解狀態數據的存儲技術,不然可能會遇到咱們異想不到的狀況,好比好久以前我想破頭也不會想到memcached是LRU是針對某個slab的(並且我還要插一句,LRU的時候其實並非遍歷slab中的chunk鏈表,並且只遍歷最開始的50個數據而已,這樣作純粹是爲了速度)。
目前對django來講基本上有兩種部署策略,
第一種是利用mod_python將django運行在apache進程中,還有一種是webserver+fastcgi,這兩種方式各有優缺點,在mod_python模式中,咱們的webserver必須使用apache,apache在webserver這一領域已經獨佔鰲頭不少年了,市場佔有率也是遠遠的超過其餘的webserver,不過近幾年來,又崛起了幾個其餘的webserver,其中比較出名的是ligttpd和nginx,它們都以高性能和低內存消耗對apache發出了挑戰,而mod_python是apache的插件,使用這種方式就把咱們的webserver限定在apache上了,不過還好apache+mod_python也是很是的穩定的方案了。
第二種就是webserver+fastcgi,這裏的webserver就能夠隨意選擇了,大多數的webserver對提供了對fastcgi的支持,好比咱們耳熟能詳的lighttpd和nginx,並且據稱在不少狀況下,FastCGI可以提供比mod_python更爲優越的安全性和效能。針對小型站點,相對於Apache來講FastCGI更爲輕量級。據稱qq的我的空間就是c++加fastcgi實現的,哦,這樣作的優點在哪裏呢,c++的處理速度將會很是的快,也就是說每一個fastcgi處理一個請求將會很是快速,好比使用python須要50毫秒,c++處理這個請求有可能只須要20毫秒(這個例子未必準確,只是爲了說明fastcgi的特性),雖然在開發上c++比較麻煩一點,不過在性能上,c++確定是no1了,從這個例子上咱們能夠看到,使用fastcgi速度取決於處理一次請求的速度(廢話,哪一個不是這樣)。
咱們來看一下使用fastcgi的通常模式:1、WEB服務器收到客戶端的頁面請求 2、WEB服務器將這個頁面請求委派給一個FastCGI 外部進程(WEB服務器於FastCGI之間是經過socket來鏈接通信的) 3、FastCGI外部進程獲得WEB服務器委派過來的頁面請求信息後進行處理,而且將處理結果(動態頁面內容)返回給WEB服務器 4、Web服務器將FastCGI返回回來的結果再轉送給客戶端瀏覽器。
對咱們來講第3步是咱們最須要關注的,由於第3步的速度嚴重影響着整個性能。因爲fastcgi是基於進程的,因此,咱們要根據咱們的應用來開啓數量合適的fastcgi進程,多開了是對資源的浪費,少開了就影響性能,這個相似咱們在tomcat中開啓處理請求的thread同樣,只不過tomcat中的request handler thread在配置起來顯然更加方便,由於咱們只要關注線程池中最大的能夠容納的線程數,最大空閒線程數等就好了。
固然fastcgi對ahuaxuan這類剛剛跨出java世界的人來講有些不爽的地方,由於基於進程的東東共享數據比較麻煩,好比寫一個ip查詢的組件,功能是這樣的,把ip地址庫加載到內存,而後根據客戶端的ip使用折半搜索改ip所在的城市,用java作很是的方便,先把幾兆的數據加載到內存中,而後每一個線程都來請求就能夠了。而對於fastcgi來就比較麻煩了,須要把這些數據加載每一個fastcgi進程中,無辜浪費掉一堆內存。不過有得必有失,由於每一個fastcgi只能同時處理一個請求,因此使用fastcgi就基本不須要考慮多線程的問題了。
經過幾天時間的學習,確實使我更加了解了python以及django,可是我也知道要掌握一門語言和技術須要的確定是不止幾天而已,幾天能夠說只是入門,說的不對的地方懇請你們批評指正。