文章评论包含两种评论,根评论:对文章的评论;子评论:对评论的评论。二者的区别在因而否存在父评论。javascript
实现流程:一、构建样式;二、提交根评论;三、显示根评论(分为render显示和Ajax显示);四、提交子评论;五、显示子评论(分为render显示和Ajax显示);六、评论树显示(博客园是楼层显示)。css
一、article_detail.html:html
{# 文章点赞,清除浮动 #} <div class="clearfix"> <div id="div_digg"> {# 推荐 #} <div class="diggit action"> <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> </div> {# 点灭 #} <div class="buryit action"> <span class="diggnum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red;"></div> </div> </div> <div class="comments"> <p>发表评论</p> <p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容</p> <textarea name="" id="" cols="60" rows="10"></textarea> {# textarea是一个内联标签 #} <p><button class="btn btn-default comment_btn">提交评论</button></p> </div>
因为id=div_digg中存在浮动,显示时会形成点赞区域和评论区域在同一行,在点赞区外包了一层div标签,添加bootstrap的clearfix清除浮动。java
因为textarea是一个内联标签,下面直接接button标签,文本框和按钮会显示在同一行,一样给按钮包一层p标签,让按钮在文本框下方显示。python
二、article_detail.css评论区样式调整git
/* 评论 */ input.author { background-image: url("/static/font/icon_form.gif"); background-repeat: no-repeat; border: 1px solid #ccc; padding: 4px 4px 4px 30px; width: 300px; font-size: 13px; background-position: 3px -3px; }
三、显示效果以下所示:ajax
一、建立评论路由数据库
urlpatterns = [ ... path('digg/', views.digg), # 点赞 path('comment/', views.comment), # 评论 ... ]
二、建立评论视图函数commentdjango
def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) return HttpResponse("comment")
三、在article_detail.html模板中建立评论事件json
<script> // 点赞请求 $('#div_digg .action').click(function () { ... // 评论请求 $(".comment_btn").click(function () { var content = $('#comment_content').val(); // 拿到评论框的内容 var pid = ""; // 父评论默认为空 $.ajax({ url: "/comment/", type: "post", data: { 'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(), 'article_id': "{{ article_obj.pk }}", 'content': content, 'pid': pid, }, success:function (data) { console.log(data); // 提交后清空评论框 $("#comment_content").val(""); } }) }) </script>
四、提交评论后,在数据库blog_comment表中能够看到评论记录
def article_detail(request, username, article_id): user = UserInfo.objects.filter(username=username).first() blog = user.blog article_obj = models.Article.objects.filter(pk=article_id).first() comment_list = models.Comment.objects.filter(article_id=article_id) return render(request, "article_detail.html", locals())
传递comment_list到模板中,根据过滤条件获取的是当前文章的评论。
{# 文章评论列表 #} <div class="comments"> <p>评论列表</p> <ul class="list-group comment_list"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href=""># {{ forloop.counter }}楼</a> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href=""><span><{{ comment.user.username }}/span></a> <a href="" class="pull-right">回复</a> </div> <div class="comment_con"> {# 评论内容 #} <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>发表评论</p> <p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容</p> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一个内联标签 #} <p><button class="btn btn-default comment_btn">提交评论</button></p> </div>
1)利用bootstrap的列表组组件来构建文章列表样式。
2)修饰评论内容样式article_detail.css:
.comment_con { margin-top: 10px; }
def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) response = {} # create_time是一个datetime.datetime对象,在json序列化时不能对对象进行json序列化,必须进行strftime的转换 response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") # 评论人 response["username"] = request.user.username # 内容 response["content"] = content return JsonResponse(response)
// 评论请求 $(".comment_btn").click(function () { var content = $('#comment_content').val(); // 拿到评论框的内容 var pid = ""; // 父评论默认为空 $.ajax({ url: "/comment/", type: "post", data: { 'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(), 'article_id': "{{ article_obj.pk }}", 'content': content, 'pid': pid, }, success: function (data) { console.log(data); // 获取视图函数返回的数据 var create_time = data.create_time; var username = data.username; var content = data.content; // ES6特性:字符串模板。 // ES6中容许使用反引号 ` 来建立字符串,此种方法建立的字符串里面能够包含由美圆符号加花括号包裹的变量${vraible}。 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span><${username}/span></a> <a href="" class="pull-right">回复</a> </div> <div class="comment_con"> {# 评论内容 #} <p>${content}</p> </div> </li>`; // DOM操做把标签字符串整个放入ul的标签中去 $("ul.comment_list").append(s); // 提交后清空评论框 $("#comment_content").val(""); } }) })
注意:1)ES6中容许使用反引号 ` 来建立字符串,此种方法建立的字符串里面能够包含由美圆符号加花括号包裹的变量${vraible}。
//产生一个随机数 var num=Math.random(); //将这个数字输出到console console.log(`your num is ${num}`);
2)获取视图函数返回的数据:
// 获取视图函数返回的数据 var create_time = data.create_time; var username = data.username; var content = data.content;
经过点击评论后的回复按钮来提交子评论,点击回复按钮事件:光标挪到输出框下;输出框显示 @用户名。
{# 文章评论列表 #} <div class="comments"> <p>评论列表</p> <ul class="list-group comment_list"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href=""># {{ forloop.counter }}楼</a> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href=""><span><{{ comment.user.username }}/span></a> <a class="pull-right reply_btn" username="{{ comment.user.username }}">回复</a> </div> <div class="comment_con"> {# 评论内容 #} <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>发表评论</p> <p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容</p> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一个内联标签 #} <p> <button class="btn btn-default comment_btn">提交评论</button> </p> </div>
给<a class="pull-right reply_btn" username="{{ comment.user.username }}">回复</a>,添加了属性username,拿到当前行评论的用户。
注意:回复这个a标签不能添加href=""属性。不然在触发回复事件后,会刷新当前页面。
// 回复按钮事件 $(".reply_btn").click(function () { $('#comment_content').focus(); // 获取焦点 // 拿到对应的父评论的用户名 var val = "@" + $(this).attr("username")+"\n"; // 给输入框赋值 $('#comment_content').val(val); });
显示效果以下所示:
article_detail.html作以下处理
<script> // 点赞请求 ... var pid = ""; // 父评论默认为空 // 评论请求 $(".comment_btn").click(function () { var content = $('#comment_content').val(); // 拿到评论框的内容 if (pid) { // pid有值,是子评论 // 处理拿到子评论值方法一: var index = content.indexOf("\n"); // 拿到换行符索引值 content = content.slice(index+1); // 切片处理,从index+1一直取到最后 } $.ajax({ url: "/comment/", type: "post", data: { 'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(), 'article_id': "{{ article_obj.pk }}", 'content': content, 'pid': pid, }, success: function (data) { console.log(data); // 获取视图函数返回的数据 var create_time = data.create_time; var username = data.username; var content = data.content; // ES6特性:字符串模板。 // ES6中容许使用反引号 ` 来建立字符串,此种方法建立的字符串里面能够包含由美圆符号加花括号包裹的变量${vraible}。 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span><${username}/span></a> <a href="" class="pull-right">回复</a> </div> <div class="comment_con"> {# 评论内容 #} <p>${content}</p> </div> </li>`; // DOM操做把标签字符串整个放入ul的标签中去 $("ul.comment_list").append(s); // 提交后清空评论框 $("#comment_content").val(""); // pid从新赋值 pid = ""; } }) }); // 回复按钮事件 $(".reply_btn").click(function () { $('#comment_content').focus(); // 获取焦点 // 拿到对应的父评论的用户名 var val = "@" + $(this).attr("username")+"\n"; // 给输入框赋值 $('#comment_content').val(val); // 拿到父评论的主键值 pid = $(this).attr("comment_pk"); }); </script>
注意:
(1)将var pid=""; 改成全局变量,拿到事件外。根据pid是否有值,处理评论框内容:
pid没有值的时候:
var content = $('#comment_content').val();
pid有值的时候:
if (pid) { // pid有值,是子评论 // 处理拿到子评论值方法一: var index = content.indexOf("\n"); // 拿到换行符索引值 content = content.slice(index+1); // 切片处理,从index+1一直取到最后 }
(2)给回复按钮这个a标签添加一个新的自定义属性:comment_pk。
<a class="pull-right reply_btn" username="{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回复</a>
点击回复按钮拿到父评论的主键值
// 拿到父评论的主键值 pid = $(this).attr("comment_pk");
(3)每次提交评论后,都要清空pid。这样发布子评论后,不刷新页面紧接着又发布评论,这个新评论就不会有父评论了,
(4)查看数据库中的blog_comment表,子评论的父评论id:
{# 文章评论列表 #} <div class="comments"> <p>评论列表</p> <ul class="list-group comment_list"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href=""># {{ forloop.counter }}楼</a> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href=""><span><{{ comment.user.username }}/span></a> <a class="pull-right reply_btn" username="{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回复</a> </div> {% if comment.parent_comment_id %} <div class="pid_info well"> <p> {# 拿到父评论对象评论人和评论内容 #} {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }} </p> </div> {% endif %} <div class="comment_con"> {# 评论内容 #} <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>发表评论</p> <p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容</p> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一个内联标签 #} <p> <button class="btn btn-default comment_btn">提交评论</button> </p> </div>
(1)在模板中判断评论对象是否有父评论,若是有显示父评论人和评论对象:
{% if comment.parent_comment_id %} <div class="pid_info well"> <p> {# 拿到父评论对象评论人和评论内容 #} {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }} </p> </div> {% endif %}
(2)bootstrap的well用在元素上,就能有嵌入(inset)的简单效果。
(3)显示效果:
(1)改写article.detail.html评论请求事件回调函数事件处理:
var pid = ""; // 父评论默认为空 // 评论请求 $(".comment_btn").click(function () { var content = $('#comment_content').val(); // 拿到评论框的内容 if (pid) { // pid有值,是子评论 // 处理拿到子评论值方法一: var index = content.indexOf("\n"); // 拿到换行符索引值 content = content.slice(index + 1); // 切片处理,从index+1一直取到最后 } $.ajax({ url: "/comment/", type: "post", data: { 'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(), 'article_id': "{{ article_obj.pk }}", 'content': content, 'pid': pid, }, success: function (data) { console.log(data); // 获取视图函数返回的数据 var create_time = data.create_time; var username = data.username; var content = data.content; var parent_username = data.parent_username; var parent_content = data.parent_content; if (pid) { // ES6特性:字符串模板。 // ES6中容许使用反引号 ` 来建立字符串,此种方法建立的字符串里面能够包含由美圆符号加花括号包裹的变量${vraible}。 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span><${username}/span></a> <a href="" class="pull-right">回复</a> </div> <div class="pid_info well"> <p> {# 拿到父评论对象评论人和评论内容 #} ${ parent_username }: ${ parent_content } </p> </div> <div class="comment_con"> {# 评论内容 #} <p>${content}</p> </div> </li>`; } else { var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span><${username}/span></a> <a href="" class="pull-right">回复</a> </div> <div class="comment_con"> {# 评论内容 #} <p>${content}</p> </div> </li>`; } // DOM操做把标签字符串整个放入ul的标签中去 $("ul.comment_list").append(s); // 提交后清空评论框 $("#comment_content").val(""); // pid从新赋值 pid = ""; } }) });
注意:根据pid是否有值对var s的标签字符串构建不一样的结构和样式。也就是针对根评论和子评论构建不一样的结构。因为构建子评论须要显示父评论的用户名和评论内容。所以须要在视图函数中返回响应的数据。
(2)在comment视图函数中添加父评论对象用户名和评论内容
def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) response = {} # create_time是一个datetime.datetime对象,在json序列化时不能对对象进行json序列化,必须进行strftime的转换 response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") # 评论人 response["username"] = request.user.username # 内容 response["content"] = content # 父评论对象评论人和评论内容 response["parent_username"] = comment_obj.parent_comment.user.username response["parent_content"] = comment_obj.parent_comment.content return JsonResponse(response)
(3)显示效果:
前面都是用的评论楼来展现评论列表,可是这种方式结构不够清晰。所以也须要学会用评论树来展现评论列表。
(1)构建article_detail.html中评论树标签和点击事件
{# 文章评论列表 #} <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> </div> <script> $(".tree_btn").click(function () { $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}", }, success: function (data) { console.log(data); } }) }) </script> <p>评论列表</p> ... </div>
(2)根据ajax的请求url创建对应的url
urlpatterns = [ path('admin/', admin.site.urls), ... path('comment/', views.comment), # 评论 path("get_comment_tree/", views.get_comment_tree), # 评论树 ... ]
(3)创建对应的视图函数get_comment_tree
def get_comment_tree(request): article_id = request.GET.get("article_id") # 过滤出文章对应的评论,挑出主键值、评论内容、父评论id,拿到的是一个queryset,结构相似一个列表里面装着一个个字典 # 可是queryset并非一个列表,能够用list()函数将其转换为列表 ret = list(models.Comment.objects.filter(article_id=article_id).values("pk", "content", "parent_comment_id")) # JsonResponse对非字典的数据进行序列化,必须设置一个参数safe=False return JsonResponse(ret, safe=False)
注意:QuerySet虽然结构相似一个列表里面装着一个个字典但并非一个列表。能够用list()函数转换为列表。
其次通常JsonResponse都是用来对字典进行序列化,若是要对一个非字典的数据序列化,必须设置一个参数safe=False,不然会报错。
(4)访问页面点击页面中评论树时,页面控制台输出了一条数组数据:
{# 文章评论列表 #} <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> </div> <script> $(".tree_btn").click(function () { $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}", }, success: function (data) { console.log(data); // data是一个列表,列表中包含一个个字典 $.each(data, function (index, comment_object) { var pk = comment_object.pk; var content = comment_object.content; var parent_comment_id = comment_object.parent_comment_id; var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>'; // 判断评论是根评论仍是子评论 if (!parent_comment_id) { // 感叹号取反 // 根评论 $(".comment_tree").append(s); } else { // 子评论 // 放入父评论的div标签 // 属性选择器,找到comment_id属性值对应的div $("[comment_id="+parent_comment_id+"]").append(s); } }) } }) }) </script> ... </div>
注意:
(1)变量s是将要插入模板中的标签字符串,标签字符串内定义的评论的内容。
<div class="comment_item" comment_id='pk' style="margin-left: 20px"> <span>评论内容</span> </div>
每一个标签字符串都定义了comment_id,对应评论对象的主键值。定义样式,则是为了子评论相对父评论向左移动20像素,样式获得错开。
(2)在ajax回调函数循环get_comment_tree视图函数返回列表
success: function (data) { console.log(data); // data是一个列表,列表中包含一个个字典 $.each(data, function (index, comment_object) { var pk = comment_object.pk; var content = comment_object.content; var parent_comment_id = comment_object.parent_comment_id; ... }) }
(3)若是判断是子评论的话,须要利用属性选择器,找到属性comment_id=父评论的id,这样筛选到对应的评论将标签字符串放如对应的div中。
var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>'; // 判断评论是根评论仍是子评论 if (!parent_comment_id) { // 感叹号取反 // 根评论 $(".comment_tree").append(s); } else { // 子评论 // 放入父评论的div标签 // 属性选择器,找到comment_id属性值对应的div $("[comment_id="+parent_comment_id+"]").append(s); }
(4)显示效果:
(1)在视图中获取的评论列表,会不会有子评论在前,父评论在后,致使在回调函数中循环列表时,发现有子评论没法找到父评论没法插入?
答:这个问题是不会发生的,由于评论生成是按主键进行排序的,根评论必定在前,与它关联的子评论必定在后。
为了保险在获取评论对象时仍是order_by作一下排序:
def get_comment_tree(request): article_id = request.GET.get("article_id") ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id")) return JsonResponse(ret, safe=False)
(2)再也不经过点击评论树标签,触发事件来显示评论树。直接访问文章页面就显示评论树?
答:再也不把ajax事件内嵌在click事件中,浏览器加载标签字符串时就会直接执行ajax,马上发新的请求拿到响应结果,因为速度很是快,马上就将评论树显示在页面上。
<script> // $(".tree_btn").click(function () { $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}", }, success: function (data) { console.log(data); // data是一个列表,列表中包含一个个字典 $.each(data, function (index, comment_object) { var pk = comment_object.pk; var content = comment_object.content; var parent_comment_id = comment_object.parent_comment_id; var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>'; // 判断评论是根评论仍是子评论 if (!parent_comment_id) { // 感叹号取反 // 根评论 $(".comment_tree").append(s); } else { // 子评论 // 放入父评论的div标签 // 属性选择器,找到comment_id属性值对应的div $("[comment_id="+parent_comment_id+"]").append(s); } }) } }) // }) </script>
好比在数据库生成一条评论对象,同时将文章的评论数进行更新。这两步操做须要设计为同进同退,即若是有一步没完成,完成的操做须要在数据库回退。
def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk # 事务操做:生成记录和评论数更新同进同退 with transaction.atomic(): # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) # 文章评论数更新 models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1) response = {} # create_time是一个datetime.datetime对象,在json序列化时不能对对象进行json序列化,必须进行strftime的转换 response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") # 评论人 response["username"] = request.user.username # 内容 response["content"] = content # 父评论对象评论人和评论内容 response["parent_username"] = comment_obj.parent_comment.user.username response["parent_content"] = comment_obj.parent_comment.content return JsonResponse(response)
from django.db import transaction
# 事务操做:生成记录和评论数更新同进同退 with transaction.atomic(): # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) # 文章评论数更新 models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)
文章被评论了,给做者发一份站内信通知他多了一条评论。这里使用django提供的模块来实现发送邮件:
from django.core.mail import send_mail
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None): """ :param subject: 标题 :param message: 邮件内容 :param from_email: 发件邮箱 :param recipient_list: 收件邮箱 :param fail_silently: :param auth_user: :param auth_password: :param connection: :param html_message: :return: """ connection = connection or get_connection( username=auth_user, password=auth_password, fail_silently=fail_silently, ) mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, connection=connection) if html_message: mail.attach_alternative(html_message, 'text/html') return mail.send()
# 邮箱配置 EMAIL_HOST = 'smtp.163.com' # 若是是 qq 改为 smtp.exmail.qq.com EMAIL_PORT = 465 # qqs是465 EMAIL_HOST_USER = 'xxx@163.com' # 账号 EMAIL_HOST_PASSWORD = 'xxxxxx' # 密码(受权码) # DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # 默认使用当前配置的user EMAIL_USE_SSL = True # 是否使用SSL证书, 网易邮箱关闭SSL后SMTP应该为25
def comment(request): ... # 为了发送邮件拿到文章对象 article_obj = models.Article.objects.filter(pk=article_id).first() ... # 发送邮件 from django.core.mail import send_mail from cnblog import settings send_mail( "您的文章%s新增了一条评论内容" % article_obj.title, content, settings.EMAIL_HOST_USER, # 发送方 ["44xxxx@qq.com"] # 接收方 ) ...
接收的邮箱,应该是用户注册时填写的邮箱信息。
评论后,个人qq邮箱收到邮件:
点击评论会发现,页面须要卡好久,严重影响了用户体验。
# 发送邮件 from django.core.mail import send_mail from cnblog import settings # send_mail( # "您的文章%s新增了一条评论内容" % article_obj.title, # content, # settings.EMAIL_HOST_USER, # 发送方 # ["44xxxx@qq.com"] # 接收方 # ) import threading t = threading.Thread(target=send_mail, args=( "您的文章%s新增了一条评论内容" % article_obj.title, content, settings.EMAIL_HOST_USER, # 发送方 ["443xxxx@qq.com"] # 接收方 )) t.start()
这样用一个线程去跑send_email,就不会影响响应结果反馈了。
def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk # 为了发送邮件拿到文章对象 article_obj = models.Article.objects.filter(pk=article_id).first() # 事务操做:生成记录和评论数更新同进同退 with transaction.atomic(): # 在数据库生成一条评论对象 父评论为空是根评论,不为空则是子评论 comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) # 文章评论数更新 models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1) response = {} # create_time是一个datetime.datetime对象,在json序列化时不能对对象进行json序列化,必须进行strftime的转换 response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") # 评论人 response["username"] = request.user.username # 内容 response["content"] = content # 发送邮件 from django.core.mail import send_mail from cnblog import settings # send_mail( # "您的文章%s新增了一条评论内容" % article_obj.title, # content, # settings.EMAIL_HOST_USER, # 发送方 # ["443614404@qq.com"] # 接收方 # ) import threading t = threading.Thread(target=send_mail, args=( "您的文章%s新增了一条评论内容" % article_obj.title, content, settings.EMAIL_HOST_USER, # 发送方 ["443614404@qq.com"] # 接收方 )) t.start() # 父评论对象评论人和评论内容 response["parent_username"] = comment_obj.parent_comment.user.username response["parent_content"] = comment_obj.parent_comment.content return JsonResponse(response)