在開發部落格系統或者是電商系統,總之,凡是帶有回覆、評論、回帖之類針對一個內容進行回覆的功能的時候,現在幾乎全部都是採用ajax非同步提交,並且同步在正確的位置顯示提交結果,而不是在提交之後重新重新整理整個頁面來顯示提交之後的內容。

ajax由於google的使用,被廣為推廣。它在客戶端使用javascript語言編寫,使用XMLHttpRequest物件,實現和服務的資料互動,詳細資訊參看:AJAX – XMLHttpRequest 物件

ajax提交或者獲取資料,常用資料形式包括:

  • 普通文字,自己定義規則,自己解析,無成熟類庫,需要自己編寫。
  • xml文字,使用xml解析器解析,有成熟類庫。
  • json文字,流行的資料形式,以體積小、靈活而著稱,有成熟類庫。

ajax有幾個好處:

  • 防止頁面重新整理,因為提交的只是一部分內容,頁面大部分內容沒有發生改變,如果還是重新整理進而重新獲取全部的話,對於伺服器和客戶端都是一種負擔。而且頁面重新整理還會產生其他問題,諸如重複提交之類的。
  • 減輕伺服器負擔,可以為更多人提供服務。

也有一些缺點:

  • 給程式設計和除錯帶來一些小麻煩,不夠使用習慣了,還是有一些方法和竅門的。
  • 由於是ajax,所以頁面地址沒有變化,所以如果想分享地址的話,難以實現。需要做額外的工作,例如把地址列印出來,讓使用者複製分享。
  • 使用者不容易知道自己的提交正在進行,因為頁面沒有重新整理,使用者可能會重新點選提交按鈕。這也可以解決,提交過程給出明顯提示,然後灰掉提交按鈕,或者做一些防止重複提交的工作。

 

今天的例子是部落格評論的非同步提交,環境是python2.7+django1.2+SAE。

下面是後臺的處理程式碼,view中的程式碼

 

  1.                       
  2. @csrf_exempt                  
  3. def comment_new(request,blog_id): 
  4.     blog=get_object_or_404(Blog,pk=blog_id) 
  5.      
  6.     #str(request.raw_post_data) 
  7.     if request.method=="POST" and request.is_ajax(): 
  8.         title=request.POST[`comment_title`
  9.         name=request.POST[`comment_author_name`
  10.         email=request.POST[`comment_author_email`
  11.         url=request.POST[`comment_author_url`
  12.         content=request.POST[`comment_content`
  13.         comment=Comment(title=title,author_name=name,author_email=email,author_url=url,content=content); 
  14.         comment.blog=blog 
  15.         comment=comment.save() 
  16.  
  17.         _dict={} 
  18.         _dict["title"]=title 
  19.         _dict["author_name"]=name 
  20.         _dict["author_email"]=email 
  21.         _dict["author_url"]=url 
  22.         _dict["content"]=content 
  23.           _dict["created_at"]=unicode(datetime.datetime.now()) 
  24.      
  25.      
  26.     return HttpResponse(simplejson.JSONEncoder().encode(str(_dict))) 
  27.  
  28.  
  29. def obj2dict(obj): 
  30.     """ 
  31.     summary: 
  32.         將object轉換成dict型別     
  33.     """ 
  34.     memberlist = [m for m in dir(obj)] 
  35.     _dict = {} 
  36.     for m in memberlist: 
  37.         if m[0] != "_" and not callable(m): 
  38.             _dict[m] = getattr(obj,m) 
  39.  
  40.     return _dict 

 

下面是前段頁面的程式碼

 

  1. <script type="text/javascript" src="/site_static/js/tiny_mce_jquery/tiny_mce.js"></script> 
  2. <script type="text/javascript" src="/site_static/js/textareas_simple.js"></script> 
  3.  
  4. {% if comments %} 
  5. <p> 
  6. <h2>Comments:</h2> 
  7. <div id="blog_comments"> 
  8. {% for comment in comments %} 
  9. <p> 
  10. <a href="{{ comment.author_url }}">{{ comment.author_name }}</a>&nbsp;&nbsp;Said at {{ comment.created_at }} 
  11. </p> 
  12. <p> 
  13. <h3>Title:</h3> 
  14. {{ comment.title }} 
  15. </p> 
  16. <p> 
  17.     <h3>Content:</h3> 
  18.     {% autoescape off %} 
  19.     {{ comment.content }} 
  20.     {% endautoescape %} 
  21.  
  22. </p> 
  23. <hr> 
  24. {% endfor %} 
  25.  
  26. </div> 
  27.  
  28. </p> 
  29. {% else %} 
  30. <h2>you are the first comment man, please!</h2> 
  31. {% endif %} 
  32. <p> 
  33. <h2>New Comment:</h2> 
  34. <script type="text/javascript"> 
  35.     $(document).ready(function(){ 
  36.         $("#comment_form").submit(function(e){ 
  37.                      
  38.             var data=$(`#comment_form`).serialize(); 
  39.              
  40.             $.ajax({ 
  41.                 url:"/blog/{{ blog.id }}/comment/new/", 
  42.                 type:"POST", 
  43.                 contentType:"application/json; charset=utf-8", 
  44.                 dataType:"json", 
  45.                 data:data, 
  46.                 success:function(data, textStatus, jqXHR){ 
  47.                     //alert(typeof(data)); 
  48.                     datadata=data.replace(/`/gi,"""); 
  49.                     datadata=data.replace(/u"/gi,"""); 
  50.                     //alert(data); 
  51.                     var obj=$.parseJSON(data); 
  52.                     //alert(obj.content); 
  53.                     $("#blog_comments").append("<p><a href=""+obj.author_url+"">"+obj.author_name+"</a>&nbsp;&nbsp;Said at "+obj.created_at+"</p><p>"+obj.title+"</p><p>"+obj.content+"</p><hr>"); 
  54.                      
  55.                     $("#comment_form")[0].reset(); 
  56.                 }, 
  57.                 error:function(jqXHR, textStatus, errorThrown){ 
  58.                     alert(textStatus); 
  59.                     alert(errorThrown); 
  60.                 } 
  61.             });      
  62.              
  63.             return false; 
  64.  
  65.         }); 
  66.     }); 
  67.      
  68. </script> 
  69. <form method="post" action="" id="comment_form"> 
  70.  
  71.     {% csrf_token %} 
  72.      
  73.     {% comment %} 
  74.     {% for field in comment_form %} 
  75.     <p> 
  76.         {{ field.errors }} 
  77.         {{ field.label_tag }}:{{ field }} 
  78.     </p> 
  79.     {% endfor %} 
  80.          
  81.     {% endcomment %} 
  82.  
  83.     <p> 
  84.     <label for="comment_title">{{ comment_form.title.label }}</label>
  85.     <input type="text" id="comment_title" name="comment_title"  placeholder="Title"/> 
  86.     {{ comment_form.title.help_text }} 
  87.     </p> 
  88.      
  89.     <p> 
  90.     <label for="comment_author_name">{{ comment_form.author_name.label }}</label>
  91.     <input type="text" id="comment_author_name" name="comment_author_name" placeholder="author name"/> 
  92.     {{ comment_form.author_name.help_text }} 
  93.     </p> 
  94.      
  95.     <p> 
  96.     <label for="comment_author_email">{{ comment_form.author_email.label }}</label>
  97.     <input type="text" id="comment_author_email" name="comment_author_email"  placeholder="author email"/> 
  98.     {{ comment_form.author_email.help_text }} 
  99.     </p> 
  100.      
  101.     <p> 
  102.     <label for="comment_author_url">{{ comment_form.author_url.label }}</label>
  103.     <input type="text" id="comment_author_url" name="comment_author_url"  placeholder="author url"/> 
  104.     {{ comment_form.author_url.help_text }} 
  105.     </p> 
  106.      
  107.     <p> 
  108.     <label for="comment_content">{{ comment_form.content.label }}</label>
  109.     <textarea rows="10" cols="50" placeholder="content" id="comment_content" name="comment_content"></textarea> 
  110.      
  111.     {{ comment_form.content.help_text }} 
  112.     </p> 
  113.      
  114.  
  115.  
  116.  
  117.      
  118.      
  119.     <input type="submit" value="Save" /> 
  120. </form> 
  121.  
  122. </p> 

在客戶端用到了三個類庫,分別是jquery-1.7.1.min.jsjquery.json-2.3.min.jsjquery.placeholder.min.js

jquery是一個javascript類庫,封裝了javascript的很多操作,使用方便。

jquery.json是jquery的一個擴充套件,用來處理json文字和object之間的轉換。

jquery.placeholder是jquery的一個擴充套件,用來實現在輸入框沒有內容的時候,新增一些提示資訊。在輸入框沒有內容的時候,顯示一些內容,提示你應該輸入什麼,輸入的格式是什麼。【placeholder是html5支援的一個屬性,專門用來做提示的。】

 

  1. datadata=data.replace(/`/gi,""");  
  2. datadata=data.replace(/u"/gi,""");  

前段頁面的上面這兩句需要大家注意,第一句用正規表示式替換【單引號】為【雙引號】,第二句替換【u+雙引號】為【雙引號】。這都是為了後面使用

$.parseJSON(data); 

做準備的。因為python後臺返回的json字串,也就是data的值是下面的格式

 

  1. {`title`:u`blog1`,`author`:u`andyshi`

字串中有單引號和u,這都需要替換,jquery解析的json必須是標準格式的,就是雙引號,而且不能包含其他內容,所以我進行了替換,然後才可以使用jquery.json進行解析。

u是因為python後臺返回的是unicode字串的緣故。應該有辦法解決,待我再研究一下,稍後補充上來。

 

補充:

之前ajax返回的資料還需要處理單引號和字母u的問題,現在修正一下。

首先將前端的ajax程式碼變更為

 

  1. $.ajax({ 
  2.                 url:"/blog/{{ blog.id }}/comment/new/"
  3.                 type:"POST"
  4.                 contentType:"application/json; charset=utf-8"
  5.                  
  6.                 data:data, 
  7.                 success:function(data, textStatus, jqXHR){ 
  8.  
  9.                     var obj=$.parseJSON(data); 
  10.                      
  11.                     $("#blog_comments").append("<p><a href=""+obj.author_url+"">"+obj.author_name+"</a>&nbsp;&nbsp;Said at "+obj.created_at+"</p><p>"+obj.title+"</p><p>"+obj.content+"</p><hr>"); 
  12.                     $("#comment_form")[0].reset(); 
  13.                 }, 
  14.                 error:function(jqXHR, textStatus, errorThrown){ 
  15.                     alert(textStatus); 
  16.                     alert(errorThrown); 
  17.                 } 
  18.             });  

很明顯,就是去掉了ajax引數dataType,返回的結果變成在服務端組織好的json字串,然後用jquery.json解析成物件。順便裡面的兩次替換就可以去掉了。

python後臺的view程式碼變更為

 

  1. @csrf_exempt                  
  2. #@csrf_protect 
  3. def comment_new(request,blog_id): 
  4.     blog=get_object_or_404(Blog,pk=blog_id) 
  5.      
  6.     #str(request.raw_post_data) 
  7.     if request.method=="POST" and request.is_ajax(): 
  8.         title=request.POST[`comment_title`
  9.         name=request.POST[`comment_author_name`
  10.         email=request.POST[`comment_author_email`
  11.         url=request.POST[`comment_author_url`
  12.         content=request.POST[`comment_content`
  13.         comment=Comment(title=title,author_name=name,author_email=email,author_url=url,content=content); 
  14.         comment.blog=blog 
  15.         comment=comment.save() 
  16.          
  17.     #return string 
  18.     return HttpResponse("{"title":"%s","author_name":"%s","author_email":"%s","author_url":"%s","content":"%s","created_at":"%s"}"  
  19.         % (title,name,email,url,content,unicode(datetime.datetime.now()))) 

直接返回我們自己構造的字串,構造的時候就使用雙引號拼接。這樣就給前臺省去了正則替換的操作。