自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Rails和Django的深度技術(shù)對比 難以取舍的公正

開發(fā) 后端
6個(gè)月以前我在大學(xué)用Ruby on Rails做了一個(gè)項(xiàng)目而且一直做到現(xiàn)在。我做地第一件事就是仔細(xì)地學(xué)習(xí)了這兩個(gè)框架并對它們進(jìn)行了比較,但是我記得當(dāng)時(shí)我很泄氣的。當(dāng)我尋找這些問題(比如說:”對于這兩者來說,數(shù)據(jù)庫的遷移是如何操作的?“、”它們的語法有什么區(qū)別?“、”用戶認(rèn)證是如何做的“)的答案時(shí),我站在一個(gè)較高的角度比較了兩者,發(fā)現(xiàn)它們大部分是非常膚淺的。

我想以一個(gè)免責(zé)聲明來開始下面的內(nèi)容。我使用Django開發(fā)網(wǎng)站已經(jīng)有三年了,眾所周知,我喜歡Django。我已經(jīng)寫了一個(gè)開源的應(yīng)用程序(app),并且我已經(jīng)將補(bǔ)丁發(fā)送到了Django.然而,我以盡可能以公正的態(tài)度寫了這篇文章,這篇文章對這個(gè)框架有稱贊,也有批評。

6個(gè)月以前我在大學(xué)用Ruby on Rails做了一個(gè)項(xiàng)目而且一直做到現(xiàn)在。我做地***件事就是仔細(xì)地學(xué)習(xí)了這兩個(gè)框架并對它們進(jìn)行了比較,但是我記得當(dāng)時(shí)我很泄氣的。當(dāng)我尋找這些問題(比如說:”對于這兩者來說,數(shù)據(jù)庫的遷移是如何操作的?“、”它們的語法有什么區(qū)別?“、”用戶認(rèn)證是如何做的“)的答案時(shí),我站在一個(gè)較高的角度比較了兩者,發(fā)現(xiàn)它們大部分是非常膚淺的。下面的評論將會回答這些問題并且比較每個(gè)web框架是如何操作模型、控制器、視圖、測試的。

簡要介紹

兩個(gè)框架都是為了更快的開發(fā)web應(yīng)用程序和更好的組織代碼這兩個(gè)需求應(yīng)運(yùn)而生的. 它們都遵循 MVC 原則, 這意味著域(模型層)的建模,應(yīng)用程序的展現(xiàn)(視圖層)以及用戶交互(控制層)三者之間都是相互分開的. 附帶說明一下, Django 實(shí)際上只考慮了讓框架做控制層的工作,因此Django 自己聲稱它是一個(gè)模型-模板-視圖(model-template-view)框架. Django 的模板可以被理解為視圖,而視圖則可以看做是MVC典型場景中的控制層. 本文中我將都是用標(biāo)準(zhǔn)的MVC術(shù)語.

Ruby on Rails

 

Ruby on Rails (RoR) 是一個(gè)用 Ruby 寫就的web開發(fā)框架,并且Ruby“famous”也經(jīng)常被認(rèn)為是歸功于它的. Rails 著重強(qiáng)調(diào)了約定大于配置和測試這兩個(gè)方面. Rails 的約定大于配置(CoC)意味著幾乎沒有配置文件, 只有實(shí)現(xiàn)約定好的目錄結(jié)構(gòu)和命名規(guī)則. 它的每一處都藏著很多小魔法: 自動引入, 自動向視圖層傳遞控制器實(shí)體,一大堆諸如模板名稱這樣的東西都是框架能自動推斷出來的. 這也就意味著開發(fā)者只需要去指定應(yīng)用程序中沒有約定的部分, 結(jié)果就是干凈簡短的代碼了.

Django

Django 是一個(gè)用 Python 寫成的web開發(fā)框架,并以吉他手 Django Reinhardt 命名. Django 出現(xiàn)的動機(jī)在于 "產(chǎn)品部密集的***期限和開發(fā)了它的有經(jīng)驗(yàn)的Web開發(fā)者他們的嚴(yán)格要求". Django 遵循的規(guī)則是 明確的說明要比深晦的隱喻要好 (這是一條核心的 Python 原則), 結(jié)果就是即使對框架不熟悉的人,代碼都是非常具有可讀性的. 項(xiàng)目中的Django是圍繞app組織的.  每一個(gè)app都有其自己的模型,控制器,視圖以及測試設(shè)置,從而像一個(gè)小項(xiàng)目一樣. Django 項(xiàng)目基本上就是一個(gè)小app的集合, 每一個(gè)app都負(fù)責(zé)一個(gè)特定的子系統(tǒng).

模型(Model)
 

讓我們先從看看每個(gè)框架怎樣處理MVC原則開始. 模型描述了數(shù)據(jù)看起來是什么樣子的,并且還包含了業(yè)務(wù)邏輯.

創(chuàng)建模型

Rails 通過在終端中運(yùn)行一個(gè)命令來創(chuàng)建模型.

  1. rails generate model Product name:string quantity_in_stock:integer   
  2.                              category:references 

該命令會自動生成一次遷移和一個(gè)空的模型文件,看起來像下面這樣:

  1. class Product < ActiveRecord::Base  
  2.    
  3. end 

由于我有Django的技術(shù)背景, 令我很生氣的一個(gè)事實(shí)就是我不能只通過模型文件就了解到一個(gè)模型有哪些字段. 我了解到Rails基本上只是將模型文件用于業(yè)務(wù)邏輯,而把模型長什么樣存到了一個(gè)叫做 schemas.rb 的文件中. 這個(gè)文件會在每次有遷移運(yùn)行時(shí)被自動更新. 如果我們看看該文件,我們可以看到我們的 Product 模型長什么樣子.

  1. create_table "products", :force => true do |t|  
  2.   t.string   "name",  
  3.   t.integer  "quantity_in_stock",  
  4.   t.integer  "category_id",  
  5.   t.datetime "created_at", :null => false 
  6.   t.datetime "updated_at", :null => false 
  7. end 

在這個(gè)模型中你可以看到兩個(gè)額外的屬性. created_at 和 updated_at 是兩個(gè)會被自動添加到Rails中的每個(gè)模型中的屬性.

在 Django 中,模型被定義到了一個(gè)叫做models.py的文件中. 同樣的 Product 模型看起來也許會像下面這樣

  1. class Product(models.Model):  
  2.     name = models.CharField()  
  3.     quantity_in_stock = models.IntegerField()  
  4.     category = models.ForeignKey('Category')  
  5.     created_at = models.DateTimeField(auto_now_add=True) # set when it's created  
  6.     updated_at = models.DateTimeField(auto_now=True) # set every time it's updated 

注意,我們必須在Django中明確的(也就是自己手動的)添加 created_at 和 updated_at 屬性. 我們也要通過auto_now_add 和 auto_now 兩個(gè)參數(shù)告訴 Django 這些屬性的行為是如何定義的.

模型(Model)字段默認(rèn)值和外鍵

Rails將默認(rèn)允許字段為空。你可以在上面的例子中看到,我們創(chuàng)建的三個(gè)字段都被允許為空。引用字段類別也將既不創(chuàng)建索引,也不創(chuàng)建一個(gè)外鍵約束。這意味著引用完整性是無法保證的。Django的字段默認(rèn)值是完全相反的。沒有字段是被允許為空的,除非明確地設(shè)置。Django的ForeignKey將自動創(chuàng)建一個(gè)外鍵約束和索引。盡管Rails這里的制定可能是出于性能的擔(dān)憂,但我會站在Django這邊,我相信這個(gè)制定可以避免(意外)糟糕的設(shè)計(jì)和意想不到的情況。舉例來說,在我們的項(xiàng)目中以前有一個(gè)學(xué)生沒有意識到他創(chuàng)建的所有字段都被允許空(null)作為默認(rèn)值。一段時(shí)間后,我們發(fā)現(xiàn)我們的一些表包含的數(shù)據(jù)是毫無意義的,如一個(gè)使用null作為標(biāo)題的輪詢。由于Rails不添加外鍵,在我們的例子中,我們可以刪除一個(gè)持續(xù)引用其他產(chǎn)品的類別,這些產(chǎn)品將會有無效引用。一種選擇是使用一個(gè)第三方應(yīng)用程序,增加對自動創(chuàng)建外鍵的支持。

遷移(Migrations)

遷移允許數(shù)據(jù)庫的模式(schema)在創(chuàng)建之后可以再次更改(實(shí)際上,在Rails中所有的內(nèi)容都使用遷移,即使是創(chuàng)建)。我不得不敬佩Rails長期以來支持這個(gè)特性。通過使用Rails的生成器(generator)即可完成工作。

  1. $ rails generate migration AddPartNumberToProducts part_number:string 

這會向Product模型(model)添加一個(gè)名為part_number的新字段(field)。

然而Django只能通過名為South的第三方庫來支持遷移。我感覺South的方式更加簡潔和實(shí)用。上面對應(yīng)的遷移工作可以直接編輯Product模型的定義并添加新的字段

  1. class Product(models.Model):  
  2.     ... # 舊字段  
  3.     part_number = models.CharField() 

然后調(diào)用

  1. $ python manage.py schemamigration products --auto 

South會自動識別到一個(gè)新增字段添加到Product模型并創(chuàng)建遷移文件。隨后會調(diào)用下面命令完成同步(synced)

  1. $ python manage.py migrate products 

Django最終在它的***版本(1.7) 將South整合進(jìn)來并支持遷移。

執(zhí)行查詢
 

感謝對象關(guān)系映射(object-relation mapping),你不需要在任何框架中寫一行SQL語句。感謝Ruby表達(dá)式,你能夠很優(yōu)雅的寫出范圍搜索查詢(range query)。.

  1. Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) 

 

這會查詢到昨天創(chuàng)建的Clients。Python不支持像1.day這種極其可讀和簡潔的語法,也不支持..范圍操作符。但是,有時(shí)在寫Rails時(shí)我感覺像是又在寫預(yù)聲明(prepared statement)一樣。比如為了選擇所有的某一字段大于某個(gè)值的行,你不得不像下面這樣

  1. Model.where('field >= ?', value) 

 

Django完成的方式不是太好,但以我的觀點(diǎn),卻更加簡介。在Django對應(yīng)的代碼如下:

  1. Model.objects.filter(field__gt=value) 

控制器(Controller)
 

控制器的工作就是利用請求返回準(zhǔn)確的應(yīng)答。網(wǎng)絡(luò)應(yīng)用程序典型工作是支持添加,編輯,刪除和顯示具體的資源,而RoR的便捷表現(xiàn)在使開發(fā)控制器的工作簡單而貼心??刂破鞅徊鸱譃槿舾蓚€(gè)方法(method),每個(gè)方法代表指定的動作(action)(show代表請求某個(gè)資源,new代表顯示創(chuàng)建資源的表單,create代表從new接收POST的數(shù)據(jù)并真正的創(chuàng)建資源)??刂破鞯膶?shí)例變量(以@為前綴)會自動被傳遞給視圖(view),Rails從方法名稱就會識別應(yīng)該把哪個(gè)模板(template)作為視圖。

  1. class ProductsController < ApplicationController  
  2.   # 自動渲染views/products/show.html.erb  
  3.   def show  
  4.     # params是包含請求變量的ruby hash  
  5.     # 實(shí)例變量會自動被傳遞給視圖  
  6.     @product = Product.find(params[:id])  
  7.   end  
  8.    
  9.   # 返回空的product,渲染views/products/new.html.erb  
  10.   def new 
  11.     @product = Product.new 
  12.   end  
  13.    
  14.   # 接收用戶提交的POST數(shù)據(jù)。多數(shù)來至于在'new'視圖中表單  
  15.   def create  
  16.     @product = Product.new(params[:product])  
  17.     if @product.save  
  18.       redirect_to @product 
  19.     else 
  20.       # 重寫渲染create.html.erb的默認(rèn)行為  
  21.       render "new" 
  22.     end  
  23.   end  
  24. end 

Django使用兩種不同的方式實(shí)現(xiàn)控制器。你可以使用一個(gè)方法來實(shí)現(xiàn)每個(gè)動作,與Rails做法非常相似,或者你可以為每個(gè)控制器動作創(chuàng)建一個(gè)類。 Django沒有區(qū)分new和create方法,資源的創(chuàng)建和空資源的創(chuàng)建發(fā)生在同一個(gè)控制器中。也沒有便捷的方法命名你的視圖。視圖變量需要從控制器顯式的傳遞,而使用的模板文件也需要顯式的設(shè)置。

  1. # django通常稱 'show' 方法為'detail' 
  2. # product_id 參數(shù)由route傳遞過來  
  3. def detail(request, product_id):  
  4.     p = Product.objects.get(pk=product_id) # pk 表示主鍵  
  5.    
  6.     # 使用傳遞的第三個(gè)參數(shù)作為內(nèi)容渲染detail.html  
  7.     return render(request, 'products/detail.html', {'product': p})  
  8.    
  9. def create(request):  
  10.     # 檢查表單是否提交   
  11.     if request.method == 'POST':  
  12.         # 類似于RoR的 'create' 動作  
  13.         form = ProductForm(request.POST) # 綁定于POST數(shù)據(jù)的表單  
  14.         if form.is_valid(): # 所有的驗(yàn)證通過  
  15.             new_product = form.save()  
  16.             return HttpResponseRedirect(new_product.get_absolute_url())  
  17.     else:  
  18.         # 類似于RoR的 'new' 動作  
  19.         form = ProductForm() # 空的表單  
  20.    
  21.     return render(request, 'products/create.html', { 'form': form }) 

在以上Django的例子中代碼數(shù)量與RoR相比很明顯。Django似乎也注意到這個(gè)問題,于是利用繼承和mixin開發(fā)出了第二種實(shí)現(xiàn)控制器的方法。第二種方法稱為基于類的視圖(class-based views) (注意, Django稱這個(gè)控制器為view),并且在Django 1.5中引入以提高代碼重用。很多常用的動作都存在可被用來繼承的類,比如對資源的顯示,列表,創(chuàng)建和更新等,這大大簡化了代碼開發(fā)。重復(fù)的工作比如指定將被使用的視圖文件名稱,獲取對象并向view傳遞該對象等工作也會被自動完成。上面相同的例子使用這種方式只有四行代碼。

  1. # 假設(shè)route傳遞了名為 'pk' 的參數(shù),包含對象的 id 并使用該id獲得對象。  
  2. # 自動渲染視圖 /products/product_detail.html  
  3. # 并將product作為上下文(context)變量傳遞給該視圖  
  4. class ProductDetail(DetailView):  
  5.     model = Product  
  6.    
  7. # 為給定的模型生成表單。如果得到POST數(shù)據(jù)  
  8. # 自動驗(yàn)證表單并創(chuàng)建資源。  
  9. # 自動渲染視圖 /products/product_create.html  
  10. # 并將表單作為上下文變量傳遞給視圖  
  11. class ProductCreate(CreateView):  
  12.     model = Product 

當(dāng)控制器比較簡單時(shí),使用基于類的視圖(class-based views)通常是***的選擇,因?yàn)榇a會變得緊密,具有可讀性。但是,取決于你的控制器的不標(biāo)準(zhǔn)(non-standard)程度,可能會需要重寫很多函數(shù)來得到想要的功能。常遇到的情況就是程序員想向視圖傳遞更多的變量,這時(shí)可以重寫get_context_data函數(shù)來完成。你是不是想按照當(dāng)前對象(模型實(shí)例)的特定的字段來渲染不同的模板?你只好重寫render_to_response函數(shù)。你想不想改變獲得對象的方式(默認(rèn)是使用主鍵字段pk)?你只好重寫get_object。例如,如果我們想要通過產(chǎn)品名稱選擇產(chǎn)品而不是id,也要把類似的產(chǎn)品傳遞給我們的視圖,代碼就有可能像這樣:

  1. class ProductDetail(DetailView):  
  2.     model = Product  
  3.    
  4.     def get_object(self, queryset=None):  
  5.         return get_object_or_404(Product, key=self.kwargs.get('name'))  
  6.    
  7.     def get_context_data(self, **kwargs):  
  8.         # 先調(diào)用基類函數(shù)獲取上下文  
  9.         context = super(ProductDetail, self).get_context_data(**kwargs)  
  10.    
  11.         # 在相關(guān)產(chǎn)品(product)中添加  
  12.         context['related_products'] = self.get_object().related_products  
  13.         return context 

視圖
 

Rails 視圖使用 內(nèi)置的Ruby 模板系統(tǒng),它可以讓你在你的模板里面編寫任意的Ruby代碼. 這就意味著它非常強(qiáng)大和快速, 而非常強(qiáng)大的同時(shí)就意味著非常大的責(zé)任. 你不得不非常小心的不去把表現(xiàn)層同任何其它類型的邏輯混在一起. 這里我需要再次提到涉及一位學(xué)生的例子. 一位新同學(xué)加入了我們的RoR項(xiàng)目,并且在學(xué)習(xí)一項(xiàng)新特性. 代碼審查的時(shí)間到了. 我們首先從控制器開始,***件令我吃驚的事情是他寫的控制器里面代碼非常少. 我轉(zhuǎn)而很快去看看他寫的視圖,看到了大塊混著HTML的ruby代碼. 誠然,Rails并不會嫌棄缺乏經(jīng)驗(yàn)的程序員,但我的觀點(diǎn)是框架可以幫助開發(fā)者避免一些壞的實(shí)踐. 例如 Django 就有一個(gè)非常簡潔的 模板語言. 你可以進(jìn)行if判斷以及通過for循環(huán)進(jìn)行迭代,但是沒有方法選擇沒有從控制器傳入的對象,因?yàn)樗⒉粫?zhí)行任意的Python表達(dá)式. 這是一個(gè)我認(rèn)為可以敦促開發(fā)者方向正確的設(shè)計(jì)決定. 這能讓我們項(xiàng)目中的新手找到組織他們代碼的正確方式.

資源: CSS, Javascript 以及 圖片

Rails 有一個(gè)很不錯(cuò)的內(nèi)置 資源管道. Rails 的資源管道具有對JavaScript和CSS文件進(jìn)行串聯(lián)、最小化和壓縮的能力. 不僅僅如此,它也還支持諸如 Coffeescript, Sass 和 ERB 等其它語言. Django 對資源的支持同Rails相比就顯得相形見絀了,它把要麻煩都丟給了開發(fā)者去處理. Django 唯一提供的就是所謂的 靜態(tài)文件, 這基本上就只是從每個(gè)應(yīng)用程序那里將所有的靜態(tài)文件集合到一個(gè)位置. 有一個(gè)叫做 django_compressor 的第三方app提供了一種類似于Rails的資源管道的解決方案.

單(Forms)

網(wǎng)絡(luò)應(yīng)用中的表單是用戶輸入(input)的界面。在Rails中的表單包含在視圖中直接使用的幫助方法(method)。

  1. <%= form_tag("/contact", method: "post") do %>  
  2.   <%= label_tag(:subject, "Subject:") %>  
  3.   <%= text_field_tag(:subject) %>  
  4.   <%= label_tag(:message, "Message:") %>  
  5.   <%= text_field_tag(:message) %>  
  6.   <%= label_tag(:subject, "Sender:") %>  
  7.   <%= text_field_tag(:sender) %>  
  8.   <%= label_tag(:subject, "CC myself:") %>  
  9.   <%= check_box_tag(:sender) %>  
  10.   <%= submit_tag("Search") %>   
  11. <% end %> 

 

像subject,message這樣的輸入字段可以在控制器中通過ruby哈希 (類似字典的結(jié)構(gòu))params來讀取,比如params[:subject]和params[:message]。Django通過另一種方式抽象了表單概念。表單封裝了字段并包含驗(yàn)證規(guī)則。它們看起來像是模型。

  1. class ContactForm(forms.Form):  
  2.     subject = forms.CharField(max_length=100)  
  3.     message = forms.CharField()  
  4.     sender = forms.EmailField()  
  5.     cc_myself = forms.BooleanField(required=False) 

 

Django會將CharField解析為對應(yīng)HTML元素的文本輸入框,將BooleanField解析為單選框。你可以按照自己的意愿使用 widget 字段更換為其他輸入元素。Django的表單會在控制器中實(shí)例化。

  1. def contact(request):  
  2.     if request.method == 'POST':  
  3.         form = ContactForm(request.POST)  
  4.         if form.is_valid():  
  5.             return HttpResponseRedirect('/thanks/'# POST之后重定向  
  6.     else:  
  7.         form = ContactForm() # An unbound form  
  8.    
  9.     return render(request, 'contact.html', { 'form': form }) 

 

Django會自動添加驗(yàn)證信息。默認(rèn)情況下所有的字段都是必須的,除非特意定義(比如cc_myself)。使用上面的代碼片段,如果表單驗(yàn)證失敗,帶有錯(cuò)誤信息的表單會自動重新顯示,已經(jīng)輸入的內(nèi)容也會顯示。下面的代碼在視圖中顯示顯示了一個(gè)表單。

  1. <form action="/contact/" method="post">   
  2. {{ form.as_p }} <!-- 生成類似于rails的表單 -->  
  3. <input type="submit" value="Submit" />  
  4. </form> 

URL 和 Route

Route 是將特定的URL匹配到指定控制器的工作。Rails建立REST網(wǎng)絡(luò)服務(wù)非常輕松,而route以HTTP的行為(verbs)來表達(dá)。

  1. get '/products/:id', to: 'products#show' 

以上的例子顯示,向/products/any_id發(fā)起的GET請求會自動route到products控制器和show動作(action)。感謝慣例優(yōu)先原則(convention-over-configuration),對于建立包含所有動作(create,show,index等等)的控制器的這種常見任務(wù),RoR建立了一種快速聲明所有常用route的方法,叫做resources。如果你依照Rails的慣例(convention)命名了控制器的方法時(shí)這會很方便。

  1. # automatically maps GET /products/:id to products#show  
  2. #                    GET /products to products#index  
  3. #                    POST /products to products#create  
  4. #                    DELETE /products/:id to products#destroy  
  5. #                    etc.  
  6. resources :products 

Django不是通過HTTP的行為來決定route。而是使用更復(fù)雜的使用正則表達(dá)式來匹配URL和對應(yīng)的控制器。

  1. urlpatterns = patterns('',  
  2.     # 在products控制器中匹配具體方法  
  3.     url(r'^products/(?P\d+)/$', products.views.DetailView.as_view(), name='detail'),  
  4.     # 匹配index方法就獲得了主頁  
  5.     url(r'^products/$', products.views.IndexView.as_view(), name='index'),  
  6.     url(r'^products/create/$', products.views.CreateView.as_view(), name='create'),  
  7.     url(r'^products/(?P\d+)/delete/$', products.views.DeleteView.as_view(), name='delete'),  

由于使用了正則表達(dá)式,框架會自動使用單純的驗(yàn)證。請求/products/test/會因匹配不到任何route而返回404,因?yàn)閠est不是正確的數(shù)字。不同的哲學(xué)思想在這里又一次出現(xiàn)。Django在命名控制器動作方面確實(shí)方便一些,以至于Django就沒有像Rails的resource那樣方便的助手,而且每個(gè)route必須顯式的定義。這將導(dǎo)致每個(gè)控制器需要若干個(gè)route規(guī)則。

測試

在Rails中測試很輕松,與Django比較起來更需要著重強(qiáng)調(diào)。

Fixture

兩個(gè)框架以相似的方式都支持fixture(示例數(shù)據(jù))。我卻給Rails更高的評價(jià),因?yàn)樗鼘?shí)用,能從文件的名稱得知你在使用哪個(gè)模板。Rails使用YAML格式的fixture,這是人類可讀的數(shù)據(jù)序列化格式。

  1. # users.yml (Rails當(dāng)前知道我們在使用user的fixtures)  
  2. john:  
  3.   name: John Smith  
  4.   birthday: 1989-04-17 
  5.   profession: Blacksmith  
  6.    
  7. bob:  
  8.   name: Bob Costa  
  9.   birthday: 1973-08-10 
  10.   profession: Surfer 

 

所有的fixture會自動加載而且在測試中可以作為本地變量來訪問。

  1. users(:john).name # John Smith 

 

Django也支持YAML格式的fixture但是開發(fā)人員更傾向于使用JSON格式。

  1. [  
  2.   {  
  3.     "model""auth.user",  
  4.     "fields": {  
  5.       "name""John Smith",  
  6.       "birthday""1989-04-17",  
  7.       "profession""Blacksmith",  
  8.     }  
  9.   },  
  10.   {  
  11.     "model""auth.user",  
  12.     "fields": {  
  13.       "name""Bob Costa",  
  14.       "birthday""1973-08-10",  
  15.       "profession""Surfer",  
  16.     }  
  17.   }  

這沒什么吸引力,注意它有多啰嗦,因?yàn)槟惚仨氾@式的定義它屬于哪個(gè)模板,然后在 fields下面列出每個(gè)字段。

#p#

測試模板

在單元測試模板時(shí)兩種框架的方式基本一致。使用一組不同類型的斷言來進(jìn)行確定,比如assert_equal,assert_not_equal,assert_nil,assert_raises等等。

  1. class AnimalTest < ActiveSupport::TestCase  
  2.   test "Animals that can speak are correctly identified" do 
  3.      assert_equal animals(:lion).speak(), 'The lion says "roar"' 
  4.      assert_equal animals(:cat).speak(), 'The cat says "meow"' 
  5.   end  
  6. end 

類似功能的代碼在Django非常相似。

  1. class AnimalTestCase(TestCase):  
  2.     def test_animals_can_speak(self):  
  3.         """Animals that can speak are correctly identified""" 
  4.         # no way of directly accessing the fixtures, so we have to  
  5.         # manually select the objects  
  6.         lion = Animal.objects.get(name="lion")   
  7.         cat = Animal.objects.get(name="cat")  
  8.         self.assertEqual(lion.speak(), 'The lion says "roar"')  
  9.         self.assertEqual(cat.speak(), 'The cat says "meow"'

 

測試控制器(controller)

Rails又因?yàn)樗攘Χ鼊僖换I。Rails 使用類名稱來決定哪個(gè)控制器正在被測試,而測試某個(gè)特定動作(action)就像調(diào)用http_verb :action_name一樣簡單。我們看一下例子。

  1. class UsersControllerTest < ActionController::TestCase  
  2.   test "should get index" do  
  3.     get :index # 向index 動作發(fā)起GET請求  
  4.     assert_response :success # 請求返回200  
  5.     # assigns是包含所有實(shí)例變量的hash  
  6.     assert_not_nil assigns(:users)  
  7.   end  
  8. end 

上面的代碼很容易理解正在發(fā)生什么。***行測試模擬了向 User控制器的 index動作發(fā)起一個(gè)請求。第二行隨后檢查請求是否成功(返回代碼200-299)。 assigns是一個(gè)hash,包含了傳遞到視圖(view)的實(shí)例變量。所以第三行檢查是否存在名為 users的實(shí)例變量并且值不是 nil。

也有一些類似于assert_difference 這樣方便的斷言幫助方法。

  1. # assert_difference檢查被測試的數(shù)字在開始和結(jié)束之間是否更改   
  2. assert_difference('Post.count') do  
  3.   # 創(chuàng)建post  
  4.   post :create, post: {title: 'Some title'}  
  5. end 

在Django中測試控制器可以通過使用一個(gè)叫 Client類來完成,它扮演著虛擬瀏覽器(dummy web browser)的角色。下面是Django中對應(yīng)的代碼。

  1. class UsersTest(unittest.TestCase):  
  2.     def setUp(self):  
  3.         self.client = Client()  
  4.    
  5.     def test_index(self):  
  6.         """ should get index """ 
  7.         response = self.client.get(reverse('users:index'))  
  8.         self.assertEqual(response.status_code, 200)  
  9.         self.assertIsNotNone(response.context['users']) 

首先我們必須在測試設(shè)置時(shí)初始化 Client。 test_index的***行模擬了向 Users控制器的 index動作申請了一個(gè) GET請求。 reverse查找對應(yīng)index動作的URL。注意代碼是如此冗余并且沒有類似于 assert_response :success的幫助方法。 response.context包含我們傳遞給視圖的變量。

很顯然Rails的magic是相當(dāng)有幫助的。Rails/Ruby同時(shí)也擁有很多第三方app,比如factory_girl,RSpecMocha,Cucumber,這使得編寫測試是一種樂趣。

工具和其他特征

依賴性管理(Dependency management)

兩種框架都有出色的依賴性管理工具。Rails使用 Bundler 來讀取Gemfile文件并跟蹤文件中對應(yīng)ruby應(yīng)用程序運(yùn)行所依賴的gem。

  1. gem 'nokogiri'   
  2. gem 'rails''3.0.0.beta3'   
  3. gem 'rack''>=1.0'   
  4. gem 'thin''~>1.1' 

簡單的在Gemfile文件中添加一個(gè)新行即可完成增加依賴( Dependency)。通過簡單調(diào)用如下命令即可安裝所有需要的gem:

  1. bundle install 

Django強(qiáng)烈推薦使用 virtualenv 來隔離Python環(huán)境。 pip 則用來管理python包。單獨(dú)的python包的安裝可以通過以下命令完成:

  1. pip install django-debug-toolbar 

而項(xiàng)目依賴文件可以通過以下集中起來:

  1. pip freeze > requirements.txt 

管理命令

基本上每個(gè)項(xiàng)目完成時(shí)都有相同的管理工作要做,比如預(yù)編譯文件(precompiling assets),清理記錄(log)等等。Rails使用Rake來管理這些任務(wù)。Rake非常靈活而且將開發(fā)任務(wù)變得簡單,特別是依賴于其他任務(wù)的。

  1. desc "吃掉食物。在吃之前需要烹飪(Cooks)和設(shè)置表格(table)。" 
  2. task eat: [:cook, :set_the_table] do  
  3.   # 在吃掉美味的食物之前, :cook和:set_the_table需要做完  
  4.   # 吃的哪部分代碼可以寫在這里  
  5. end 

Rake的任務(wù)可以有前提條件(prerequisite)。上面稱為 eat的任務(wù),在執(zhí)行之前必須運(yùn)行任務(wù) cook和任務(wù) set_the_table。Rake也支持命名空間(namespace),能將相同的任務(wù)結(jié)合成組(group)來完成。執(zhí)行任務(wù)只需簡單的調(diào)用任務(wù)的名稱:

  1. rake eat 

Django管理命令就沒那么靈活而且不支持前提條件和命名空間。雖然任務(wù)最終也會完成,但不是很出色。

  1. class Command(BaseCommand):  
  2.     help = '吃掉食物' 
  3.    
  4.     def handle(self, *args, **options):  
  5.         call_command('cook'# 這里是如何在代碼中調(diào)用管理命令  
  6.         set_the_table() # 但子任務(wù)需要是常規(guī)的python函數(shù)  
  7.         # 這里是吃的那些代碼 

如果我們將上面內(nèi)容保存到eat.py中,我們可以如下調(diào)用它:

  1. python manage.py eat 

國際化和本地化

在Rails中國際化有些偏弱。在文件夾config/locales,翻譯字符串在文件中作為ruby哈希來定義。

  1. # config/locales/en.yml  
  2. en: # the language identifier  
  3.   greet_username: "Hello, %{user}!" # translation_key: "value"  
  4.    
  5. # config/locales/pt.yml  
  6. pt:  
  7.   greet_username: "Olá, %{user}!" 

通過函數(shù)t進(jìn)行翻譯。函數(shù)***個(gè)變量是決定哪個(gè)字符串需要使用的key(比如greet_username)。Rails會自動選擇正確的語言。

  1. t('greet_username', user: "Bill"# Hi, Bill or Olá, Bill 

 

我發(fā)現(xiàn)處理本地語言文件中key名字和手動注冊這些內(nèi)容很繁瑣。Django將其打包進(jìn)非常便捷的gettext。翻譯也是通過一個(gè)幫助函數(shù)完成(ugettext),但是這次的key是不需要翻譯的字符串本身。

  1. ugettext('Hi, %(user)s.') % {'user''Bill'# Hi, Bill 或者 Olá, Bill 

 

Django會檢查所有的源代碼并調(diào)用以下命令自動收集將要翻譯的字符串:

  1. django-admin.py makemessages -a 

 

上面的命令執(zhí)行后會為每一種你想要的翻譯的語言生成一個(gè)文件。文件內(nèi)容可能像這樣:

  1. # locale/pt_BR/LC_MESSAGES/django.po  
  2. msgid "Hi, %(user)s." # key  
  3. msgstr "Olá, %(user)s" # 值 (翻譯) 

 

注意到我已經(jīng)在msgstr填充了翻譯內(nèi)容(那些本來是空的)。一旦翻譯完成,必須對它們進(jìn)行編譯。

  1. django-admin.py compilemessages 

 

這種本地化項(xiàng)目的方法實(shí)際上更實(shí)用,因?yàn)椴恍枰紤]key的名字,在需要的時(shí)候也不需要現(xiàn)查找。

【譯注】即無需自定義key,django會將整句話作為key值代入

用戶授權(quán)

不得不說當(dāng)我知道RoR(Ruby on Rails)沒有打包任何形式的用戶授權(quán)時(shí)多少有點(diǎn)震驚。我想不出任何不需要授權(quán)和用戶管理的項(xiàng)目。在這方面***的gem是devise,毫無疑問也是Rails上***的,在Github上有Rails一半的得分。

盡管Django從最開始就將授權(quán)框架打包進(jìn)來,但直到一年之前這種授權(quán)方式的靈活性才有所改善,就是當(dāng)版本1.5發(fā)布并帶來可配置的用戶模型(user model)。之前,你會被強(qiáng)制要求使用Django的方式定義用戶,而不能任意更改字段或者添加字段(field)。如今這不再是問題,你可以用自己定義的用戶模型代替原有模型

第三方庫

這里沒什么好說的。這篇文章里已經(jīng)提到了很多二者可使用的第三方庫,而且都擁有太多的app。Django Packages是個(gè)非常好的網(wǎng)站,可以用來搜索Django的App。不過還未發(fā)現(xiàn)Rails有類似的網(wǎng)站。

社區(qū)

雖然我沒有更具體的數(shù)據(jù)來證明,但我相當(dāng)確定Rails的社區(qū)更大一些。在Github上RoR擁有Django兩倍的得分。在Stackoverflow上標(biāo)記為Rails的問題也有兩倍之多。而且似乎RoR比Django有更多的工作(在Stackoverflow職業(yè)中24158)。Rails很龐大而且有非常多迷人的資源來供學(xué)習(xí),比如Rails CastsRails for Zombies。Django擁有Getting Started with Django但是沒有可比性。我知道Django使用The Django Book,但是已經(jīng)落后若干年了。不要以為我說錯(cuò)了,盡管有很多Django團(tuán)體而且如果你遇到問題,你很容易通過google找到答案,但Django就是沒有Rails龐大。
 

結(jié)論

Ruby on Rails和Django在網(wǎng)絡(luò)開發(fā)方面都是非常出色的框架。在開發(fā)模塊化的簡潔的代碼,在減少開發(fā)時(shí)間。我已經(jīng)離不開ORM框架,模板引擎和會話管理系統(tǒng)。那么問題是我如何選擇呢?

選擇任何一個(gè)都不會錯(cuò)。我的建議通常就是兩者都使用并選出你最合適的。最終的決定會取決于你傾向于哪種語言或者哪種原則:慣例優(yōu)先原則(convention-over-configuration,CoC)還是顯式優(yōu)先隱式原則(explicit is better than implicit)。使用CoC可以自動加載(import),控制器實(shí)例會自動傳遞給視圖以及便捷的編寫測試。使用顯式優(yōu)先隱式,會明確知道代碼在做什么,即使對那些不熟悉框架的人。

從我個(gè)人經(jīng)驗(yàn)來看我更喜歡Django。我喜歡Python的明確(explicitness),喜歡Django的表單以及此框架更有防御性(有限的模板語言,在model字段中null默認(rèn)不可用)。但我也知道更多人離開了Rails的魔法和它優(yōu)秀的測試環(huán)境是沒法活的。

責(zé)任編輯:林師授 來源: 開源中國社區(qū)編譯
相關(guān)推薦

2017-06-05 09:35:39

2019-03-06 09:55:54

Python 開發(fā)編程語言

2017-05-17 13:21:36

深度學(xué)習(xí)機(jī)器學(xué)習(xí)算法

2024-10-07 08:59:47

sleepwait線程

2016-10-17 15:04:02

開源DistributedApache Kafk

2024-02-19 10:25:34

深度學(xué)習(xí)人工智能AI

2009-07-17 13:13:47

iBATIS Hibe

2024-12-20 16:49:15

MyBatis開發(fā)代碼

2021-08-04 12:11:40

GitHub開發(fā)社區(qū)FSF

2023-05-14 22:00:01

2009-07-01 10:30:00

Ruby on Rai

2018-02-07 09:05:27

區(qū)塊鏈特性優(yōu)勢

2012-05-09 10:32:28

HTML5

2011-07-29 09:24:14

2009-12-16 17:37:31

Ruby on Rai

2021-05-10 14:54:42

區(qū)塊鏈經(jīng)濟(jì)技術(shù)

2009-07-28 14:30:35

ASP.NET JSP

2018-03-09 12:02:22

多云取舍安全

2020-12-07 19:00:29

Rails

2018-05-02 10:17:39

HPC深度學(xué)習(xí)容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號