17.模板页面
文章展示的页面布局和左侧栏使用的个人站点样式, 右侧展示文章详细内容.
0. 新建base.html模板页面
1. 将个人站点页面代码复制到模板页面base.html中
2. 设置替换的区域
3. 个人站点页面继承模板页面
17.1 模板页面
base.html页面代码 ↓
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>个人站点title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
<link rel="stylesheet" href="{% static 'bootstrap-sweetalert-master/dist/sweetalert.css' %}">
<script src="{% static 'bootstrap-sweetalert-master/dist/sweetalert.min.js' %}">script>
<link rel="stylesheet" href="/media/css/{{ blog_obj.site_theme }}/">
head>
<body>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span>
<span class="icon-bar">span>
<span class="icon-bar">span>
button>
<a class="navbar-brand" href="#">{{ request.user.username }}a>
div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">博客 <span class="sr-only">(current)span>a>li>
<li><a href="#">文章a>li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">更多 <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="#">Actiona>li>
<li><a href="#">Another actiona>li>
<li><a href="#">Something else herea>li>
<li role="separator" class="divider">li>
<li><a href="#">Separated linka>li>
<li role="separator" class="divider">li>
<li><a href="#">One more separated linka>li>
ul>
li>
ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
div>
<button type="submit" class="btn btn-default">Submitbutton>
form>
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#">{{ request.user.username }}a>li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">更多 <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码a>li>
<li><a href="#">修改头像a>li>
<li><a href="#">后台管理a>li>
<li role="separator" class="divider">li>
<li><a href="/logout/">退出登入a>li>
ul>
li>
{% else %}
<li><a href="/register/">注册a>li>
<li><a href="/login/">登入a>li>
{% endif %}
ul>
div>
div>
nav>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="panel panel-primary">
<div class="panel-heading">分类div>
<div class="panel-body">
{% for sort_obj in sort_list %}
<p>
<a href="/{{ site_name }}/sort/{{sort_obj.sort__pk }}">
{{ sort_obj.sort__name }}({{ sort_obj.count_num }})
a>
p>
{% endfor %}
div>
div>
<div class="panel panel-info">
<div class="panel-heading">标签div>
<div class="panel-body">
{% for tag_obj in tag_list %}
<p>
<a href="/{{ site_name }}/tag/{{ tag_obj.tag__pk }}/">
{{ tag_obj.tag__name }}({{ tag_obj.count_num }})
a>
p>
{% endfor %}
div>
div>
<div class="panel panel-default">
<div class="panel-heading">时间归档div>
<div class="panel-body">
{% for year_month_obj in year_month_list %}
<p>{{ year_month_obj.month|date:"Y年m月" }}({{ year_month_obj.count_num }})a>p>
{% endfor %}
div>
div>
div>
{% block centent %}
{% endblock %}
div>
div>
body>
html>
17.2 继承模板页面
个人站点继承模板页面
{% extends 'base.html' %}
{% block centent %}
<div class="col-md-9">
{% for queryset_obj in article_query_set %}
<div class="media">
<h4 class="media-heading"><a href="">{{ queryset_obj.title }}a>h4>
<div class="media-left media-middle">
<a href="#">
<img class="media-object" src="/media/{{ queryset_obj.blog.userinfo.avatar }}" alt="..."
width="80">
a>
div>
<div class="media-body">
{{ queryset_obj.desc }}
div>
<div class="pull-right">
<p>
<span>pasted@span>
<span><a href="">{{ queryset_obj.blog.userinfo.username }}a>span>
<span>{{ queryset_obj.create_time|date:'Y-m-d' }}span>
<span class="glyphicon glyphicon-comment">评论({{ queryset_obj.comment_num }})span>
<span class="glyphicon glyphicon-thumbs-up">点赞({{ queryset_obj.up_num }})span>
<span class="glyphicon glyphicon-thumbs-down">点踩({{ queryset_obj.down_num }})span>
<span><a href="">编辑a>span>
p>
div>
<hr>
div>
<nav aria-label="Page navigation">nav>
{% endfor %}
{# 利用自动分页器 #}
{{ page_obj.page_html|safe }}
div>
{% endblock %}
18. 文章详细展示
在点击文件的标题之后, 展示文章的详细内容.
url的设计格式
127.0.0.1:8000/用户/article/文章的主键
18.1 路由层
def article_desc(request, site_name, article_id):
article_obj = models.Article.objects.filter(pk=article_id).first()
if not article_obj:
return render(request, 'error.html')
return render(request, 'article.html', locals())
18.2 文章展示页面
文章展示页面继承 base.html 模板页面.
def article_desc(request, site_name, article_id):
article_obj = models.Article.objects.filter(pk=article_id).first()
blog_obj = models.Blog.objects.filter(site_name=site_name).first()
if not article_obj:
return render(request, 'error.html')
return render(request, 'article.html', locals())
18.3 绑定文章id
主页 与 个人站点 的页面中点击文章标题展示文章的内容, 为文章标题的a标签设置路由.
<a href="/{{ queryset_obj.blog.site_name }}/article/{{ queryset_obj.pk }}/">
{{ queryset_obj.title }}
a>
<h4 class="media-heading"><a href="/{{ site_name }}/article/{{ queryset_obj.pk }}/">{{ queryset_obj.title }}a>h4>
19. 制作包含标签
文章详细左侧表单需要数据才能展示出来.
获取数据的方法:
1. 可以将个人站点的分类归档代码复制一份. (不推荐)
2. 将左侧栏制作成inclusion_tag包含标签, base模板页面调用inclusion_tag包含标签即可.
制作inclusion_tag包含标签步骤:
1. 在app应用下创建一个templatetags目录
2. 在该文件内创建一个menu.py文件(命名无特殊要求)
3. 在tempales中创建一个left_menu.html文件, 包含标签生成页面的文件.
4. 将需要数据的三个表单html代码复制到 left_menu.html文件中.
3. 在py文件中写上固定代码导入需要的模块, 写业务逻辑, 最后将数据传递给指定的页面.
19.1 自定义模板语法
将个人站点的归档代码复制到menu.py文件中, 导入需要的模块.
将个人站点归档代码可以删除掉.
主表.objects.条件.annotate(别名=聚合函数('从表的字段')).valeu(需要展示的字段)
Count 操作的字段针对的是从表的主键, 主键有唯一标识, 只要涉及统计什么的数量就直接使用 '从表__id'.
Max, Min, Sum, Avg 操作它的字段.
from django import template
register = template.Library()
from app01 import models
from django.db.models import Max, Min, Sum, Count, Avg
from django.db.models.functions import TruncMonth
@register.inclusion_tag('left_menu.html')
def left_menu(site_name):
blog_obj = models.Blog.objects.filter(site_name=site_name).first()
if blog_obj:
article_query_set = models.Article.objects.filter(blog=blog_obj)
sort_list = models.Sort.objects.filter(blog=blog_obj).annotate(
count_num=Count('article__pk')).values('pk', 'name', 'count_num')
print(article_query_set)
print(sort_list)
tag_list = models.Tag.objects.filter(blog=blog_obj).annotate(
count_num=Count('article__pk')).values('pk', 'name', 'count_num')
year_month_list = models.Article.objects.filter(blog=blog_obj).annotate(month=TruncMonth('create_time')).values(
'month').annotate(count_num=Count('pk')).values('month', 'count_num')
return locals()
19.2 获取数据生成模板
将左侧栏表单复制到这里left_menu.html页面中
menu.py生成的数据会被传递到这个页面中, 并渲染出信息.
<link rel="stylesheet" href="/media/css/{{ blog_obj.site_theme }}/">
<div class="panel panel-primary">
<div class="panel-heading">分类div>
<div class="panel-body">
{% for sort_obj in sort_list %}
<p>
<a href="/{{ site_name }}/sort/{{sort_obj.pk }}">
{{ sort_obj.name }}({{ sort_obj.count_num }})
a>
p>
{% endfor %}
div>
div>
<div class="panel panel-info">
<div class="panel-heading">标签div>
<div class="panel-body">
{% for tag_obj in tag_list %}
<p>
<a href="/{{ site_name }}/tag/{{ tag_obj.pk }}/">
{{ tag_obj.name }}({{ tag_obj.count_num }})
a>
p>
{% endfor %}
div>
div>
<div class="panel panel-default">
<div class="panel-heading">时间归档div>
<div class="panel-body">
{% for year_month_obj in year_month_list %}
<p>{{ year_month_obj.month|date:"Y年m月" }}({{ year_month_obj.count_num }})a>p>
{% endfor %}
div>
div>
19.3 使用包含标签
在模板页面base.html 中调用包含标签.
{% load menu %}
{% left_menu site_name %}
19.4 文章展示问题
现在文章就是一个段落p标签展示, 没有任何的样式, 如果想让文章变的好看就因该使用html来写.
使用Typora写好之后到处HTML格式文件.
打开导出的文件, 开发开发者工具复制html写的博客代码.
进入后台管理, 将HTML代码文章写入数据库中.
文章页面中 使用safe过滤器将渲染成HTML页面.
<p>{{ article_obj.content|safe }}p>
复制博客园的文章 复制div id="cnblogs_post_body" 的标签.
将所有的文章内容都替换成html格式的文章, 图片有防盗技术就无法展示.
20. 文章点赞点踩
2.1 点赞点踩按钮
复制博客园的点赞, 点赞的HTML代码.
1. 复制HTML标签
<div id="div_digg">
<div class="diggit" onclick="votePost(16081428,'Digg')">
<span class="diggnum" id="digg_count">0span>
div>
<div class="buryit" onclick="votePost(16081428,'Bury')">
<span class="burynum" id="bury_count">0span>
div>
<div class="clear">div>
<div class="diggword" id="digg_tips">
div>
div>
2. 将点击事件删除掉后, 复制到article.html页面中
3. 复制 div_digg diggit diggnum buryit burynum clear diggword 的样式, 一个一个复制下来.
#div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 125px;
text-align: center;
margin-top: 10px;
}
.diggit {
float: left;
width: 46px;
height: 52px;
background: url(//static.cnblogs.com/images/upup.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.diggnum {
font-size: 14px;
color: #075db3;
}
.buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url(//static.cnblogs.com/images/downdown.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.burynum {
font-size: 14px;
color: #075db3;
}
.clear {
clear: both;
}
.diggword {
margin-top: 5px;
margin-left: 0;
font-size: 12px;
color: #808080;
}
4. 博客园的图片有防盗技术, 将图片下载到本地.
https://static.cnblogs.com/images/upup.gif
https://static.cnblogs.com/images/downdown.gif
background: url(/static/img/upup.jpg) no-repeat;
background: url(/static/img/downdown.jpg) no-repeat;
5. 在base.html的<heah>标签中设置css的替换区域.
{% block css %}
{% endblock %}
{% extends 'base.html' %}
{% block css %}
<style>
#div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 125px;
text-align: center;
margin-top: 10px;
}
.diggit {
float: left;
width: 46px;
height: 52px;
background: url(/static/img/upup.jpg) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.diggnum {
font-size: 14px;
color: #075db3;
}
.buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url(/static/img/downdown.jpg) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.burynum {
font-size: 14px;
color: #075db3;
}
.clear {
clear: both;
}
.diggword {
margin-top: 5px;
margin-left: 0;
font-size: 12px;
color: #808080;
}
style>
{% endblock %}
{% block centent %}
<div class="col-md-9">
<div>
<h1 class="text-center">{{ article_obj.title }}h1>
<p>{{ article_obj.content|safe }}p>
div>
<div>
<div id="div_digg">
<div class="diggit action ">
<span class="diggnum" id="digg_count">{{ article_obj.up_num }}span>
div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_num }}span>
div>
<div class="clear">div>
<div class="diggword" id="digg_tips"><span id="msg" style="color: red">span>div>
div>
div>
div>
{% endblock %}
<div class="clear">div>
2.2 绑定事件
在base.html设置js独立的区域.
{% block js %}
<script>
$(".action").on('click', function () {
let is_Up = $(this).hasClass('diggit')
$.ajax({
url: '/up_or_down/',
type: 'post',
data: {
"csrfmiddlewaretoken": '{{ csrf_token }}',
'article_id': '{{ article_obj.pk }}',
is_Up: is_Up
},
success: function (args) {
if (args.code === 200) {
$('#msg').text(args.right_msg)
$('#digg_count').text(args.up_num)
$('#bury_count').text(args.down_num)
}else{
$('#msg').html(args.error_msg)
}
}
})
})
</script>
{% endblock %}
2.3 获取ajax数据
1. 判断请求方式
2. 判断用户是否登入, (文章可以是不登入的用户看, 点赞点踩则需要登入)
3. 判断用户是否给自己的文章点赞
4. 判断用户是否点击过这篇文件
5. 判断点赞还是点踩
逻辑有点先, 先写正确的逻辑在写错误的逻辑.
操作两张表:
Article 中使用F查询, 多点击的数据进行操作
UpAndDown 表中记录 那个用户给哪篇文章点赞还是点踩.
user_id article_id is_up (True为点赞/ False为点踩)
1 3 True
判断某个用户时候给某篇文章是否点击过. 点赞点踩表对象.filter (user_id and article_id) 满足这两个条件
说明点过了.
def up_or_down(request):
if request.is_ajax():
back_dict = {'code': 200}
if request.user.is_authenticated():
post_obj = request.POST
article_id = post_obj.get('article_id')
is_up = post_obj.get('is_Up')
import json
is_up = json.loads(is_up)
article_obj = models.Article.objects.filter(pk=article_id).first()
if article_obj.blog.userinfo.username != request.user.username:
up_or_down_obj = models.UpAndDown.objects.filter(user_id=request.user.pk, article_id=article_obj)
if not up_or_down_obj:
if is_up:
models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
back_dict['right_msg'] = '点赞成功!'
else:
models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') - 1)
back_dict['right_msg'] = '点踩成功!'
models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
article_obj = models.Article.objects.filter(pk=article_id).first()
back_dict['up_num'] = article_obj.up_num
back_dict['down_num'] = article_obj.down_num
else:
back_dict = {'code': 401, 'error_msg': '已经评论过不能在评论!'}
else:
back_dict = {'code': 402, 'error_msg': '自己的文章此功能不可操作!'}
else:
back_dict = {'code': 403, 'error_msg': '请先登入账户!'}
print(back_dict)
return JsonResponse(back_dict)
else:
return HttpResponse('非法访问')
is_up = request.POST.get('is_Up')
import json
is_up = json.loads(is_up)
点击别人的文章
点击自己的文章
没有登入时点击
back_dict = {'code': 403, 'error_msg': '请先登入账户!'}
2.4 点赞点踩计数
两种方法:
1. 后端写入数据之后, 将文章对象发给前端刷新计数.
2. 后端获取包含计数标签的值, 做加减计算, 再刷新显示.
采用方式1, 在后端已经计算过, 就没必要在后端计算了.
back_dict['up_num'] = article_obj.up_num
back_dict['down_num'] = article_obj.down_num
$('#digg_count').text(args.up_num)
$('#bury_count').text(args.down_num)
21. 评论
21.1 根评论
在文章展示页面article.html部署一个评论框.
<div>
<p><span class="glyphicon glyphicon-comment">发表评论span>p>
<textarea name="comment" id="id_comment" cols="45" rows="10">textarea>
<p>
<button id="id_btn">提交button>
<span id="id_error">span>
p>
div>
设置浮动让评论和点赞点赞不在同一行当中.
<div class="clearfix">
<div id="div_digg">
...
div>div>
21.2 提交评论
ajax提交评论的信息, 那个用户给哪篇文章评论了什么.
$('#id_btn').on('click', function () {
let comment = $("[name='comment']").val()
$.ajax({
url: '/submit_comment/',
type: 'post',
data: {
article_id: '{{ article_obj.pk }}',
comment: comment,
"csrfmiddlewaretoken": '{{ csrf_token }}',
},
success: function (args) {
alert(args)
}
})
})
21.3 将提交信息写入表中
先写正确的逻辑.
def submit_comment(request):
if request.is_ajax():
if request.user.is_authenticated():
post_obj = request.POST
article_id = post_obj.get('article_id')
comment = post_obj.get('comment')
print(article_id, comment)
models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
article_obj = models.Article.objects.filter(pk=article_id).first()
models.Comment.objects.create(user=request.user, article=article_obj, content=comment)
back_dict = {'code': 200, 'right_msg': '评论成功'}
else:
back_dict = {'code': '400', 'error_msg': '请先登入在评论'}
return JsonResponse(back_dict)
else:
return HttpResponse('非法访问')
21.4 展示根评论
文章详细展示中, 获取评论的内容, 并渲染到前端.
comment_obj = models.Comment.objects.filter(article_id=article_id)
<div>
{% for obj in comment_obj %}
<div class="panel panel-default">
<div class="panel-heading">
<p>
<span>{{ forloop.counter }}楼span>
<span>{{ obj.comment_time|date:'Y-m-d h:i:s' }}span>
<span>{{ obj.user.username }}span>
<span class="pull-right"><a href="#">回复a>span>
p>
div>
<div class="panel-body">
<div>
{{ obj.content }}
div>
div>
div>
{% endfor %}
div>
21.5 提交评论时渲染
1. 点击提交评论按键后清空评论框内的内容,
2. 渲染自己的评论
方式1: DOM临时渲染
方式2: 页面刷新render渲染
模板代码${替换的内容}...
success:function (args) {
if (args.code === 200) {
$('#id_comment').val('')
let username = '{{ request.user.username }}'
let tem =
${username}
${comment}
$('#id_exhibit').append(tem)
}
}else{
}
没有用户登入时渲染提示登入信息.
if (args.code === 200) {
...
}else{
$('#id_error').html(args.error_msg)
21.6 子评论
点击回复书写字评论.
点击回复的时候, 鼠标聚焦到评论框中.
格式:
@用户名(换行)
|(<--光标在这)
* 为回复按钮绑定点击事件, 这个回复是在for循环内不能使用id值来标记, 可以使用类来标记. 之后查找该标签.
需要获取的信息, 评论用户的名字, 根评论的主键id, 可以在for循环中为标签自定义两个属性接收需要的值.
注意点 a标签不要有href, href='#'会跳转到页面头部.
<span class="pull-right"><a class="reply" username="{{ obj.user.username }}" parent_id="{{ obj.pk }}">回复a>span>
$('.reply').on('click', function (){
let username = $(this).attr('username')
let comment_id = $(this).attr('parent_id')
$('#id_comment').val('@' + username + '\n').focus()
})
自评与根评论都是同一个按钮提交数据.
区分根评论和子评论: 子评论的parent_id有值. parent_id指向评论表中的id.
{% block js %}
<script>
$(".action").on('click', function () {
let is_Up = $(this).hasClass('diggit')
$.ajax({
url: '/up_or_down/',
type: 'post',
data: {
"csrfmiddlewaretoken": '{{ csrf_token }}',
'article_id': '{{ article_obj.pk }}',
'is_Up': is_Up
},
success: function (args) {
if (args.code === 200) {
$('#msg').text(args.right_msg)
$('#digg_count').text(args.up_num)
$('#bury_count').text(args.down_num)
} else {
$('#msg').html(args.error_msg)
}
}
})
})
var parent_id = null
$('#id_btn').on('click', function () {
let comment = $("[name='comment']").val()
$.ajax({
url: '/submit_comment/',
type: 'post',
data: {
'article_id': '{{ article_obj.pk }}',
'comment': comment,
'parent_id': parent_id,
"csrfmiddlewaretoken": '{{ csrf_token }}',
},
success: function (args) {
if (args.code === 200) {
$('#id_comment').val('')
let username = '{{ request.user.username }}'
let tem =
${username}
${comment}
$('#id_exhibit').append(tem)
}
}
})
})
$('.reply').on('click', function () {
let username = $(this).attr('username')
parent_id = $(this).attr('parent_id')
$('#id_comment').val('@' + username + '\n').focus()
})
</script>
def submit_comment(request):
if request.is_ajax():
if request.user.is_authenticated():
post_obj = request.POST
article_id = post_obj.get('article_id')
comment = post_obj.get('comment')
parent_id = post_obj.get('parent_id')
print(article_id, comment)
models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
article_obj = models.Article.objects.filter(pk=article_id).first()
models.Comment.objects.create(user=request.user, article=article_obj, content=comment, parent_id=parent_id)
back_dict = {'code': 200, 'right_msg': '评论成功'}
else:
back_dict = {'code': '400', 'error_msg': '请先登入在评论'}
return JsonResponse(back_dict)
else:
return HttpResponse('非法访问')
print(parent_id, type(parent_id))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pevzSlH-1649082690806)(https://s2.loli.net/2022/04/02/Emg7GxoijZlkR15.gif)]
子评论中@用户 不是评论的内容不因该写进数据中, 将这部分进行切除.
$('#id_btn').on('click', function () {
let comment = $("[name='comment']").val()
if (parent_id) {
let IndexNum = comment.indexOf('\n') + 1
comment = comment.slice(IndexNum)
}
...
21.7 子评论的渲染
格式:
@用户
信息
<div class="panel-body">
<div>
{% if obj.parent_id %}
<div>
<p><span style="color: #aaff00">@{{ obj.parent.user.username }}span>p>
div>
{% endif %}
{{ obj.content }}
div>
div>
obj.parent.user.username
评论的数据对象.自关联的外键, 拿到根评论的数据对象.外键.username拿到根评论的用户名.
21.8 重置子评论全局变量
第一次写是子评论
第二次写是根评论
这时会发现都是子评论, 因为parent_id获取到值之后就一直有值,
不重置为null的话, 获取到了值, 之后永远都是子评论.
在回调函数的末尾重置parent_id为null
再次测试, 先写一个子评论, 在写一个根评论.
正常
文章展示代码
{% extends 'base.html' %}
{% block css %}
<style>
#div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 125px;
text-align: center;
margin-top: 10px;
}
.diggit {
float: left;
width: 46px;
height: 52px;
background: url(/static/img/upup.jpg) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.diggnum {
font-size: 14px;
color: #075db3;
}
.buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url(/static/img/downdown.jpg) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.burynum {
font-size: 14px;
color: #075db3;
}
.clear {
clear: both;
}
.diggword {
margin-top: 5px;
margin-left: 0;
font-size: 12px;
color: #808080;
}
style>
{% endblock %}
{% block centent %}
<div class="col-md-9">
<div>
<h1 class="text-center">{{ article_obj.title }}h1>
<p>{{ article_obj.content|safe }}p>
div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action ">
<span class="diggnum" id="digg_count">{{ article_obj.up_num }}span>
div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_num }}span>
div>
<div class="clear">div>
<div class="diggword" id="digg_tips"><span id="msg" style="color: red">span>div>
div>
div>
<div id="id_exhibit">
{% for obj in comment_obj %}
<div class="panel panel-default">
<div class="panel-heading">
<p>
<span>{{ forloop.counter }}楼span>
<span>{{ obj.comment_time|date:'Y-m-d h:i:s' }}span>
<span>{{ obj.user.username }}span>
<span class="pull-right"><a class="reply" username="{{ obj.user.username }}"
parent_id="{{ obj.pk }}">回复a>span>
p>
div>
<div class="panel-body">
<div>
{% if obj.parent_id %}
<div>
<p><span style="color: #aaff00">@{{ obj.parent.user.username }}span>p>
div>
{% endif %}
{{ obj.content }}
div>
div>
div>
{% endfor %}
div>
<div>
<p><span class="glyphicon glyphicon-comment">发表评论span>p>
<textarea name="comment" id="id_comment" cols="45" rows="10">textarea>
<p>
<button id="id_btn">提交button>
<span id="id_error">span>
p>
div>
div>
{% endblock %}
{% block js %}
<script>
$(".action").on('click', function () {
let is_Up = $(this).hasClass('diggit')
$.ajax({
url: '/up_or_down/',
type: 'post',
data: {
"csrfmiddlewaretoken": '{{ csrf_token }}',
'article_id': '{{ article_obj.pk }}',
'is_Up': is_Up
},
success: function (args) {
if (args.code === 200) {
$('#msg').text(args.right_msg)
$('#digg_count').text(args.up_num)
$('#bury_count').text(args.down_num)
} else {
$('#msg').html(args.error_msg)
}
}
})
})
var parent_id = null
$('#id_btn').on('click', function () {
let comment = $("[name='comment']").val()
if (parent_id) {
let IndexNum = comment.indexOf('\n') + 1
comment = comment.slice(IndexNum)
}
$.ajax({
url: '/submit_comment/',
type: 'post',
data: {
'article_id': '{{ article_obj.pk }}',
'comment': comment,
'parent_id': parent_id,
"csrfmiddlewaretoken": '{{ csrf_token }}',
},
success: function (args) {
if (args.code === 200) {
$('#id_comment').val('')
let username = '{{ request.user.username }}'
let tem =
${username}
${comment}
$('#id_exhibit').append(tem)
}else{
$('#id_error').html(args.error_msg)
}
parent_id = null
}
})
})
$('.reply').on('click', function () {
let username = $(this).attr('username')
parent_id = $(this).attr('parent_id')
$('#id_comment').val('@' + username + '\n').focus()
})
script>
{% endblock %}
Original: https://blog.csdn.net/qq_46137324/article/details/123888270
Author: 开局签到Python基础
Title: 14. Django博客项目3 点赞点踩&根评论子评论
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/735716/
转载文章受原作者版权保护。转载请注明原作者出处!