BBS-个人站点页
重点功能
- 侧边栏搜索功能路由整合
- 判断用户是否存在,用户不存在显示404页面
- 虚拟字段,年月,使用TruncMonth模块
- 按照不同的条件进行分组(annotate),显示该分组下的所有文章
- 根据不同的筛选条件来查询符合的文章
一、添加路由
urls.py文件中
个人站点路由
# 个人站点页面路由
url(r'^(?P<username>\w+)/$', views.site),
我们可以将最左侧的3项过滤栏的路由整合到一起(使用有名分组的形式)
# 侧边栏搜索功能
# ?wupeiqi/tag/1
# url(r'^(?P<username>\w+)/tag/(\d+)$', views.site), # 标签路由,存标签id
# url(r'^(?P<username>\w+)/category/(\d+)$', views.site), # 分类路由
# url(r'^(?P<username>\w+)/archive/(\w+)$', views.site), # 年月档案路由,不同的符号
# 侧边栏搜索功能的整合路由
url(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)', views.site), # 优化一下
# django2版本:re_path
分析:
"""
侧边栏点击添加不同的路由:
按照标签筛选
https://www.cnblogs.com/wupeiqi/tag/1/
按照分类筛选
https://www.cnblogs.com/wupeiqi/category/1/
按照时间筛选
https://www.cnblogs.com/wupeiqi/archive/2020-11/
组装侧边栏路由:
https://www.cnblogs.com/站点名/tag/标签id/
https://www.cnblogs.com/站点名/category/分类id/
https://www.cnblogs.com/站点名/archive/年-月/
视图函数仍然是site(),只是展示的文章少了(符合条件的文章数少了),即视图函数中要在查询之前先根据条件筛选一下文章。
"""
二、个人站点前端
1、base.html
步骤:
1.三七分,左边三分写导航栏,右边七分是文章列表展示
个人站点标题
2.文章数据,是后端查询当前站点的所有文章
3.调正文章下附带文字,class="pull-right" <!-- 样式在右边显示 -->
作者名
注册时间
点赞数、点踩数、评论数
注册
4.左边导航栏展示,所需数据需要先从后端获取
4.1 分类,分类下的所有数量。
4.2 标签,标签下的所有文章数量。
4.3 年月时间,符合的所有文章数量。
5. 左边的导航栏中的所有地址跳转
5.1 分类跳转路由:站点名(与用户名一致)/分类category/分类id
5.2 标签跳转路由:文章站点名(与用户名一致)/标签tag/标签id
5.3 标签跳转路由:文章站点名(与用户名一致)/日期archive/y-m
base页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父继承页面</title>
{# <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css"></script>#}
{% load static %}<!--引入静态文件-->
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'layer/layer.js' %}"></script>
{% block css %}
{% endblock %}
</head>
<body>
{# 导航条开始 #}
<!-- 跟home网页一样的导航条 -->
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<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 navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{{ blog.site_title}}</a><!-- 个人站点标题 -->
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<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="#">专区</a></li>
<li><a href="#">闪存</a></li>
<li><a href="#">班级</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">怀旧</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">发现</a></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">搜索</button>
</form>
{# 右侧展示用户名开始 #}
<ul class="nav navbar-nav navbar-right">
{% if request.session.username %} <!-- 用户登录显示用户名,失败显示注册登录样式 -->
<li><a href="#">{{ request.session.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>
</ul>
</li>
{% else %}
<li><a href="/register/">注册</a></li><!-- 点击注册跳转到注册页面 -->
<li><a href="/login/">登录</a></li>
{% endif %}
</ul>
{# 右侧展示用户名结束 #}
{# 修改密码模态框开始 #}
<div><!-- 点击修改密码,弹出模态框 -->
<div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog"
aria-labelledby="myLargeModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="row">
<h1 class="text-center">修改密码</h1>
<div class="col-md-8 col-md-offset-2">
<form action="">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" class="form-control" disabled
value="{{ request.session.username }}"><!-- 用户名显示并且禁用 -->
</div>
<div class="form-group">
<label for="old_password">原密码:</label>
<input type="password" id="old_password" class="form-control">
</div>
<div class="form-group">
<label for="new_password">新密码:</label>
<input type="password" id="new_password" class="form-control">
</div>
<div class="form-group">
<label for="re_password">确认密码:</label>
<input type="password" id="re_password" class="form-control">
</div>
{# <input type="button" class="btn btn-success" value="提交">#}
<button class="btn btn-success btn-block">提交</button>
<!-- button放在form表单中有自动提交的事件,下面script中需要阻止后续事件的执行 -->
<br>
<br>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{# 修改密码模态框结束 #}
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{# 导航条结束 #}
<!-- 三九分 -->
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
{# 左侧搜索展示开始 #}
<div class="panel panel-warning">
<div class="panel-heading">文章分类</div>
<div class="panel-body">
{% for cate in cate_list %}
<!-- 跳转到文章站点名(与用户名一致)/分类category/分类id。分类名(符合的文章数量) -->
<p>
<a href="/{{ username }}/category/{{ cate.2 }}">{{ cate.0 }}({{ cate.1 }})</a>
</p>
{% endfor %}
</div>
</div>
<div class="panel panel-success">
<div class="panel-heading">文章标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<!-- 跳转到文章站点名(与用户名一致)/标签tag/标签id。标签名(符合的文章数量) -->
<p>
<a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a>
</p>
{% endfor %}
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">随笔档案</div>
<div class="panel-body">
{% for date in date_list %}
<!-- 跳转到文章站点名(与用户名一致)/日期archive/y-m。2023年5月(符合的文章数量) -->
<p>
<a href="/{{ username }}/archive/{{ data.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}({{ date.co }})</a>
</p>
{% endfor %}
</div>
</div>
{# 左侧搜索展示结束 #}
</div>
{# 右侧文章内容开始 #}
<div class="col-md-9"><!-- 右侧的9分显示文章内容 -->
{% block content %}
{% endblock %}
</div>
{# 右侧文章内容结束 #}
</div>
</div>
{% block js %}
{% endblock %}
</body>
</html>
2、个人站点页面
templates中的site.html(在此页面中继承了上面所写的base.html)
<!-- 个人站点页面 -->
{% extends 'base.html' %}<!-- 继承base页面 -->
{% block content %}
<!-- 中间是文本内容,使用媒体对象 -->
<ul class="media-list">
{% for article in article_list %}<!-- 循环所有文章对象的列表 -->
<li class="media">
<h4 class="media-heading"><a href="/{{ username }}/article/{{ article.pk }}">{{ article.title }}</a></h4><!-- 文章标题 -->
<div class="media-left">
<a href="#">
<!-- 显示文章列表中的avatar,使用media文件夹 -->
<!-- 文章对象.站点表(反向).用户表(反向) -->
<img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" alt="..."
style="width: 70px;"><!-- 头像 -->
</a>
</div>
<div class="media-body">{{ article.desc }}</div><!-- 文章摘要 -->
<br>
<div class="pull-right"><!-- 全部右侧显示 -->
<!-- posted @ 2020-05-06 09:05 武沛齐 阅读(13888) 评论(50) 推荐(37) 编辑 -->
<span>posted @ </span>
<!-- 文章对象.站点表(反向).用户表(反向) -->
<span>{{ article.create_time|date:'Y-m-d' }} </span><!-- 注册时间 -->
<span><a href="">{{ article.blog.userinfo.username }}</a></span> <!-- 用户名 -->
<span><span class="glyphicon glyphicon-thumbs-up"></span>({{ article.up_num }}) </span>
<!-- 点赞数 -->
<span><span class="glyphicon glyphicon-thumbs-down"></span>({{ article.down_num }}) </span>
<!-- 点踩数 -->
<span><span class="glyphicon glyphicon-comment"></span>({{ article.comment_num }}) </span>
<!-- 评论数 -->
<span><a href="">编辑</a></span>
</div>
<hr>
</li>
{% endfor %}
</ul>
{% endblock %}
3、404页面
<html>
<head>
<meta charset="utf-8">
<link rel="icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon">
<title>404 页面不存在 - BBS</title>
<style type="text/css">
body {
margin: 8% auto 0;
max-width: 400px;
min-height: 200px;
padding: 10px;
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
padding-right: 200px;
position: relative;
}
p {
color: #555;
margin: 15px 0px;
}
img {
border: 0px;
}
.d {
color: #404040;
}
.robot img {
max-width: 192px;
}
.robot {
position: absolute;
top: 0;
right: 0;
}
</style>
</head>
<body>
<!-- 404页面可以直接复制 -->
<!--把两张照片复制下来-->
<p style="margin-left: 5px;"><a href="https://www.cnblogs.com/"><img src="/static/img/logo.svg"style="height:45px" alt="cnblogs"></a></p>
<div style="margin-top:20px">
<p style=""><b style="">404.</b> 抱歉,您访问的资源不存在。</p>
<p class="d">可能是网址有误,或者对应的内容被删除,或者处于私有状态。</p>
<p style="color:#777;">代码改变世界,联系邮箱 contact@cnblogs.com</p>
<p><a href="https://www.cnblogs.com/cmt/p/17320765.html">园子的商业化努力-困境求助:开设捐助通道</a></p>
</div>
<div class="robot"><a href="//www.cnblogs.com/cmt/articles/13940458.html"><img
src="/static/img/img.png" alt="404 robot"></a></div>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-4CQQXWHK3C"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-4CQQXWHK3C');
</script>
</body>
</html>
图片防盗链
""""
图片防盗链 -- 404页面
在请求头里面有个参数 referer参数可以区分出地址是从哪来的
referer参数指代的是上一次访问的路径地址,就是你这个地址是从哪过来的
怎么解决?直接把图片下载好后保存自己的项目的静态文件中,路径写静态文件的地址。
"""
三、添加视图函数
1、站点后端步骤
1.先根据用户名查询用户表
2. 用户不存在即站点不存咋,显示404页面
3. 查询出当前站点对象并赋值,供后续的筛选条件
4. 查询当前站点下的所有文章,前端9份展示
# (5-7步)前端导航条要展示的数据查询
5. 查询当前站点下的所有分类和统计的文章数(分类表查文章,反向),annocate分组、Count计数、values()列表套字典
6. 查询当前站点下的所有标签和统计的文章数(标签表查文章,反向),annocate分组、Count计数、values_list()列表套元组
7. 查询当前站点下的所有文章,并且按照年月展示,统计数量(文章表)
TruncMonth:统计年月的模块
先按照年月分组,annocate,虚拟字段
.values('month').annotate():按照虚拟字段中的月分组
选择月份和计数,values()列表套字典
8. 根据kwargs来筛选符合的文章
8.1 判断kwargs,有值就代表筛选,没有值就是所有文章
8.2 获取kwargs中的两个值
8.3 先按照condition条件筛选,分类、标签、年月
8.4 再按照具体的参数进一步筛选
文章查分类表 --> 反向(表名小写__字段)
文章查标签表 --> 正向(外键字段名__字段)
年月:解压赋值年月,单表查询,基于双下划线查询,(字段__year)
2.虚拟字段:年月
"""
id title content create_time month
1 a a 2023-05-10 2023-05
官方提供的TruncMonth模块是创建一个年月的虚拟字段
"""
-官方提供
from django.db.models.functions import TruncMonth
Article.objects
.annotate(month=TruncMonth('create_time')) # Truncate to month and add to select list # 括号内写存时间的字段,截断到月份并添加到选择列表
.values('month') # Group By month # 按照年月分组
.annotate(c=Count('id')) # Select the count of the grouping # 按照月分组计数
.values('month', 'c') # (might be redundant, haven't tested) select month and count # 选择月份和计数
3.函数代码
from django.db.models import Count
@login_auth
# def site(request, username):
def site(request, username, **kwargs): # 后面的不同参数直接用kwargs接收
"""个人站点页面显示函数"""
# 先根据用户名查询用户信息,用户表
user_obj = models.UserInfo.objects.filter(username=username).first()
if not user_obj: # 站点不存在,直接显示404页面
return render(request, '404.html')
# 站点对象(用户表->站点表-->正向)
blog_obj = user_obj.blog # 站点信息要先查询出当前用户的站点对象
# print(blog_obj) # Blog object
# 查询当前站点的所有文章
article_list = models.Article.objects.filter(blog=blog_obj).all()
"""筛选符合的文章"""
if kwargs: # 有值就代表筛选,没有值就是所有文章
"""
点击标签的字典:{'condition': 'tag', 'param': '1'}
点击分类的字典:{'condition': 'category', 'param': '1'}
点击年月的字典:{'condition': 'archive', 'param': '2020-11'}
"""
condition = kwargs.get('condition')
param = kwargs.get('param')
# 按照condition条件先筛选
if condition == 'category': # 按照分类id进一步筛选,筛选文章
# 文章查分类表 --> 反向(表名小写__字段)
article_list = article_list.filter(category__pk=param)
elif condition == 'tag':
# 文章查标签表 --> 正向(外键字段名__字段)
article_list = article_list.filter(tags__pk=param)
else:
year, month = param.split('-') # 2020-11 解压赋值:[2020, 11]
# 单表查询,基于双下划线查询,(字段__year)
article_list = article_list.filter(create_time__year=year, create_time__month=month)
# 1.查询当前站点下的所有分类(左侧导航条展示)和分类下的所有文章(右侧点击一个分类,只显示该分类下的文章)
# 按照分类分组(annotate),然后查询分类下的文章数,并统计
# 结合聚合函数进行统计,要导入
# 分类查询文章-->反向查询, 分类名,统计的文章数,分类id
# cate_list = models.Category.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values('name', 'count_num')
# <QuerySet [{'name': 'kevin的分类一', 'count_num': 1}, {'name': 'kevin的分类二', 'count_num': 1}]>
cate_list = models.Category.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values_list(
'name', 'count_num', 'pk')
# print(cate_list) # <QuerySet [('kevin的分类一', 1), ('kevin的分类二', 1), ('kevin的分类三', 1)]>
"""
.values(): QuerySet列表套字典 <QuerySet [{}, {}, {}]>
.values_list(): QuerySet列表套元组 <QuerySet [(), (), ()]>
"""
# 2.查询当前站点下的所有标签
# 标签表查文章表-->反向
tag_list = models.Tag.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values_list('name',
'count_num',
'pk')
# <QuerySet [('kevin的标签一', 1), ('kevin的标签二', 1), ('kevin的标签三', 1)]>
# 3.查询当前站点下的所有文章,并且按照年月展示--随笔档案
from django.db.models.functions import TruncMonth
# TruncMonth是统计月的
# annocate前面写values()是按照括号内的值分类
date_list = models.Article.objects.filter(blog=blog_obj).annotate(month=TruncMonth('create_time')).values(
'month').annotate(co=Count("pk")).values('month', 'co')
# <QuerySet [{'month': datetime.date(2022, 3, 1), 'co': 2}, {'month': datetime.date(2023, 4, 1), 'co': 1}]>
return render(request, 'site.html', locals())