BBs项目评论端口
整体总结:
- 在出现bug的时候,先判断是前端bug还是后端bug,再判断bug错误类型,以及报错信息
- 如果出现bug但是没报错,可以通过后端用print分段代码,前端用consloe方式分段
- 大部分功能逻辑其实都是类似,比如点赞和评论,views都写到了判断是否登录,判断是否是ajax请求等等,真正展示功能特性的代码其实就两三行,所以要分清主次
- 前端注意的就是标签的查找,绑定事件的使用,标签属性的修改,标签内容的修改,ajax请求,部分DOM操作,以上必须数量掌握
- 后端最容易出错的应该是连表和跨表查询的写法
一、文章详情展示
总结注意点:
- url顶替问题,注意url顺序和正则的优先级
- 多次用到同一块页面可以制作成inclusion_tag
- 模版的继承基本语法要熟练掌握
1 将侧边栏做成inclusion_tag
left_menu.html
<div class="panel panel-default">
<div class="panel-heading">文章标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }} ({{ tag.1 }})</a></p>
{% endfor %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title " >文章分类</h3>
</div>
<div class="panel-body">
{% for category in category_list %}
<p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})</a></p>
{% endfor %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title " >日期</h3>
</div>
<div class="panel-body">
{% for date in date_list %}
<p><a href="/{{ username }}/archive/{{ date.0|date:'Y-m' }}">{{ date.0 }} ({{ date.1 }})</a></p>
{% endfor %}
</div>
</div>
在项目中创建一个templatetags文件夹(固定名称)然后在该文件夹内创建一个任意名称的py文件
mytag.py
from django import template
from app01 import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
# 导入模块和注册是必写的代码
register = template.Library()
# 给这个函数添加一个渲染页面
@register.inclusion_tag('left_menu.html')
def left_menu(username):
user_obj = models.UserInfo.objects.filter(username=username).first()
blog = user_obj.blog
category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list(
'name', 'count_num', 'pk')
# 2 查询当前用户下所有的标签下的文章数
tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name',
'count_num',
'pk')
# 3 以创建日期为分类
date_list = models.Article.objects.filter(blog=blog).annotate(mouth=TruncMonth('create_time')).values(
'mouth').annotate(count_num=Count('pk')).values_list('mouth', 'count_num')
return locals()
展示文章详情的视图函数
views.py
通过前端点击返回给后端需要的数据,然后筛选得到具体的文章对象
def article_detail(request, username, article_id):
article_obj = models.Article.objects.filter(pk=article_id, blog__userinfo__username=username).first()
if not article_obj:
return render(request, 'errors.html')
comment_list = models.Comment.objects.filter(article=article_obj)
return render(request, 'article_detail.html', locals())
二、点赞点踩功能
总结注意点:
- if嵌套的时候尽量先把正确的逻辑写好再写错误的逻辑
- F查询对字段的修改
- 传给前端的数据如果是一段html代码,可以通过.html('')的方法去渲染
- 绑定点击事件的时候尽量绑定大一点的div,不然绑定太小了怎么错的都不知道
- 重点区分jquery的一些常用方法,记住使用规律
views.py
import json
from django.db.models import F
def up_or_down(request):
if request.is_ajax():
back_dic = {'code': 2000}
# 判断用户是否登录
if request.user.is_authenticated():
article_id = request.POST.get('article_id')
article_obj = models.Article.objects.filter(pk=article_id).first()
# 判断操作的是否是自己的文章
if article_obj.blog.userinfo.username != request.user:
# 判断用户是否给这个文章点赞过
is_used = models.UpAndDowm.objects.filter(user=request.user, article=article_obj)
if not is_used:
print(666)
# 在两张表内存储点赞点踩
is_up = request.POST.get('is_up')
is_up = json.loads(is_up)
back_dic['code'] = 1000
print(is_up)
# 点赞
if is_up:
back_dic['msg'] = '点赞成功!'
models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
else:
back_dic['msg'] = '点踩成功!'
print(back_dic)
models.Article.objects.filter(pk=article_id).update(dowm_num=F('dowm_num') + 1)
models.UpAndDowm.objects.create(user=request.user, article=article_obj, is_up=is_up)
else:
back_dic['msg'] = '不能给已操作过的文章点赞点踩!'
else:
back_dic['msg'] = '不能给自己的文章点赞点踩!'
else:
back_dic['msg'] = '请先<a href="/login/">登录</a>'
return JsonResponse(back_dic)
article_detail.html
{% block css %}
// 样式都是copy别人写好的,有能cv的就别自己写
<style>
#div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 128px;
text-align: center;
margin-top: 10px;
}
.diggit {
float: left;
width: 46px;
height: 52px;
background: url('/static/img/upup.gif') no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url('/static/img/downdown.gif') no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.clear {
clear: both;
}
</style>
{% endblock %}
{% block content %}
<h2>{{ article_obj.title }}</h2>
<div class="article-content">
{{ article_obj.content|safe }}
</div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count" up_or_down = 'true'>{{ article_obj.up_num }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count" up_or_down = 'false'>{{ article_obj.dowm_num }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color: red;"></div>
</div>
</div>
// 给所有的action类绑定事件
$('.action').click(function () {
{#alert($(this).hasClass('diggit'))#}
let isUp = $(this).hasClass('diggit');
let $div = $(this);
// 朝后端发送ajax请求
$.ajax({
url:'/up_or_down/',
type:'post',
data:{
'article_id':'{{ article_obj.pk }}',
'is_up':isUp,
'csrfmiddlewaretoken':'{{ csrf_token }}'
},
success:function (args) {
if(args.code == 1000){
$('#digg_tips').text(args.msg)
// 将前端的数字加一
// 先获取到之前的数字
let oldNum = $div.children().text(); // 文本 是字符类型
// 易错点
$div.children().text(Number(oldNum) + 1) // 字符串拼接了 1+1 = 11 11 + 1 = 111
}else{
$('#digg_tips').html(args.msg)
}
}
})
})
三、评论功能
总结注意点:
- 在一张表中有一个字段和表本身关联的时候,需要注意必须可以为空
- 有一些同时删除同时跟新的操作在没有外键关联的情况下可以使用事务
- 当我们在前端做一些对值的判断操作时,必须注意这个值是否需要清空
views.py
def comment(request):
if request.is_ajax():
back_dic = {'code': 2000}
if request.method == 'POST':
if request.user.is_authenticated():
article_id = request.POST.get('article_id')
content = request.POST.get('content')
parent_id = request.POST.get('parent_id')
with transaction.atomic():
models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
models.Comment.objects.create(user=request.user, article_id=article_id, content=content,
parent_id=parent_id)
back_dic['code'] = 1000
back_dic['msg'] = '评论成功'
print(back_dic)
return JsonResponse(back_dic)
article_detail.html
<div>
<ul class="list-group">
{% for comment in comment_list %}
<li class="list-group-item">
<span>#{{ forloop.counter }}楼</span>
<span>{{ comment.comment_time|date:'Y-m-d' }}</span>
<span>{{ comment.user.username }}</span>
<span><a class="pull-right reply" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}">回复</a></span>
<div>
{% if comment.parent_id %}
<p>@{{ comment.parent.user.username }}</p>
{% endif %}
{{ comment.content }}
</div>
</li>
{% endfor %}
</ul>
</div>
{# 渲染评论楼#}
{% if request.user.is_authenticated %}
<div>
<p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
<div>
<textarea name="comment" id="id_comment" cols="60" rows="10" ></textarea>
</div>
<button class="btn btn-primary" id="id_submit">提交评论</button>
<span style="color: red" id="errors"></span>
</div>
{% else %}
<li><a href="{% url 'reg' %}">注册</a></li>
<li><a href="{% url 'login' %}">登陆</a></li>
{% endif %}
$.ajax({
url:'/comment/',
type:'post',
data:{
'article_id':'{{ article_id }}',
'content':content,
'parent_id':parent_id,
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
success:function (args) {
if(args.code==1000){
$('#errors').text('{{ args.msg }}');
$('#id_comment').val('');
let userName = '{{ request.user.username }}';
let temp = `
<li class="list-group-item">
<span>${userName}</span>
<span><a href="#" class="pull-right">回复</a></span>
<div>
${content}
</div>
</li>`
$('.list-group').append(temp);
parent_id = null;
}
}
})
})
$('.reply').click(function () {
let commentUserName = $(this).attr('username');
parent_id = $(this).attr('comment_id');
$('#id_comment').val('@'+commentUserName+'\n').focus()
})