在開發部落格系統或者是電商系統,總之,凡是帶有回覆、評論、回帖之類針對一個內容進行回覆的功能的時候,現在幾乎全部都是採用ajax非同步提交,並且同步在正確的位置顯示提交結果,而不是在提交之後重新重新整理整個頁面來顯示提交之後的內容。
ajax由於google的使用,被廣為推廣。它在客戶端使用javascript語言編寫,使用XMLHttpRequest物件,實現和服務的資料互動,詳細資訊參看:AJAX – XMLHttpRequest 物件。
ajax提交或者獲取資料,常用資料形式包括:
- 普通文字,自己定義規則,自己解析,無成熟類庫,需要自己編寫。
- xml文字,使用xml解析器解析,有成熟類庫。
- json文字,流行的資料形式,以體積小、靈活而著稱,有成熟類庫。
ajax有幾個好處:
- 防止頁面重新整理,因為提交的只是一部分內容,頁面大部分內容沒有發生改變,如果還是重新整理進而重新獲取全部的話,對於伺服器和客戶端都是一種負擔。而且頁面重新整理還會產生其他問題,諸如重複提交之類的。
- 減輕伺服器負擔,可以為更多人提供服務。
也有一些缺點:
- 給程式設計和除錯帶來一些小麻煩,不夠使用習慣了,還是有一些方法和竅門的。
- 由於是ajax,所以頁面地址沒有變化,所以如果想分享地址的話,難以實現。需要做額外的工作,例如把地址列印出來,讓使用者複製分享。
- 使用者不容易知道自己的提交正在進行,因為頁面沒有重新整理,使用者可能會重新點選提交按鈕。這也可以解決,提交過程給出明顯提示,然後灰掉提交按鈕,或者做一些防止重複提交的工作。
今天的例子是部落格評論的非同步提交,環境是python2.7+django1.2+SAE。
下面是後臺的處理程式碼,view中的程式碼
- @csrf_exempt
- def comment_new(request,blog_id):
- blog=get_object_or_404(Blog,pk=blog_id)
- #str(request.raw_post_data)
- if request.method=="POST" and request.is_ajax():
- title=request.POST[`comment_title`]
- name=request.POST[`comment_author_name`]
- email=request.POST[`comment_author_email`]
- url=request.POST[`comment_author_url`]
- content=request.POST[`comment_content`]
- comment=Comment(title=title,author_name=name,author_email=email,author_url=url,content=content);
- comment.blog=blog
- comment=comment.save()
- _dict={}
- _dict["title"]=title
- _dict["author_name"]=name
- _dict["author_email"]=email
- _dict["author_url"]=url
- _dict["content"]=content
- _dict["created_at"]=unicode(datetime.datetime.now())
- return HttpResponse(simplejson.JSONEncoder().encode(str(_dict)))
- def obj2dict(obj):
- """
- summary:
- 將object轉換成dict型別
- """
- memberlist = [m for m in dir(obj)]
- _dict = {}
- for m in memberlist:
- if m[0] != "_" and not callable(m):
- _dict[m] = getattr(obj,m)
- return _dict
下面是前段頁面的程式碼
- <script type="text/javascript" src="/site_static/js/tiny_mce_jquery/tiny_mce.js"></script>
- <script type="text/javascript" src="/site_static/js/textareas_simple.js"></script>
- {% if comments %}
- <p>
- <h2>Comments:</h2>
- <div id="blog_comments">
- {% for comment in comments %}
- <p>
- <a href="{{ comment.author_url }}">{{ comment.author_name }}</a> Said at {{ comment.created_at }}
- </p>
- <p>
- <h3>Title:</h3>
- {{ comment.title }}
- </p>
- <p>
- <h3>Content:</h3>
- {% autoescape off %}
- {{ comment.content }}
- {% endautoescape %}
- </p>
- <hr>
- {% endfor %}
- </div>
- </p>
- {% else %}
- <h2>you are the first comment man, please!</h2>
- {% endif %}
- <p>
- <h2>New Comment:</h2>
- <script type="text/javascript">
- $(document).ready(function(){
- $("#comment_form").submit(function(e){
- var data=$(`#comment_form`).serialize();
- $.ajax({
- url:"/blog/{{ blog.id }}/comment/new/",
- type:"POST",
- contentType:"application/json; charset=utf-8",
- dataType:"json",
- data:data,
- success:function(data, textStatus, jqXHR){
- //alert(typeof(data));
- datadata=data.replace(/`/gi,""");
- datadata=data.replace(/u"/gi,""");
- //alert(data);
- var obj=$.parseJSON(data);
- //alert(obj.content);
- $("#blog_comments").append("<p><a href=""+obj.author_url+"">"+obj.author_name+"</a> Said at "+obj.created_at+"</p><p>"+obj.title+"</p><p>"+obj.content+"</p><hr>");
- $("#comment_form")[0].reset();
- },
- error:function(jqXHR, textStatus, errorThrown){
- alert(textStatus);
- alert(errorThrown);
- }
- });
- return false;
- });
- });
- </script>
- <form method="post" action="" id="comment_form">
- {% csrf_token %}
- {% comment %}
- {% for field in comment_form %}
- <p>
- {{ field.errors }}
- {{ field.label_tag }}:{{ field }}
- </p>
- {% endfor %}
- {% endcomment %}
- <p>
- <label for="comment_title">{{ comment_form.title.label }}</label>:
- <input type="text" id="comment_title" name="comment_title" placeholder="Title"/>
- {{ comment_form.title.help_text }}
- </p>
- <p>
- <label for="comment_author_name">{{ comment_form.author_name.label }}</label>:
- <input type="text" id="comment_author_name" name="comment_author_name" placeholder="author name"/>
- {{ comment_form.author_name.help_text }}
- </p>
- <p>
- <label for="comment_author_email">{{ comment_form.author_email.label }}</label>:
- <input type="text" id="comment_author_email" name="comment_author_email" placeholder="author email"/>
- {{ comment_form.author_email.help_text }}
- </p>
- <p>
- <label for="comment_author_url">{{ comment_form.author_url.label }}</label>:
- <input type="text" id="comment_author_url" name="comment_author_url" placeholder="author url"/>
- {{ comment_form.author_url.help_text }}
- </p>
- <p>
- <label for="comment_content">{{ comment_form.content.label }}</label>:
- <textarea rows="10" cols="50" placeholder="content" id="comment_content" name="comment_content"></textarea>
- {{ comment_form.content.help_text }}
- </p>
- <input type="submit" value="Save" />
- </form>
- </p>
在客戶端用到了三個類庫,分別是jquery-1.7.1.min.js,jquery.json-2.3.min.js和jquery.placeholder.min.js。
jquery是一個javascript類庫,封裝了javascript的很多操作,使用方便。
jquery.json是jquery的一個擴充套件,用來處理json文字和object之間的轉換。
jquery.placeholder是jquery的一個擴充套件,用來實現在輸入框沒有內容的時候,新增一些提示資訊。在輸入框沒有內容的時候,顯示一些內容,提示你應該輸入什麼,輸入的格式是什麼。【placeholder是html5支援的一個屬性,專門用來做提示的。】
- datadata=data.replace(/`/gi,""");
- datadata=data.replace(/u"/gi,""");
前段頁面的上面這兩句需要大家注意,第一句用正規表示式替換【單引號】為【雙引號】,第二句替換【u+雙引號】為【雙引號】。這都是為了後面使用
$.parseJSON(data);
做準備的。因為python後臺返回的json字串,也就是data的值是下面的格式
- {`title`:u`blog1`,`author`:u`andyshi`}
字串中有單引號和u,這都需要替換,jquery解析的json必須是標準格式的,就是雙引號,而且不能包含其他內容,所以我進行了替換,然後才可以使用jquery.json進行解析。
u是因為python後臺返回的是unicode字串的緣故。應該有辦法解決,待我再研究一下,稍後補充上來。
補充:
之前ajax返回的資料還需要處理單引號和字母u的問題,現在修正一下。
首先將前端的ajax程式碼變更為
- $.ajax({
- url:"/blog/{{ blog.id }}/comment/new/",
- type:"POST",
- contentType:"application/json; charset=utf-8",
- data:data,
- success:function(data, textStatus, jqXHR){
- var obj=$.parseJSON(data);
- $("#blog_comments").append("<p><a href=""+obj.author_url+"">"+obj.author_name+"</a> Said at "+obj.created_at+"</p><p>"+obj.title+"</p><p>"+obj.content+"</p><hr>");
- $("#comment_form")[0].reset();
- },
- error:function(jqXHR, textStatus, errorThrown){
- alert(textStatus);
- alert(errorThrown);
- }
- });
很明顯,就是去掉了ajax引數dataType,返回的結果變成在服務端組織好的json字串,然後用jquery.json解析成物件。順便裡面的兩次替換就可以去掉了。
python後臺的view程式碼變更為
- @csrf_exempt
- #@csrf_protect
- def comment_new(request,blog_id):
- blog=get_object_or_404(Blog,pk=blog_id)
- #str(request.raw_post_data)
- if request.method=="POST" and request.is_ajax():
- title=request.POST[`comment_title`]
- name=request.POST[`comment_author_name`]
- email=request.POST[`comment_author_email`]
- url=request.POST[`comment_author_url`]
- content=request.POST[`comment_content`]
- comment=Comment(title=title,author_name=name,author_email=email,author_url=url,content=content);
- comment.blog=blog
- comment=comment.save()
- #return string
- return HttpResponse("{"title":"%s","author_name":"%s","author_email":"%s","author_url":"%s","content":"%s","created_at":"%s"}"
- % (title,name,email,url,content,unicode(datetime.datetime.now())))
直接返回我們自己構造的字串,構造的時候就使用雙引號拼接。這樣就給前臺省去了正則替換的操作。