在开发博客系统或者是电商系统,总之,凡是带有回复、评论、回帖之类针对一个内容进行回复的功能的时候,现在几乎全部都是采用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()))) 

直接返回我们自己构造的字符串,构造的时候就使用双引号拼接。这样就给前台省去了正则替换的操作。