新聞中心
Pyramid, Django, 和 Flask都是優(yōu)秀的框架,為項(xiàng)目選擇其中的哪一個(gè)都是傷腦筋的事。我們將會(huì)用三種框架實(shí)現(xiàn)相同功能的應(yīng)用來(lái)更容易的對(duì)比三者。也可以直接跳到框架實(shí)戰(zhàn)(Frameworks in Action)章節(jié)查看代碼(code)。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、虛擬主機(jī)、營(yíng)銷軟件、網(wǎng)站建設(shè)、三門峽網(wǎng)站維護(hù)、網(wǎng)站推廣。
1. 簡(jiǎn)介
世界上可選的基于Python的web框架有很多。Django, Flask, Pyramid, Tornado, Bottle, Diesel, Pecan, Falcon等等,都在爭(zhēng)取開(kāi)發(fā)者支持。作為一開(kāi)發(fā)者從一堆選擇中篩選出一個(gè)來(lái)完成項(xiàng)目將會(huì)成為下一個(gè)大工程。我們今天專注于Flask, Pyramid, 和 Django。它們涵蓋了從小微項(xiàng)目到企業(yè)級(jí)的web服務(wù)。
為了更容易在三者中作出選擇(至少更了解它們),我們將用每一個(gè)框架構(gòu)建同樣的應(yīng)用并比較它們的代碼,對(duì)于每一個(gè)方法我們會(huì)高亮顯示它的優(yōu)點(diǎn)和缺點(diǎn)。如果你只想要代碼,直接跳到框架實(shí)戰(zhàn)章節(jié)(Frameworks in Action),或者查看其在Github上的代碼。
Flask是一個(gè)面向簡(jiǎn)單需求小型應(yīng)用的“微框架(microframework)”。Pyramid和Django都是面向大型應(yīng)用的,但是有不同的拓展性和靈活性。Pyramid的目的是更靈活,能夠讓開(kāi)發(fā)者為項(xiàng)目選擇合適的工具。這意味著開(kāi)發(fā)者能夠選擇數(shù)據(jù)庫(kù)、URL結(jié)構(gòu)、模板類型等等。Django目的是囊括web應(yīng)用的所有內(nèi)容,所以開(kāi)發(fā)者只需要打開(kāi)箱子開(kāi)始工作,將Django的模塊拉進(jìn)箱子中。
Django包括一個(gè)開(kāi)箱即用的 ORM ,而Pyramid和 Flask讓開(kāi)發(fā)者自己選擇如何或者是否存儲(chǔ)他們的數(shù)據(jù)。到目前為止對(duì)于非Django的web應(yīng)用來(lái)說(shuō)***的ORM是SQLAlchemy,同時(shí)還有多種其他選擇,從 DynamoDB和MongoDB 到簡(jiǎn)單本地存儲(chǔ)的LevelDB 或樸實(shí)的SQLite。Pyramid被設(shè)計(jì)為可使用任何數(shù)據(jù)持久層,甚至是還沒(méi)有開(kāi)發(fā)出來(lái)的。
2.關(guān)于框架
Django的”batteries included” 特性讓開(kāi)發(fā)者不需要提前為他們的應(yīng)用程序基礎(chǔ)設(shè)施做決定,因?yàn)樗麄冎繮ython已經(jīng)深入到了web應(yīng)用當(dāng)中。Django已經(jīng)內(nèi)建了模板、表單、路由、認(rèn)證、基本數(shù)據(jù)庫(kù)管理等等。比較起來(lái),Pyramid包括路由和認(rèn)證,但是模板和數(shù)據(jù)庫(kù)管理需要額外的庫(kù)。
前面為 Flask和Pyramid apps選擇組件的額外工作給那些使用案例不適用標(biāo)準(zhǔn)ORM的開(kāi)發(fā)者提供了更多的靈活性,同樣也給使用不同工作流和模版化系統(tǒng)的開(kāi)發(fā)者們帶來(lái)了靈活性。
Flask,作為三個(gè)框架里面最稚氣的一個(gè),開(kāi)始于2010年年中。Pyramid框架是從Pylons項(xiàng)目開(kāi)始的,在2010年底獲得 Pyramid這個(gè)名字,雖然在2005年就已經(jīng)發(fā)布了***個(gè)版本。Django 2006年發(fā)布了***個(gè)版本,就在Pylons項(xiàng)目(***叫Pyramid)開(kāi)始之后。Pyramid和Django都是非常成熟的框架,積累了眾多插件和擴(kuò)展以滿足難以置信的巨大需求。
雖然Flask歷史相對(duì)更短,但它能夠?qū)W習(xí)之前出現(xiàn)的框架并且把注意力放在了微小項(xiàng)目上。它大多數(shù)情況被使用在一些只有一兩個(gè)功能的小型項(xiàng)目上。例如 httpbin,一個(gè)簡(jiǎn)單的(但很強(qiáng)大的)調(diào)試和測(cè)試HTTP庫(kù)的項(xiàng)目。
3. 社區(qū)
***活力的社區(qū)當(dāng)屬Django,其有80,000個(gè)StackOverflow問(wèn)題和一系列來(lái)自開(kāi)發(fā)者和優(yōu)秀用戶的良好的博客。Flask和Pyramid社區(qū)并沒(méi)有那么大,但它們的社區(qū)在郵件列表和IRC上相當(dāng)活躍。StackOverflow上僅有5,000個(gè)相關(guān)的標(biāo)簽,F(xiàn)lask比Django小了15倍。在Github上,它們的star近乎相當(dāng),Django有11,300個(gè),F(xiàn)lask有10,900個(gè)。
三個(gè)框架都使用的是BSD衍生的協(xié)議。Flask和Django的協(xié)議是BSD 3條款,Pyramid的Repoze Public License RPL是BSD協(xié)議 4條款的衍生。
4. Bootstrapping
Django和Pyramid都內(nèi)建bootstrapping工具。Flask沒(méi)有包含類似的工具,因?yàn)镕lask的目標(biāo)用戶不是那種試圖構(gòu)建大型MVC應(yīng)用的人。
4.1 Flask
Flask的hello world應(yīng)用非常的簡(jiǎn)單,僅僅單個(gè)Python文件的7行代碼就夠了。
- # from http://flask.pocoo.org/ tutorial
- from flask import Flask
- app = Flask(__name__)
- @app.route("/") # take note of this decorator syntax, it's a common pattern
- def hello():
- return "Hello World!"
- if __name__ == "__main__":
- app.run()
這是Flask沒(méi)有bootstrapping工具的原因:沒(méi)有它們的需求。從Flask主頁(yè)上的Hello World特性看,沒(méi)有構(gòu)建Python web應(yīng)用經(jīng)驗(yàn)的開(kāi)發(fā)者可以立即開(kāi)始hacking。
對(duì)于各部分需要更多分離的項(xiàng)目,F(xiàn)lask有blueprints。例如,你可以將所有用戶相關(guān)的函數(shù)放在users.py中,將銷售相關(guān)的函數(shù)放在ecommerce.py中,然后在site.py中添加引用它們來(lái)結(jié)構(gòu)化你的Flask應(yīng)用。我們不會(huì)深入這個(gè)功能,因?yàn)樗隽宋覀冋故綿emo應(yīng)用的需求。
4.2 Pyramid
Pyramid 的 bootstrapping工具叫 pcreate,是Pyramid的組成部分. 之前的 Paste 工具套裝提供了 bootstrapping ,但是從那之后被 Pyramid專用工具鏈替代了。
- $ pcreate -s starter hello_pyramid # Just make a Pyramid project
Pyramid 比 Flask 適用于更大更復(fù)雜的應(yīng)用程序. 因?yàn)檫@一點(diǎn),它的 bootstrapping工具創(chuàng)建更大的項(xiàng)目骨架. Pyramid 同樣加入了基本的配置文件,一個(gè)例子模版和用于將程序打包上傳到 Python Package Index的所有文件。
- hello_pyramid
- ├── CHANGES.txt
- ├── development.ini
- ├── MANIFEST.in
- ├── production.ini
- ├── hello_pyramid
- │ ├── __init__.py
- │ ├── static
- │ │ ├── pyramid-16x16.png
- │ │ ├── pyramid.png
- │ │ ├── theme.css
- │ │ └── theme.min.css
- │ ├── templates
- │ │ └── mytemplate.pt
- │ ├── tests.py
- │ └── views.py
- ├── README.txt
- └── setup.py
作為***描述的框架,Pyramid的bootstrapper非常靈活. 不局限于一個(gè)默認(rèn)的程序;pcreate 可以使用任意數(shù)量的項(xiàng)目模版. 包括我們上面用到的pcreate里面的”starter”的模版, 還有 SQLAlchemy- ,ZODB-支持scaffold項(xiàng)目. 在 PyPi可以發(fā)現(xiàn)已經(jīng)為Google App Engine, jQuery Mobile, Jinja2 templating, modern frontend frameworks做好的scaffolds, 還有更多~
4.3 Django
Django 也有自己的 bootstrap 工具, 內(nèi)置在 django-admin 中.
- django-admin startproject hello_django
- django-admin startapp howdy # make an application within our project
Django 跟 Pyramid 區(qū)別在于: Django 由多個(gè)應(yīng)用程序組成一個(gè)項(xiàng)目, 而 Pyramid 以及 Flask 項(xiàng)目是包含 View 和 Model 單一應(yīng)用程序 . 理論上, Flask 和 Pyramid 的項(xiàng)目允許存在多個(gè) project/app, 不過(guò)在默認(rèn)配置中只能有一個(gè).
- hello_django
- ├── hello_django
- │ ├── __init__.py
- │ ├── settings.py
- │ ├── urls.py
- │ └── wsgi.py
- ├── howdy
- │ ├── admin.py
- │ ├── __init__.py
- │ ├── migrations
- │ │ └── __init__.py
- │ ├── models.py
- │ ├── tests.py
- │ └── views.py
- └── manage.py
Django 默認(rèn)只在項(xiàng)目中創(chuàng)建 空白的 model 和模板文件, 供新手參考的示范代碼不多. 此外, 開(kāi)發(fā)者在發(fā)布應(yīng)用程序的時(shí)候, 還要自己配置, 這也是個(gè)麻煩.
bootstrap 工具的缺點(diǎn)是沒(méi)有指導(dǎo)開(kāi)發(fā)者如何打包應(yīng)用. 對(duì)于那些沒(méi)有經(jīng)驗(yàn)的新手來(lái)說(shuō), ***次部署應(yīng)用將是個(gè)很頭疼的問(wèn)題. 像 django-oscar 這樣的大社區(qū), 項(xiàng)目都是打包好了, 放在 PyPi 上供大家安裝. 但是 Github 上面的小項(xiàng)目缺少統(tǒng)一的打包方式.
5. 模板
一個(gè)Python應(yīng)用能夠響應(yīng)HTTP請(qǐng)求將是一個(gè)偉大的開(kāi)端,但是有可能你的大多數(shù)用戶是沒(méi)有興趣使用curl與你的web應(yīng)用交互的。幸運(yùn)的是,這三個(gè)競(jìng)爭(zhēng)者提供了使用自定義信息填充HTML的方法,以便讓大伙們能夠享受時(shí)髦的Bootstrap 前端。
模板讓你能夠直接向頁(yè)面注入動(dòng)態(tài)信息,而不是采用AJAX。你只需要一次請(qǐng)求就可以獲取整個(gè)頁(yè)面以及所有的動(dòng)態(tài)數(shù)據(jù),這對(duì)用戶體驗(yàn)來(lái)說(shuō)是很好的。這對(duì)于手機(jī)網(wǎng)站來(lái)說(shuō)尤其重要,因?yàn)橐淮握?qǐng)求花費(fèi)的時(shí)間會(huì)更長(zhǎng)。
所有的模板選項(xiàng)依賴于“上下文環(huán)境(context)”,其為模板轉(zhuǎn)換為HTML提供了動(dòng)態(tài)信息。模板的最簡(jiǎn)單的例子是填充已登錄用戶的名字以正確的迎接他們。也可以用AJAX獲取這種動(dòng)態(tài)信息,但是用一整個(gè)調(diào)用來(lái)填寫用戶的名字有點(diǎn)過(guò)頭了,而同時(shí)模板又是這么的簡(jiǎn)單。
5.1 Django
我們使用的例子正如寫的那么簡(jiǎn)單,假設(shè)我們有一個(gè)包含了用戶名的funllname屬性的user對(duì)象。在Python中我們這樣向模板中傳遞當(dāng)前用戶:
- def a_view(request):
- # get the logged in user
- # ... do more things
- return render_to_response(
- "view.html",
- {"user": cur_user}
- )
擁有這個(gè)模板的上下文很簡(jiǎn)單,傳入一個(gè)Python對(duì)象的字典和模板使用的數(shù)據(jù)結(jié)構(gòu)?,F(xiàn)在我們需要在頁(yè)面上渲染他們的名字,以防頁(yè)面忘了他們是誰(shuí)。
- {% if user %}
- You are logged in as {{ user.fullname }}
- {% endif %}
首先,你會(huì)注意到這個(gè) {% if user %} 概念。在Django模板中, {% 用來(lái)控制循環(huán)和條件的聲明。這里的if user聲明是為了防止那些不是用戶的情況。匿名用戶不應(yīng)該在頁(yè)面頭部看到“你已經(jīng)登錄”的字樣。
在if塊內(nèi),你可以看到,包含名字非常的簡(jiǎn)單,只要用{{}}包含著我們要插入的屬性就可以了。{{是用來(lái)向模板插入真實(shí)值的,如{{ user.fullname }}。
模板的另一個(gè)常用情況是展示一組物品,如一個(gè)電子商務(wù)網(wǎng)站的存貨清單頁(yè)面。
- def browse_shop(request):
- # get items
- return render_to_response(
- "browse.html",
- {"inventory": all_items}
- )
在模板中,我們使用同樣的{%來(lái)循環(huán)清單中的所有條目,并填入它們各自的頁(yè)面地址。
- {% for widget in inventory %}
- {{ widget.displayname }}
- {% endfor %}
為了做大部分常見(jiàn)的模板任務(wù),Django可以僅僅使用很少的結(jié)構(gòu)來(lái)完成目標(biāo),因此很容易上手。
5.2 Flask
Flask默認(rèn)使用受Django啟發(fā)的Jinja2模板語(yǔ)言,但也可以配置來(lái)使用另一門語(yǔ)言。不應(yīng)該抱怨一個(gè)倉(cāng)促的程序員分不清Django和Jinja模板。事實(shí)是,上面的Django例子在Jinja2也有效。為了不去重復(fù)相同的例子,我們來(lái)看下Jinja2比Django模板更具表現(xiàn)力的地方。
Jinja和Django模板都提夠了過(guò)濾的特性,即傳入的列表會(huì)在展示前通過(guò)一個(gè)函數(shù)。一個(gè)擁有博文類別屬性的博客,可以利用過(guò)濾特性,在一個(gè)用逗號(hào)分割的列表中展示博文的類別。
Categories: {{ post.categories|join:", " }} Categories: {{ post.categories|join(", ") }}
在Jinja模板語(yǔ)言中,可以向過(guò)濾器傳入任意數(shù)量的參數(shù),因?yàn)镴inja把它看成是 使用括號(hào)包含參數(shù)的Python函數(shù)的一個(gè)調(diào)用。Django使用冒號(hào)來(lái)分割過(guò)濾器的名字和過(guò)濾參數(shù),這限制了參數(shù)的數(shù)目只能為一。
Jinjia和Django的for循環(huán)有點(diǎn)類似。我們來(lái)看看他們的不同。在Jinjia2中,for-else-endfor結(jié)構(gòu)能遍歷一個(gè)列表,同時(shí)也處理了沒(méi)有項(xiàng)的情況。
- {% for item in inventory %}
{{ item.render() }}- {% else %}
No items found
Try another search, maybe?
- {% endfor %}
Django版的這個(gè)功能是一樣的,但是是用for-empty-endfor而不是for-else-endfor。
- {% for item in inventory %}
{{ item.render }}- {% empty %}
No items found
Try another search, maybe?
- {% endfor %}
除了語(yǔ)法上的不同,Jinja2通過(guò)執(zhí)行環(huán)境和高級(jí)特性提供了更多的控制。例如,它可以關(guān)閉危險(xiǎn)的特性以安全的執(zhí)行不受信任的模板,或者提前編譯模板以確保它們的合法性。
5.3 Pyramid
與Flask類似,Pyramid支持多種模板語(yǔ)言(包括Jinja2和Mako),但是默認(rèn)只附帶一個(gè)。Pyramid使用Chameleon,一個(gè) ZPT (Zope Page Template) 模板語(yǔ)言的實(shí)現(xiàn)。我們來(lái)回頭看看***個(gè)例子,添加用戶的名字到網(wǎng)站的頂欄。Python代碼除了明確調(diào)用了render_template函數(shù)外其他看起來(lái)都差不多。
- @view_config(renderer='templates/home.pt')
- def my_view(request):
- # do stuff...
- return {'user': user}
但是我們的模板看起來(lái)有些不同。ZPT是一個(gè)基于XML得模板標(biāo)準(zhǔn),所以我們使用了類XSLT語(yǔ)句來(lái)操作數(shù)據(jù)。
- tal:content="string:You are logged in as ${user.fullname}"
- class="col-md-2 whoami">
Chameleon對(duì)于模板操作有三種不同的命名空間。TAL(模板屬性語(yǔ)言)提供了基本的條件語(yǔ)句,字符串的格式化,以及填充標(biāo)簽內(nèi)容。上面的例子只用了TAL來(lái)完成相關(guān)工作。對(duì)于更多高級(jí)任務(wù),就需要TALES和METAL。TALES( 模板屬性表達(dá)式語(yǔ)法的語(yǔ)言)提供了像高級(jí)字符串格式化,Python表達(dá)式評(píng)估,以及導(dǎo)入表達(dá)式和模板的表達(dá)式。
METAL(宏擴(kuò)展模板屬性語(yǔ)言)是Chameleon模板***大的(和復(fù)雜的)一部分。宏是可擴(kuò)展的,并能被定義為帶有槽且當(dāng)宏被調(diào)用時(shí)可以被填充。
6. 利用框架行動(dòng)起來(lái)
對(duì)于各個(gè)框架,我們將通過(guò)制作一個(gè)叫做wut4lunch的應(yīng)用來(lái)了解,這個(gè)應(yīng)用是告訴整個(gè)互聯(lián)網(wǎng)你午飯吃了什么的社交網(wǎng)絡(luò)。很自由的一個(gè)起始想法,完全可以隨意改變。應(yīng)用將有一個(gè)簡(jiǎn)單的接口,允許用戶提交他們午飯的內(nèi)容,并看到其他用戶吃的什么的列表。主頁(yè)完成后將看起來(lái)像這樣。
6.1 使用Flask的Demo應(yīng)用
最短的實(shí)現(xiàn)用了34行Python代碼和一個(gè)22行的Jinja模板。首先,我們有些管理類的任務(wù)要做,比如初始化我們的應(yīng)用并拉近我們的ORM。
- from flask import Flask
- # For this example we'll use SQLAlchemy, a popular ORM that supports a
- # variety of backends including SQLite, MySQL, and PostgreSQL
- from flask.ext.sqlalchemy import SQLAlchemy
- app = Flask(__name__)
- # We'll just use SQLite here so we don't need an external database
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
- db = SQLAlchemy(app)
現(xiàn)在我們看下我們的模型,這將和另兩個(gè)樣例基本一樣。
- class Lunch(db.Model):
- """A single lunch"""
- id = db.Column(db.Integer, primary_key=True)
- submitter = db.Column(db.String(63))
- food = db.Column(db.String(255))
哇,相當(dāng)簡(jiǎn)單。最難的部分是找到合適的 SQLAlchemy數(shù)據(jù)類型,選擇數(shù)據(jù)庫(kù)中String域的長(zhǎng)度。使用我們的模型也超級(jí)簡(jiǎn)單,這在于我們將要看到 SQLAlchemy查詢語(yǔ)法。
構(gòu)建我們的提交表單也很簡(jiǎn)單。在引入Flask-WTForms和正確的域類型后,你可以看到表單看起來(lái)有點(diǎn)像我們的模型。主要的區(qū)別在于新的提交按鈕和食物與提交者姓名域的提示。
應(yīng)用中的SECRET_KEY域是被WTForms用來(lái)創(chuàng)建CSRF符號(hào)的。它也被itsdangerous(Flask內(nèi)包含)用來(lái)設(shè)置cookies和其他數(shù)據(jù)。
- from flask.ext.wtf import Form
- from wtforms.fields import StringField, SubmitField
- app.config['SECRET_KEY'] = 'please, tell nobody'
- class LunchForm(Form):
- submitter = StringField(u'Hi, my name is')
- food = StringField(u'and I ate')
- # submit button will read "share my lunch!"
- submit = SubmitField(u'share my lunch!')
讓表單在瀏覽器中顯示意味著模板要有它。我們像下面那樣傳遞進(jìn)去。
- from flask import render_template
- @app.route("/")
- def root():
- lunches = Lunch.query.all()
- form = LunchForm()
- return render_template('index.html', form=form, lunches=lunches)
好了,發(fā)生了什么?我們得到已經(jīng)用Lunch.query.all()提交的午餐列表,并實(shí)例化一個(gè)表單,讓用戶提交他們自己的美食之旅。為了簡(jiǎn)化,變量使用相同的名字出入模板,但這不是必須的。
Wut 4 Lunch - What are people eating?
Wut4Lunch is the latest social network where you can tell all your friends
- about your noontime repast!
這就是模板的真實(shí)情況,我們?cè)谝呀?jīng)吃過(guò)的午餐中循環(huán),并在
- 中展示他們。這幾乎與我們前面看到的循環(huán)例子一樣。
- {% for lunch in lunches %}
- {{ lunch.submitter|safe }} just ate {{ lunch.food|safe }}
- {% else %}
- Nobody has eaten lunch, you must all be starving!
- {% endfor %}
- What are YOU eating?
- {{ form.hidden_tag() }}
- {{ form.submitter.label }} {{ form.submitter(size=40) }}
- {{ form.food.label }} {{ form.food(size=50) }}
- {{ form.submit }}
模板的
表單被不同的語(yǔ)法渲染,我們需要人工在表單主體中添加CSRF token,但這些區(qū)別更多的是裝飾
6.3測(cè)試Pyramid版App
***,我們看看用Pyramid實(shí)現(xiàn)的同樣的程序。與Django和Flask的***不同是模板。只需要對(duì)Jinja2做很小的改動(dòng)就足以解決我們?cè)贒jango中的問(wèn)題。這次不是這樣的,Pyramid的Chameleon模板的語(yǔ)法更容易讓人聯(lián)想到XSLT而不是別的。
- Nobody has eaten lunch, you must all be starving!
與Django模板類似,缺少for-else-endfor結(jié)構(gòu)使得邏輯稍微的更清晰了。這種情況下,我們以if-for 和 if-not-for 語(yǔ)句塊結(jié)尾以提供同樣的功能。使用{{或{%來(lái)控制結(jié)構(gòu)和條件的Django以及AngularJS類型的模板讓使用XHTML標(biāo)簽的模板顯得很外行。
Chameleon模板類型的一大好處是你所選擇的編輯器可以正確的使語(yǔ)法高亮,因?yàn)槟0迨怯行┑肵HTML。對(duì)于Django和Flask模板來(lái)說(shuō),你的編輯器需要能夠正確的支持這些模板語(yǔ)言高亮顯示。
- What are YOU eating?
- Name: ${form.text("submitter", size=40)}
- What did you eat? ${form.text("food", size=40)}


咨詢
建站咨詢