一、回顾
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. BBS项目 CMS 1. 登录 1. form组件 2. auth模块 3. 验证码 2. 注册 1. form组件 1. 生成html代码 直接for循环form_obj,就能够遍历所有字段 2. 验证 1. 默认的那些验证 2. 正则的验证 3. 全局钩子做确认密码的验证 4. 判断用户名是否已经存在 1. input框失去焦点就发ajax到后端判断 2. form组件中使用局部钩子来判断 2. auth模块 --> 扩展auth_user表 --> create_user() 1. UserInfo这个类里面,avatar是一个FileField avatar = models.FileField(upload_to="avatars/", default="avatars/default.png") 2. 注意事项: 1. FileField保存的是一个路径,而不是一个文件 2. upload_to:具体保存的文件路径就会在media目录下 3. 上传头像 1. ajax如何上传文件 2. Django中media的配置 1. settings.py中: - MEDIA_URL:别名 - MEDIA_ROOT:给用户上传的所有文件指定一个存放目录 2. urls.py中: from django.views.static import serve from django.conf import settings url(r'^media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}) 3. 博客首页 1. 文章列表(排样式) 2. 分页 1. 封装的要彻底 2. 封装后的结果要有普适性(url要写成配置项) 4. 个人博客主页 1. 分类 1. 文章分类 2. 文章标签 2. 日期归档 1. 日期格式化函数 --> MySQL内置的函数都有哪一些? --> 《漫画数据库》 1. MySQL:DATE_FORMAT('字段', '格式') 2. sqlite:strftime('格式', '字段') 2. ORM中如何执行原生的SQL语句 1. 使用extra()在执行ORM查询的同时,嵌入一些SQL语句 2. 直接执行原生SQL语句 from django.db import connection cursor = connection.cursor() cursor.execute('select id from userinfo;') cursor.fetchall() 3. 分组聚合 QuertySet.annotate() --> 分组,前面查的是什么字段就按什么字段分组 QuertySet.aggregate() --> 聚合,给QuerySet中的每个对象多一个属性 4. 4合1路由 不同的路由可以使用同一个视图函数!!! --> 视图函数中通过参数的不同,实现不同的功能 5. 文章详情页 1. 母板继承 2. inclusion_tag 1. 返回一段HTML代码,用数据填充的HTML代码 2. 具体的写法 1. 在app下面创建一个名为 templatetags 的 Python Package 2. 在上面的包中创建一个 py文件 3. 按照inclusion_tag的格式要求写功能函数 from django import template register = template.Library() @register.inclusion_tag(file='xx.html') def show_menu(*arg): ... return {"k1": "v1"} (xx.html中使用k1这个变量) 3. 点赞 1. ajax发送点赞的请求 1. 点赞必须是登录用户,没登录跳转到登录页面 2. 不能给自己的文章点赞 3. 一个用户只能给一篇文章点一次赞或踩一次 2. 后端创建点赞记录(事务操作) 1. 创建新的点赞记录 2. 去对应的文章表里把点赞数更新一个 3. ORM事务操作 from django.db import transaction with transaction.atomic(): sql1; sql2; 4. Django模板语言里面的JS代码 如何在js中引用模板语言的变量,注意加引号!!!
二、今日内容(评论楼和评论树)
1.评论展示HTML:注意给每个评论楼层添加一个comment_id,后续别人回复改评论,取其值为父评论id
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- 评论展示部分 开始--> <div class="list-group commnet_list"> {% for comment in comment_list %} <a href="javascript:void(0);" class="list-group-item"> <h4 class="list-group-item-heading" pid='{{ comment.id }}'> <span>{{ forloop.counter }}楼</span> <span>{{ comment.create_time|date:'Y-m-d H:i:s' }}</span> <span>{{ comment.user.username }}</span> <span class="pull-right reply">回复</span> </h4> {% if comment.parent_comment %} <span style="display: block;color: blue">@{{ comment.parent_comment.user.username }}</span> {% endif %} <p class="list-group-item-text" style="text-indent:20px; ">{{ comment.content }}</p> </a> {% endfor %} </div> <!-- 评论展示部分 结束-->
2.设置事件委托,点击回复定位至评论框,并在评论框内设置出现@用户名,存储comment_id 至button标签
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
$('.list-group').on('click', '.reply', function () { var replyName = $(this).prev().text(); $('#new-comment').focus().val('@' + replyName + ' '); var pId = $(this).parent().attr('pid'); $('#submit').data('pId', pId); })
3.评论区书写代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- 评论书写部分 开始--> <hr> <h4>发表评论</h4> <hr> <div> <span>用户名:</span> <input type="text" value="{{ request.user.username }}" disabled> </div> <div> <p>评论:</p> <textarea name="comment" id="new-comment" cols="60" rows="10"></textarea> </div> <div> <button class="btn" id="submit">提交</button> </div> <div style=" 100%;height: 100px;"></div> <!-- 评论书写部分 结束-->
4.ajax提交内容,通过button标签中存储的pid是否存在判断是子评论还是一级评论并处理评论内容(去除@XXX),ajax提交并返回响应数据,js添加响应html代码至浏览器。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
$('#submit').click(function () { var content = $('#new-comment').val(); var articleId = '{{ article.id }}'; var parentId = $('#submit').data('pId') || ""; if (parentId) { content = content.slice(content.indexOf(' ') + 1,) } $.ajax({ url: '/comment/?next=' + '{{ request.get_full_path }}', type: 'post', data: { content: content, article_id: articleId, parent_id: parentId, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, success: function (res) { if (res.code === 0) { var count = $('.commnet_list>a').length+1; {#判断是否需要@回复,还是一级评论#} if (res.data.parentName) { var reply_html = `<span style="display: block;color: blue">@${res.data.parentName }</span>`; } else { var reply_html = '' } var comment_html = ` <a href="javascript:void(0);" class="list-group-item"> <h4 class="list-group-item-heading" pid='${ res.data.commentId }'> <span>${ count }楼</span> <span>${ res.data.create_time}</span> <span>${ res.data.username }</span> <span class="pull-right reply">回复</span> </h4>`+reply_html+ `<p class="list-group-item-text" style="text-indent:20px; ">${ res.data.content }</p> </a>` $('.commnet_list').append(comment_html); $('#new-comment').val(''); $('#submit').removeData('pId') } } }) }) </script> <script> var userId = '{{ request.user.pk }}'; var articleId = '{{ article.id }}'; var isUp = false; $(".digg").click(function () { var username = '{{ username }}' var loginname = '{{ request.user.username }}' if (!'{{ request.user.username }}') { {#如果用户未登录,跳转到登录页面#} location.href = '/login2/?next=' + '{{ request.get_full_path }}'; } {#如果用户给自己点赞或反对,不行#} else if (username == loginname) { if ($(this).hasClass('diggit')) { $('#msg').text('不能给自己点赞!').css('color', 'red'); } else { $('#msg').text('不能反对自己!').css('color', 'red'); } } else { if ($(this).hasClass('diggit')) { isUp = true; } $.ajax({ url: '/evalute/', type: 'post', data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), userId: userId, articleId: articleId, isUp: isUp, }, success: function (res) { if (res.code !== 0) { $('#msg').text(res.msg).css('color', 'red'); } else { if (isUp) { var $up = $('#digg_count') $up.text(+$up.text() + 1) } else { var $down = $('#bury_count') $down.text(+$down.text() + 1) } } } }) } }) {#用户对已点赞或踩灭的文章进行取消!显示#} $('document').ready(function () { $.ajax({ url: '/cancel/', type: 'post', data: { userId: '{{ request.user.id }}', articleId: '{{ article.id }}', csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, success: function (res) { if (res.code === 0) { if (res.msg === 1) { $('#digg_tips').html('您已推荐过,<a href="javascript:void(0);" class="digg_gray">取消 </a>'); } else { $('#digg_tips').html('您已反对过,<a href="javascript:void(0);" class="digg_gray">取消 </a>'); } } else { $('#digg_tips').css('display', 'none'); } } }) }) $('#digg_tips').click(function () { $.ajax({ url: '/cancel1/', type: 'post', data: { userId: '{{ request.user.id }}', articleId: '{{ article.id }}', csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() }, success: function (res) { if (res.code !== 0) { $('#digg_tips').html('推荐取消失败,<a href="javascript:void(0);" class="digg_gray">刷新重试</a>'); } else { location.reload(); } } }) })
三、预习和扩展
1.模板中表单的简单写法
2.注册过程中检验是否存在同名用户的局部钩子
3.DOM元素存储data、获取data、删除data
4.JS三元运算和 ||
5.JS字符串截取
6.事件委托的运用
7. js字符串前加+号可以将字符串变成数字:
$down.text(+$down.text() + 1)