5.博客系统| 文章详情页
文章详情页
文章详情页的渲染;点赞与踩灭(点赞人即当前登录人); 评论
1. 文章详情页的设计和数据构建
urls.py
#media配置
re_path(r"media/(?P<path>.*)$", serve, {"document_root":settings.MEDIA_ROOT}),
re_path('^(?P<username>\w+)/articles/(?P<article_id>\d+)$',views.article_detail),
views.py
def get_classification_data(username): #查询相关的数据,获取分类的数据
user = UserInfo.objects.filter(username=username).first() #个人站点对象
blog = user.blog #视图函数里边构建的数据就是为了传到模板里边去使用它们。
cate_list = models.Category.objects.filter(blog=blog).annotate(c = Count("article__title")).values_list("title", "c")
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c = Count("article")).values_list("title","c")
date_list = models.Article.objects.filter(user=user).extra(select={"y_m": "date_format(create_time,'%%Y-%%m')"}).values("y_m").annotate(c=Count("nid")).values_list("y_m","c")
return {"blog":blog, "cate_list":cate_list, "tag_list":tag_list, "date_list":date_list}
def article_detail(request, username, article_id): #127.0.0.1:8000/egon/article/1
context = get_classification_data(username)
return render(request, "article_detail.html", context)
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; margin-top: -10px;; } .backend{ float:right; color: white; text-decoration: none; font-size: 16px; margin-right:10px; margin-top: 10px;; } .pub_info{ margin-top: 10px; color: darkgrey; } </style> <link rel="stylesheet" href="/static/blog/bootstrap/css/bootstrap.css"> </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>
home_site.html
{% extends 'base.html' %} //继承base.html {% block content %} <div class="article_list"> {% for article in article_list %} <div class="article-item clearfix"> <h5><a href="">{{ 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 %}
2. 文章详情页的inclution_tag(定义自己的标签)
样式和数据结合成一个整体
两种方法:1是可以用函数get_classification_data (构建函数,传到模板文件里边去,如上边那种方法)
2是可以用inclution_tag(方法二,样式和数据结合成一个整体)
my_tags.py
#Author:Kris from django import template from blog import models from django.db.models import Count register = template.Library() @register.simple_tag def multi_tag(x, y): return x*y #参数是要接收一个模板 @register.inclusion_tag("classification.html") #引入一套模板语法,构建下面这几个就是为了传给模板里边的base.html的col-md-3让它去渲染了 ,可以把它单独拿开放到classfication.html def get_classification_style(username): #如果是封装成函数,一调用就返回数据,而它还可以把数据交给一个模板,把数据和样式结合成一个整体 user = models.UserInfo.objects.filter(username=username).first() #个人站点对象 blog = user.blog cate_list = models.Category.objects.filter(blog=blog).annotate(c = Count("article__title")).values_list("title", "c") tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c = Count("article")).values_list("title","c") date_list = models.Article.objects.filter(user=user).extra(select={"y_m": "date_format(create_time,'%%Y-%%m')"}).values("y_m").annotate(c=Count("nid")).values_list("y_m","c") return {"blog":blog, "cate_list":cate_list, "tag_list":tag_list, "date_list":date_list} #先解读上边这些数据,然后返回一个字典给classification.htl,渲染之后就是真正的html了,
##get_classification_style函数拿到的就是渲染之后的这个html文件。
定义好标签之后,是在模板里边去使用。
article_detail.html
{% extends "base.html" %} {% block content %} {% load my_tags %} //要先load下 ,相当于python的import {% multi_tag 3 9 %} // 27 {% endblock %}
base.html
<div class="col-md-3 menu"> {% load my_tags %} //把原来在base.html里边的col-md-3的内容单独作为一个classification.html,然后再load下把它引入进来。 {% get_classification_style username %} //把那一堆html代码又返回到这个地方来了 </div>
classificition.html
<div> <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>
home_site.html
{% extends 'base.html' %} {% block content %} <div class="article_list"> {% for article in article_list %} <div class="article-item clearfix"> <h5><a href="">{{ 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 %}
views.py的视图函数访问home_site.html进行渲染,同时继承了base.html进行渲染,渲染base的时候会去调用get_classification_style,先取数据再去交给classification.html这套样式渲染,渲染之后的结果再放到这个col-md-3中,
views.py
def home_site(request, username, **kwargs): ''' 个人站点视图 :param request: :param username: :return: ''' print("kwargs", kwargs) #区分访问的是站点页面还是站点下的跳转页面 print("username", username) user = UserInfo.objects.filter(username = username).first() #过滤 #判断用户是否存在 if not user: return render(request, "not_found.html") #查询当前站点对象 blog = user.blog #当前用户或者当前站点对应所有文章 #基于对象查询 #articile_list = user.article_set.all() #基于__ article_list = models.Article.objects.filter(user=user) if kwargs: condition = kwargs.get("condition") param = kwargs.get("param") #2012-12 if condition == "category": #http://127.0.0.1:8000/egon/category/egon的web/ article_list = article_list.filter(category__title=param) elif condition == "tag": #http://127.0.0.1:8000/egon/tag/web/ article_list = article_list.filter(tags__title=param) else: year, month = param.split("-") #http://127.0.0.1:8000/egon/archive/2018-07/ article_list = article_list.filter(create_time__year=year, create_time__month=month) def get_classification_data(username): user = UserInfo.objects.filter(username=username).first() #个人站点对象 blog = user.blog cate_list = models.Category.objects.filter(blog=blog).annotate(c = Count("article__title")).values_list("title", "c") tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c = Count("article")).values_list("title","c") date_list = models.Article.objects.filter(user=user).extra(select={"y_m": "date_format(create_time,'%%Y-%%m')"}).values("y_m").annotate(c=Count("nid")).values_list("y_m","c") return {"blog":blog, "cate_list":cate_list, "tag_list":tag_list, "date_list":date_list} def article_detail(request, username, article_id): #context = get_classification_data(username) return render(request, "article_detail.html", locals())
3.文章详情页渲染的标签字符串转义
def article_detail(request, username, article_id): ''' 文章详情页 :param request: :param username: :param article_id: :return: ''' user = models.UserInfo.objects.filter(username=username).first() #个人站点对象 blog = user.blog article_obj = models.Article.objects.filter(pk=article_id).first() #拿到文章对象,要传到模板里边去 # context = get_classification_data(username) comment_list = models.Comment.objects.filter(article_id=article_id) return render(request, "article_detail.html", locals())
article_detail.html
{% extends "base.html" %} {% block content %} {# {% load my_tags %}#} {# {% multi_tag 3 9 %}#} <h3 class="text-center">{{ article_obj.title }}</h3> <div class="cont"> {{ article_obj.content |safe }} {# 加这个是为了告诉django的模板不要给我做任何转义,为了安全性;网页源码#} </div> {% endblock %}
作为一个用户提交了JS代码,相当于我可以控制了其他看到这篇文章的用户的浏览器了,可以通过js代码去控制其他信息。这叫XSS恶意攻击。
Django加了safe转义了给你变成了特殊符号。作为开发人员,在用户提交的时候,不应该允许他提交。