博客系统|文章详情页、评论、评论树
1、简单的实现评论功能
article_detail.html,拿到数据 由路--给视图函数--写入数据库
<p>评论内容:</p> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> <p> <button class="btn btn-default comment_btn">提交评论</button> </p>
url
path("comment/", views.comment),
--
<script> $('.comment_btn').click(function () { {#根评论#} var pid=''; var content=$('#comment_content').val(); $.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) } }) })
---
def comment(request): """ 提交评论视图函数 功能: 1 保存评论 2 创建事务 3 发送邮件 :param request: :return: """ 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)
评论之后--清空评论框
显示根评论的两种方法
1、render显示根评论
---
class="list-group
article_detail.html
<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><div class="comment_con"> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul>
2、ajax显示评论:es6 模板字符串
上一种方法评论后刷新才显示---用ajax评论就显示
success:function (data) { console.log(data); var create_time = data.create_time; var username=data.username; var content=data.content; // 构建标签字符串 es6语法 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span>${username}</span></a> </div> <div class="comment_con"> <p>${content}</p> </div> </li>`; $("ul.comment_list").append(s);
def comment():
response={} response['create_time']=comment_obj.create_time.strftime("%Y-%m-%d %X") response['username']=request.user.username response['content']=content
return JsonResponse(response)
根评论
{# 评论#} <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> </div> <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> <p> <button class="btn btn-default comment_btn">提交评论</button> </p> </div> <script> $('.comment_btn').click(function () { {#根评论#} var pid=''; var content=$('#comment_content').val(); $.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语法 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span>${username}</span></a> </div> <div class="comment_con"> <p>${content}</p> </div> </li>`; $("ul.comment_list").append(s); // 传回数据后,清空评论框中的内容 $('#comment_content').val(''); } }) }) </script>
3、子评论
--
--
// 回复按钮事件 $('.reply_btn').click(function () { $('#comment_content').focus(); // 找父评论 var val='@'+$(this).attr('username')+'\n'; $('#comment_content').val(val) })
提交子评论
根评论和子评论看是否点击回复
--
对样式进行改进
子评论对内容进行处理
清空pid
# indexOf() 定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。 语法 stringObject.indexOf(searchvalue,fromindex) # slice() 定义和用法 slice() 方法可从已有的数组中返回选定的元素。 语法 arrayObject.slice(start,end)
博客系统之render显示子评论
{% if comment.parent_comment_id %} <div class="pid_info well"> <p> {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }} </p> </div> {% endif %}
博客系统之Ajax显示子评论的思路
4、评论树
1、评论树简介
1、方式1:递归完成
2、方式2:评论树展示数据
2、评论树的请求数据
{# 评论#} <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> </div> {#-------评论树-------#} <script> $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}" }, success: function (comment_list) { console.log(comment_list); $.each(comment_list, 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 + '><span>' + content + '</span></div>'; if (!parent_comment_id) { $(".comment_tree").append(s); } else { $("[comment_id=" + parent_comment_id + "]").append(s); } }) } }) </script>
视图函数
def get_comment_tree(request): article_id=request.GET.get('article_id') ret=list(models.Comment.objects.filter(article_id=article_id).values('pk','content','parent_comment_id')) return JsonResponse(ret,safe=False)
-----
function (index, comment_object)
函数中两个参数对应下图中的,一个索引,循环的元素对象
博客系统之评论事务操作
# 事务 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) # 数据同步 F比较两个字段 models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+1)
博客系统之评论的邮件发送new
别人对你的博客评论或者对你的评论进行评论,博客发邮件给你
django中配置邮件
https://blog.csdn.net/xinxinnogiveup/article/details/78900811
https://code.ziqiangxuetang.com/django/django-send-email.html
1、settings配置
# 发送邮件 EMAIL_USE_SSL = True # EMIAL_HOST = 'smtp.exmail.qq.com' # 如果是163 改成smtp.163.com EMAIL_HOST = 'smtp.qq.com' # 如果是 163 改成 smtp.163.com EMAIL_PORT = 465 # 163的端口号是25 EMAIL_HOST_USER = 'xxxxxxxx@qq.com' # 账号 EMAIL_HOST_PASSWORD = 'oXXXXXxxxxx' # qq邮箱的授权码而不是密码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
# 发邮件 from django.core.mail import send_mail from cnblog import settings send_mail('您的文章%s新增了一条内容' % article_obj.title,content, settings.EMAIL_HOST_USER, ["xxxxxxx@qq.com"])
加线程代码优化
# 发邮件 from django.core.mail import send_mail from cnblog import settings import threading t=threading.Thread(target=send_mail,args=('您的文章%s新增了一条内容' % article_obj.title,content, settings.EMAIL_HOST_USER, ["836342406@qq.com"])) t.start()
整体代码
url
# 点赞 path('digg/',views.digg), path("comment/", views.comment), # 获取评论树相关数据 path("get_comment_tree/", views.get_comment_tree), # media re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}), # 文章详情页 # re_path('(?P<username>\w)/(?P<article_id>\d+)$',views.article_detail), re_path('^(?P<username>\w+)/articles/(?P<article_id>\d+)$', views.article_detail), # 关于个人站点的url re_path("^(?P<username>\w+)/$", views.home_site), # home_site(reqeust,username="yuan") #个人站点下的跳转 re_path('^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site), # home_site(reqeust,username="yuan",condition='tag',param='python')
settings
LANGUAGE_CODE = 'en-us' # 时区选择 # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True # 转换时区 # USE_TZ = True USE_TZ = False # 发送邮件 EMAIL_USE_SSL = True # EMIAL_HOST = 'smtp.exmail.qq.com' # 如果是163 改成smtp.163.com EMAIL_HOST = 'smtp.qq.com' # 如果是 163 改成 smtp.163.com EMAIL_PORT = 465 EMAIL_HOST_USER = '@qq.com' # 账号 EMAIL_HOST_PASSWORD = 'xxxxxx' # qq邮箱的授权码而不是密码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
view视图
from django.shortcuts import render # Create your views here. from django.shortcuts import render, HttpResponse,redirect # Create your views here. # 用户认证模块 from django.contrib import auth from django.http import JsonResponse from blog.Myforms import UserForm from blog.models import UserInfo from blog import models import json from django.db.models import F from django.db import transaction from django.http import JsonResponse from django.db.models import Avg, Max, Min, Sum, Count from django.db import transaction def login(request): if request.method == 'POST': response = {'user': None, 'msg': None} user = request.POST.get('user') pwd = request.POST.get('pwd') # 读取验证码 valid_code = request.POST.get('valid_code') # 回话跟踪技术,保存验证码 valid_code_str = request.session.get('valid_code_str') # 如果书写的验证码和生成的验证码一致 # 不区分大小写---可以统一变成大写 if valid_code.upper() == valid_code_str.upper(): user = auth.authenticate(username=user, password=pwd) if user: auth.login(request, user) # request.user==当前登录对象 response['user'] = user.username else: response['msg'] = 'username or password error' else: response['msg'] = 'valid code error!' return JsonResponse(response) # ajax返回一个响应字符串 return render(request, 'login.html') # 随机验证码 def get_validCode_img(request): from blog.utils.validCode import get_valid_Code_img data = get_valid_Code_img(request) return HttpResponse(data) def index(request): article_list=models.Article.objects.all() return render(request, 'index.html', locals()) def register(request): # 实例化 form 对象 if request.is_ajax(): print(request.POST) # 对传来的数据进行验证 form = UserForm(request.POST) response = {'user': None, 'msg': None} if form.is_valid(): response['user'] = form.cleaned_data.get('user') # 注册成功要把数据添加进数据库 # 生成一条用户记录 user = form.cleaned_data.get('user') pwd = form.cleaned_data.get('pwd') email = form.cleaned_data.get('email') avatar_obj = request.FILES.get('avatar') # if avatar_obj: # user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj) # else: # user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email) extra = {} if avatar_obj: extra["avatar"] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) else: print(form.cleaned_data) print(form.errors) response['msg'] = form.errors # 返回给Ajax return JsonResponse(response) form = UserForm() return render(request, 'register.html', locals()) def logout(request): # request.session.flush() auth.logout(request) return redirect('/login/') def get_classification_data(username): user_obj = models.UserInfo.objects.filter(username=username).first() blog = user_obj.blog cate_list = models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count('article__title')).values_list( 'title', 'c') tag_list = models.Tag.objects.filter(blog=blog).values('pk').annotate(count=Count('article')).values_list('title', 'count') date_list = models.Article.objects.filter(user=user_obj).extra( select={'y_m_date': "date_format(create_time,'%%Y-%%m')"}).values('y_m_date').annotate( c=Count('nid')).values_list('y_m_date', 'c') return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list} def home_site(request,username,**kwargs): ''' 个人站点视图函数 :param request: :return: ''' user_obj=models.UserInfo.objects.filter(username=username).first() # 判断用户是否存在 if not user_obj: return render(request,'not_found.html') blog = user_obj.blog print(blog) # 当前用户或者当前站点所对应文章 # 方式一基于对象查询 # 作者和文章的关系---> 一对多(文章) # article_list=user_obj.article_set.all() # 方式二 基于双下划线 __ 跨表查询 article_list=models.Article.objects.filter(user=user_obj) # 判断是否跳转到其他地方 # re_path("^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$", views.home_site), if kwargs: condition = kwargs.get("condition") # 标签\分类\归档 param = kwargs.get("param") # 具体的哪一个 if condition == "category": article_list = article_list.filter(category__title=param) elif condition == "tag": article_list = article_list.filter(tags__title=param) print(article_list) else: year, month = param.split('-') print(year, month) article_list = article_list.filter(create_time__year=year,create_time__month=month) print(article_list) # 查询每一个分类名称以及对应的文章数 # annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # values('group by的字段') # ret=models.Category.objects.values('pk').annotate(c=Count('article__title')).values('title','c') # print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 cate_list=models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count('article__title')).values_list('title','c') print(cate_list) # 每一个标签以及对应得文章数 tag_list=models.Tag.objects.filter(blog=blog).values('pk').annotate(count=Count('article')).values_list('title','count') print('tag_list',tag_list) # 单表分组查询 # 查询当前站点每一个年月的名称以及对应的文章数 # 方式一 date_list = models.Article.objects.filter(user=user_obj).extra( select={'y_m_date': "date_format(create_time,'%%Y-%%m')"}).values('y_m_date').annotate( c=Count('nid')).values_list('y_m_date', 'c') print(date_list) # 方式二 from django.db.models.functions import TruncMonth # # date_list=models.Article.objects.filter(user=user_obj).annotate(month=TruncMonth('create_time')).values('month').annotate(c=Count('nid')).values_list('month','c') # print(date_list) return render(request, "home_site.html", locals()) def article_detail(request,username,article_id): user_obj = models.UserInfo.objects.filter(username=username).first() blog = user_obj.blog cate_list = models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count('article__title')).values_list( 'title', 'c') tag_list = models.Tag.objects.filter(blog=blog).values('pk').annotate(count=Count('article')).values_list('title', 'count') date_list = models.Article.objects.filter(user=user_obj).extra( select={'y_m_date': "date_format(create_time,'%%Y-%%m')"}).values('y_m_date').annotate( c=Count('nid')).values_list('y_m_date', 'c') 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()) # {'context':context,'blog':blog,'article_obj':article_obj,'comment_list':comment_list} def digg(request): print(request.POST) article_id=request.POST.get('article_id') # is_up=request.POST.get('is_up') # 字符串 is_up=json.loads(request.POST.get('is_up')) # 字符串 # 点赞人即当前登录人 user_id=request.user.pk # 重复点赞和反对--都无效 obj=models.ArticleUpDown.objects.filter(user_id=user_id,article_id=article_id).first() response={'state':True,'msg':None} if not obj: ard=models.ArticleUpDown.objects.create(user_id=user_id,article_id=article_id,is_up=is_up) queryset= models.Article.objects.filter(pk=article_id) if is_up: queryset.update(up_count=F('up_count')+1) else: queryset.update(down_count=F('up_count')+1) else: response['state']=False response['handled']=obj.is_up return JsonResponse(response) def comment(request): """ 提交评论视图函数 功能: 保存评论 创建事务 发送邮件 :param request: :return: """ 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() # 事务 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) # 数据同步 F比较两个字段 models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+1) response={} 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 import threading t=threading.Thread(target=send_mail,args=('您的文章%s新增了一条内容' % article_obj.title,content, settings.EMAIL_HOST_USER, ["836342406@qq.com"])) t.start() return JsonResponse(response) def get_comment_tree(request): article_id=request.GET.get('article_id') ret=list(models.Comment.objects.filter(article_id=article_id).values('pk','content','parent_comment_id')) return JsonResponse(ret,safe=False)
article_detail.html
{% extends "base.html" %} {% block content %} {% csrf_token %} <div class="article_info"> <h3 class="text-center title">{{ article_obj.title }}</h3> <div class="cont"> {{ article_obj.content|safe }} </div> {# 博客园的点赞样式 ----#} <div class=""> <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="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <hr> <hr> <div class="diggword" id="digg_tips" style="color: red;" ></div> </div> </div> </div> {# 点赞#} <script> $("#div_digg .action").click(function () { var is_up = $(this).hasClass("diggit"); $obj = $(this).children("span"); $.ajax({ url: "/digg/", type: "post", data: { "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), "is_up": is_up, "article_id": "{{ article_obj.pk }}", }, success: function (data) { console.log(data); if (data.state) { var val = parseInt($obj.text()); $obj.text(val + 1); } else { var val2 = data.handled ? "您已经推荐过!" : "您已经反对过!"; $("#digg_tips").html(val2); setTimeout(function () { $("#digg_tips").html("") }, 1000) } } }) }); {#评论请求#} $('.comment_btn').click(function () { var content = $("#comment_content").val(); $.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) } }) }) </script> {# 评论#} <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> {# <div comment_id="'1">#} {# <span></span>#} {# </div>#} {# <div comment_id="'2">#} {# <span></span>#} {# </div>#} {# <div comment_id="'3">#} {# <span></span>#} {# </div>#} </div> {#-------评论树-------#} <script> $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}" }, success: function (comment_list) { console.log(comment_list); $.each(comment_list, 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 + '><span>' + content + '</span></div>'; if (!parent_comment_id) { $(".comment_tree").append(s); } else { $("[comment_id=" + parent_comment_id + "]").append(s); } }) } }) </script> <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> <p> <button class="btn btn-default comment_btn">提交评论</button> </p> </div> <script> var pid=''; $('.comment_btn').click(function () { {#根评论#} {#var pid='';#} var content=$('#comment_content').val(); if(pid){ var index=content.indexOf('\n'); content=content.slice(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语法 var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span>${username}</span></a> </div> <div class="comment_con"> <p>${content}</p> </div> </li>`; $("ul.comment_list").append(s); // 传回数据后,清空评论框中的内容 $('#comment_content').val(''); pid='' } }) }); // 回复按钮事件 $('.reply_btn').click(function () { $('#comment_content').focus(); // 找父评论 var val='@'+$(this).attr('username')+'\n'; $('#comment_content').val(val) pid=$(this).attr('comment_pk') }) </script> {% endblock %}
home_site.html
{% extends 'base.html'%} {% block css %} {% endblock %} {% block content %} {# 文章样式#} <div class="article_list"> {% for article in article_list %} <div class="article-item clearfix"> <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5> <div class="article-desc"> {{ article.desc }} </div> <div class="small pub_info pull-right"> <span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span> <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }}) <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }}) </div> </div> <hr> {% endfor %} </div> {% endblock %}
base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin: 0; padding: 0 } .header { width: 100%; height: 60px; background-color: #369 } .header .title { font-size: 18px; font-weight: 100; line-height: 60px; color: white; margin-left: 15px } .backend { float: right; margin-right: 15px; color: white; text-decoration: none; } .article_info .title{ margin-bottom: 100px; } {% block css %} {% endblock %} </style> <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> {# 在base引入home_site 和 article_datail的样式#} <link rel="stylesheet" href="/static/css/article_detail.css" > <link rel="stylesheet" href="/static/css/home_site.css" > <script src="/static/JS/jquery-3.2.1.min.js"></script> <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 --> <script src="/static/blog/bootstrap-3.3.7/js/bootstrap.min.js"></script> </head> <body> <div class="header"> <div class="content"> <p class="title"> <span>{{ blog.title }}</span> <a href="" class="backend">管理</a> </p> </div> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-warning"> <div class="panel-heading">我的标签</div> <div class="panel-body"> {% for tag in tag_list %} <p><a href="/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-danger"> <div class="panel-heading">随笔分类</div> <div class="panel-body"> {% for cate in cate_list %} <p><a href="/{{ username }}/category/{{ cate.0 }}">{{ cate.0 }}({{ cate.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading">随笔归档</div> <div class="panel-body"> {% for date in date_list %} <p><a href="/{{ username }}/archive/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</a></p> {% endfor %} </div> </div> </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>