视频加载失败

文章相关功能

2380 字
12 分钟
文章相关功能

【三】手动录入数据#

  • 从文章表入手
  • 录入文章需要个人站点 --- 创建个人站点
  • 录入文章需要文章标签 --- 创建文章标签
  • 文章与标签进行绑定
  • 用户与个人站点进行绑定

【四】主页文章渲染#

【1】需求分析#

【2】视图函数#

def home(request, *args, **kwargs):
article_all = Article.objects.all()
return render(request, "home.html", locals())

【3】路由映射#

from django.contrib import admin
from django.urls import path
from django.urls.conf import include
from blog.views import home
urlpatterns = [
path('admin/', admin.site.urls),
path("", home, name="home"),
path("user/", include("user.urls")),
path("blog/", include("blog.urls")),
path("article/", include("article.urls")),
]

【4】前端页面#

{# 加载静态文件 #}
{% load static %}
{# 加载 inclusion_tag #}
{% load CommonInclusionTag %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>#}
{# <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>#}
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.min.css' %}">
<script src="{% static 'js/sweetalert.min.js' %}"></script>
<script src="{% static 'plugins/layui/layui/layui.js' %}"></script>
<link rel="stylesheet" href="{% static 'plugins/layui/layui/css/layui.css' %}">
</head>
<body>
{# 导航条开始 #}
<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="{% url 'home' %}">BBS</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="https://www.cnblogs.com/dream-ze" target="_blank">博客园</a></li>
<li><a href="https://blog.csdn.net/Chimengmeng" target="_blank">CSDN</a></li>
<li><a href="https://juejin.cn/" target="_blank">掘金</a></li>
<li role="separator" class="divider"></li>
<li><a href="https://gitee.com/chi-meng/bbs-related-projects" target="_blank">Gitee</a></li>
</ul>
</li>
</ul>
{# 搜索框 跳转百度页面 #}
<form class="navbar-form navbar-left" id="searchForm">
<div class="form-group">
<input type="text" class="form-control" placeholder="放松一刻" id="searchText">
</div>
<button type="submit" class="btn btn-default">百度一下</button>
</form>
{# 右侧登录注册导航栏 #}
<ul class="nav navbar-nav navbar-right">
{# 利用auth模块的登陆认证功能,利用 Django的 模版语法,判断当前用户是否登录 #}
{# 如果当前用户处于登陆状态则将右侧登录注册隐藏,渲染当前个人信息 #}
{% if request.user.is_authenticated %}
{# 渲染当前用户的头像 #}
<li>
<img src="{{ request.user.avatar }}" alt="缓冲中 ~~~ " class="img-circle"
style="height: 48px;width: 48px">
</li>
{# 页面显示当前登录的用户名 #}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
{{ request.user.username }}[
{% if request.user.is_superuser %}
管理员
{% else %}
用户
{% endif %}
]
<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><a href="{% url 'adv' %}">添加广告</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">注销登陆</a></li>
</ul>
</li>
{# 如果当前用户处于未登录的状态,则渲染注册和登陆框 #}
{% else %}
<li><a href="{% url 'register' %}">注册</a></li>
<li><a href="{% url 'login' %}">登陆</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{# 导航条结束 #}
{# 修改密码模态框开始 #}
<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="modal-header">
{# 右上角关闭模态框的 x #}
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
{# 大标题 : 修改密码页面 #}
<h4 class="modal-title text-center" id="myLargeModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6 col-md-offset-3">
{# 定义form表单,但是仍然不走 form 表单提交数据,而是使用ajax #}
<form id="reset_password_form">
<div class="form-group">
<label for="inputUsername">用户名 : </label>
<input type="text" class="form-control" name="username" id="inputUsername"
value="{{ request.user.username }}" disabled>
</div>
<div class="form-group">
<label for="inputOldPassword">旧密码 : </label>
<input type="text" class="form-control" name="old_password" id="inputOldPassword"
placeholder="old_password">
</div>
<div class="form-group">
<label for="inputNewPassword">新密码 : </label>
<input type="text" class="form-control" name="new_password" id="inputNewPassword"
placeholder="new_password">
</div>
<div class="form-group">
<label for="inputConfirmPassword">确认密码 : </label>
<input type="text" class="form-control" name="confirm_password"
id="inputConfirmPassword"
placeholder="confirm_password">
</div>
{# 定义点击修改按钮 #}
<div class="form-group">
<input type="button" class="btn btn-warning btn-block" value="确认修改"
id="btn_reset_password">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{# 修改密码模态框结束 #}
{# 主题框架搭建开始 #}
{% block content_outer %}
<div class="container-fluid">
<div class="row">
{# 左侧广告栏 #}
<div class="col-md-2">
{% home_adv %}
</div>
{# 中间主题内容 #}
<div class="col-md-8">
{% block content_main %}
{# 渲染所有用的文章 #}
{% for article_obj in article_all %}
{# 每一篇文章对象都是一个小的组件 #}
<div class="media">
{# 渲染文章标题 #}
<h4 class="media-heading"><a href="">{{ article_obj.title }}</a></h4>
{# 渲染左侧侧边头像 #}
<div class="media-left">
<a href="#">
<img class="media-object" data-src="holder.js/64x64" alt="64x64"
src="{{ article_obj.blog.userinfo.avatar }}" data-holder-rendered="true"
style="width: 64px; height: 64px;">
</a>
</div>
{# 渲染文章的简介信息 #}
<div class="media-body">
{# 有文章简介时渲染文章简介 #}
{% if article_obj.desc %}
{{ article_obj.desc }}
{# 没有文章简介时,渲染当前文章内容的前 30 个字符 #}
{% else %}
{{ article_obj.content|truncatechars:30 }} ...
{% endif %}
</div>
{# 加一个换行美观页面 #}
<br>
{# 渲染底部的 作者 发布日期 点赞数 点踩数 评论数 #}
<div>
{# 当前文章作者 跨表查询 从文章表出发 --> 个人站点表 ---> 用户表 #}
<a href="" style="padding-right: 10px">{{ article_obj.blog.userinfo.username }}</a>
{# 文章发布时间,做前端模板的格式化输出语法 #}
<span style="padding-right: 10px">{{ article_obj.create_time|date:"Y-m-d H:i:s" }}</span>
{# 当前文章的点赞数图标及数量 , 图标来自于 bootstrap #}
<span style="padding-right: 10px">
<span class="glyphicon glyphicon-thumbs-up"></span>
{{ article_obj.up_num }}
</span>
{# 当前文章的点踩数图标及数量 , 图标来自于 bootstrap #}
<span style="padding-right: 10px">
<span class="glyphicon glyphicon-thumbs-down"></span>
{{ article_obj.down_num }}
</span>
{# 当前文章的评论数图标及数量 , 图标来自于 bootstrap #}
<span style="padding-right: 10px">
<span class="glyphicon glyphicon-comment"></span>
{{ article_obj.comment_num }}
</span>
</div>
</div>
{# 美化页面,每一篇文章信息都加一个横线划分 #}
<hr>
{% endfor %}
{% endblock %}
</div>
{# 右侧广告栏 #}
<div class="col-md-2">
{% home_adv %}
</div>
</div>
</div>
{% endblock %}
{# 主题框架搭建结束 #}
{# js文件书写位置 #}
{# 百度搜索框点击搜索事件开始 #}
<script>
$(document).ready(function () {
// 定义函数
function search() {
// 使用jQuery获取输入框中的值
let keyword = $("#searchText").val();
// 构造跳转到百度搜索的链接
let urlTag = "https://www.baidu.com/s?wd=" + encodeURIComponent(keyword);
// 在新窗口中打开链接
window.open(urlTag, "_blank");
}
// 给搜索按钮绑定点击事件
$("#searchForm").submit(function (event) {
// 阻止表单默认提交行为
event.preventDefault();
// 调用搜索函数
search();
});
// 修改密码按钮点击事件
$("#btn_reset_password").on("click", function () {
// 使用 sweetalert 的弹框确认事件
swal({
title: "修改密码操作不可逆!",
text: "一旦密码被修改,无法找回原密码,确认修改吗?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((willReset) => {
// 判断用户是否点击了确认按钮
if (willReset) {
let data = {}
$.each($("#reset_password_form").serializeArray(), function (index, dataDict) {
data[dataDict.name] = dataDict.value
})
data["csrfmiddlewaretoken"] = "{{ csrf_token }}"
$.ajax({
url: "{% url 'reset_password' %}",
type: "post",
data: data,
success: (response) => {
if (response.code === 200) {
swal({
title: response.message,
icon: "success",
button: true,
}).then(() => {
window.location.href = response.next_url;
});
} else {
swal({
title: response.errors,
icon: "error",
button: true,
}).then(() => {
window.location.reload()
});
}
}
})
} else {
swal("好好想一想哦 ~ ~ ~ ");
}
});
})
});
</script>
{# 百度搜索框点击搜索事件结束 #}
</body>
</html>

【五】个人站点文章#

【1】需求分析#

【2】视图函数#

# 个人站点视图
def site(request, username, *args, **kwargs):
# 【一】校验当前用户是否存在,如果不存在则返回 404 error 页面
if not UserInfo.objects.filter(username=username).exists():
return render(request, "errors/404.html", locals())
# 【二】用户名存在则返回当前用户视图
# 筛选出当前用户下的所有文章数据
article_list = Article.objects.filter(blog__userinfo__username=username).all()
return render(request, "site.html", locals())

【3】路由映射#

# 个人站点路由
path("site/<slug:username>/", site, name="site"),

【4】前端页面#

(1)注册侧边栏#

  • blog/templatetags/CommonInclusionTag.py
@register.inclusion_tag("inclusion_tags/article_left.html")
def article_left(user_obj):
# user_obj : 当前登录的用户对象
# 【一】获取当前用户下所有的标签分类名称
# 跨表查询:从关联的 用户 表中查询出当前用户的个人站点
# article_category_class = CategoryClass.objects.filter(blog=user_obj.blog)
# 【二】获取当前用户下的所有分类及分类下的文章数
article_category_class = CategoryClass.objects.filter(blog=user_obj.blog).annotate(
article_num=Count('article__pk')).values("name", "article_num")
# 【三】获取当前用户下所有的标签名称
# article_category_tags = CategoryTag.objects.filter(blog=user_obj.blog)
# 【四】获取当前用户下的所有标签及标签下的文章数
article_category_tags = CategoryTag.objects.filter(blog=user_obj.blog).annotate(
article_num=Count('article__pk')).values("name", "article_num")
# 【四】获取当前用户下的所有文章的日期(按照年月分组)
# 日期归档 官网提供了一个关于日期字段的切割数据的方法
# https://docs.djangoproject.com/zh-hans/3.2/ref/models/database-functions/#trunc
# 不要忘了修改配置
# 切换时区为亚洲上海 : TIME_ZONE = 'Asia/Shanghai'
# USE_TZ = False
article_list = Article.objects.filter(blog=user_obj.blog).annotate(month=TruncMonth("create_time")).annotate(
article_num=Count("pk")).values("month", "article_num")
print(article_list)
# 【五】获取当前用户下的所有文章点赞数
article_up = Article.objects.filter(blog=user_obj.blog).order_by("-up_num").values("title", "up_num")
return locals()
  • blog/templates/inclusion_tags/article_left.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
{# 随笔分类 块 #}
<div class="list-group">
{# 头部 标题 #}
<a href="#" class="list-group-item active">
随笔分类
</a>
{# 遍历每一个文章对象 #}
{% for article_category_class_obj in article_category_class %}
<a href="#" class="list-group-item">
{{ article_category_class_obj.name }} ({{ article_category_class_obj.article_num }})
</a>
{% endfor %}
</div>
{# 随笔标签块 #}
<div class="list-group">
{# 头部 标题 #}
<a href="#" class="list-group-item active">
随笔标签
</a>
{# 遍历每一个文章标签和文章对象 #}
{% for article_category_tag_obj in article_category_tags %}
<a href="#" class="list-group-item">
{{ article_category_tag_obj.name }} ({{ article_category_tag_obj.article_num }})
</a>
{% endfor %}
</div>
{# 随笔归档块 #}
<div class="list-group">
{# 头部 标题 #}
<a href="#" class="list-group-item active">
随笔档案
</a>
{# 遍历所有筛选过后的文章对象 #}
{% for article_obj in article_list %}
{# 获取每一个文章对象中的 归档后的月份 和 文章数 #}
<a href="#" class="list-group-item">{{ article_obj.month|date:"Y-m" }} ({{ article_obj.article_num }})</a>
{% endfor %}
</div>
<div class="list-group">
<a href="#" class="list-group-item active">
点赞排行
</a>
{% for article_obj in article_up %}
<a href="#" class="list-group-item">{{ article_obj.title }} ({{ article_obj.up_num }})</a>
{% endfor %}
</div>
</body>
</html>

(2)个人站点页#

{% extends 'home.html' %}
{% block title %}
{{ request.user.blog.site_title }}
{% endblock %}
{% block content_outer %}
<div class="container-fluid">
<div class="row">
{# 左面三分为侧边 展示栏 #}
<div class="col-md-2">
{# 加载 inclusion_tag 标签 #}
{% load CommonInclusionTag %}
{# 调用自定义的标签,参数为当前登录用户的对象 #}
{% article_left request.user %}
</div>
{# 右边九份为文章展示栏 #}
<div class="col-md-10">
{# 右上角的随笔总数 #}
<div class="pull-right">
随笔 - {{ article_list.count }}
</div>
{# 换行为了好看 #}
<br>
{# 遍历每一篇文章对象进行渲染 #}
{% for article_obj in article_list %}
<div class="media">
{# 主体 部分 渲染 文章的标题和简介 #}
<div class="media-body">
<h4 class="media-heading"><a href="">{{ article_obj.title }}</a></h4>
<hr>
<span>{{ article_obj.desc }}</span>
</div>
{# 底部渲染 文章的发布日期,作者,点赞数,点踩数,评论数,编辑按钮等 #}
<div class="media-bottom pull-right">
posted
@ {{ article_obj.create_time|date:"Y-m-d H:i:s" }}
<a href="">{{ article_obj.blog.userinfo.username }}</a>
点赞 {{ article_obj.up_num }}
点踩 {{ article_obj.down_num }}
评论 {{ article_obj.comment_num }}
<a href="">编辑</a>
</div>
</div>
{# 换行是为了好看 #}
<br>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Profile Image of the Author
Dream
Hello, I'm Dream.
公告
欢迎来到我的博客!爱学习爱进步。
分类
标签
最新动态
站点统计
文章
169
分类
3
标签
26
总字数
334,507
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录