目录
一、个人站点其余功能
1、侧边栏筛选功能
这里我们想要实现的功能就是点击分类标签后可以筛选出对应分类下的文章,并展示到网页上。这里我们可以查看博客园的样式进行模仿,首先需要模仿的就是路由的样式。
路由
- 1.先研究博客园三种情况下的筛选特性
分类筛选路由特性: 站点名称/category/数据主键值
标签筛选路由特性: 站点名称/tag/数据主键值
日期筛选路由特性: 站点名称/archive/文章年月 - 2.研究路由开设接口
多个路由使用相同的视图函数 因为个人站点的文章和侧边栏筛选的文章互为父子集
当我们根据博客园的路由进行模仿的时候,我们先是使用了转换器进行匹配,但是我们发现三个分类,其实东西不多,代码可以整合,因此我们使用正则合并路由,同时让这些路由也使用个人站点的视图函数
# 侧边栏筛选接口
# path('<str:username>/category/<int:category_id>/', views.site_func),
# path('<str:username>/tag/<int:tag_id>/', views.site_func),
# path('<str:username>/archive/<str:yearAndmonth>/', views.site_func),
# 上述三个路由可以合并成一个路由
re_path('^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<params>.*?)/', views.site_func)
而这里通过动态匹配,少的时候后端的视图函数会接受到两个变量,多的时候则会有四个,这里我们使用可变长参数接收。
同时我们考虑到个人站点也的左侧标签分类栏,在多个网页都需要使用,接下去我们编写标签分类栏后也需要显示,因此我们使用自定义inclusion_tag把这部分代码包出去,通过导入的方式使用他。
这样就可以避免前后端代码重复使用(主要是后端需要给前端传查询后的对象)
而使用自定义inclusion_tag有一些前置条件我们不能忘记配置。
这是应用下templatetags文件夹下mytag.py的代码:
from django import template
from app01 import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
register = template.Library()
@register.inclusion_tag('leftmenu.html',name='mymenu')
def index(username):
site_obj = models.Site.objects.filter(site_name=username).first()
# 查询个人站点下所有的分类名称以及每个分类下的文章数
category_queryset = models.Category.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
'name', 'article_num', 'pk')
# 查询个人站点下所有的标签名称以及每个标签下的文章数
tag_queryset = models.Tag.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
'name', 'article_num', 'pk')
# 年月分组并统计文章个数
from django.db.models.functions import TruncMonth
date_queryset = models.Article.objects.filter(site=site_obj).annotate(month=TruncMonth('create_time')).values(
'month').annotate(
article_num=Count('pk')).values('month', 'article_num')
return locals()
这部分功能的前端页面代码leftmenu.py:
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">文章分类</h3>
</div>
<div class="panel-body">
{% for category_obj in category_queryset %}
<p>
<a href="/{{ site_obj.site_name }}/category/{{ category_obj.pk }}/">{{ category_obj.name }}({{ category_obj.article_num }})</a>
</p>
{% endfor %}
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">文章标签</h3>
</div>
<div class="panel-body">
{% for tag_obj in tag_queryset %}
<p>
<a href="/{{ site_obj.site_name }}/tag/{{ tag_obj.pk }}/">{{ tag_obj.name }}({{ tag_obj.article_num }})</a>
</p>
{% endfor %}
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">日期归档</h3>
</div>
<div class="panel-body">
{% for date_obj in date_queryset %}
{#这里的日期需要自定义格式,中间用-连接,但是只要前后端统一,也可以替换成别的#}
<p>
<a href="/{{ site_obj.site_name }}/archive/{{ date_obj.month|date:'Y-m' }}/">{{ date_obj.month|date:'Y年m月' }}({{ date_obj.article_num }})</a>
</p>
{% endfor %}
</div>
</div>
这样我们在别的html文件中可以删除重复的代码,用两句代码导入即可
主页html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<script src="{% static 'jquery-3.6.1.js' %}"></script>
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
{#因为个人主页会出现需要继承主页的情况,同时又需要做到可以自定义样式,因此我们这里需要用到母版的语法#}
{% block css %}
{% endblock %}
{% load static %}
<link rel="SHORTCUT ICON" href="{% static "images/favicon1.ico" %}" />
</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="#">
{#个人主页中需要显示为个人标题,因此也添加母版语法#}
{% block title %}
BBS
{% endblock %}
</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>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="请输入搜索内容">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</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">
{#通过模态框的样板代码,我们发现他是通过data-toggle和data-target两个属性来进行绑定的,模态框代码另外编写就可以使用上#}
<li><a href="#" data-toggle="modal" data-target="#myModal">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="#">后台管理</a></li>
<li role="separator" class="divider"></li>
{#注销登陆绑定后端视图函数对应的路由就可以实现功能,代码也很简单使用auth模块提供的方法即可#}
<li><a href="/logout/">注销登录</a></li>
</ul>
</li>
{% else %}
{#登陆和注册使用a标签进行页面跳转#}
<li><a href="{% url 'register_view' %}">注册</a></li>
<li><a href="{% url 'login_view' %}">登录</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<!--导航条结束-->
<!--模态框开始-->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title text-center" id="myModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="">用户名</label>
{#这里展示用户名给用户看看,但是使用了disabled属性不让用户修改#}
<input type="text" value="{{ request.user.username }}" disabled class="form-control">
</div>
<div class="form-group">
<label for="">原密码</label>
<input type="text" id="old_pwd" class="form-control">
</div>
<div class="form-group">
<label for="">新密码</label>
<input type="text" id="new_pwd" class="form-control">
</div>
<div class="form-group">
<label for="">确认密码</label>
<input type="text" id="confirm_pwd" class="form-control">
</div>
</div>
<div class="modal-footer">
{#这里我们设置一个span标签展示错误信息提示#}
<span id="error" style="color: red"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
{#因为我们使用的是模态框来修改密码,因此需要使用ajax来提交数据#}
<button type="button" class="btn btn-warning" id="setBtn">修改</button>
</div>
</div>
</div>
</div>
<!--模态框结束-->
<!--内容区域开始-->
<div class="container-fluid">
<div class="row">
{#这里也是用母版语法让子网页可以自定义#}
{% block content %}
{#主页的主要内容分成三大部分,左右两边用row分开展示一些广告或是标签,中间部分展示文章(文章标题文章作者和头像以及部分内容展示)#}
<div class="col-md-2">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">重金求子</h3>
</div>
<div class="panel-body">
事后必有重谢:wuyong123
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">百万大奖</h3>
</div>
<div class="panel-body">
恭喜你幸运儿:zhanghong321
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">广告招租</h3>
</div>
<div class="panel-body">
旺铺吃一辈子:suiyuanli222
</div>
</div>
</div>
<div class="col-md-8">
{% for article_obj in page_queryset %}
<div class="media">
<h4 class="media-heading"><a href="/{{ article_obj.site.userinfo.username }}/article/{{ article_obj.pk }}/">{{ article_obj.title }}</a></h4>
<div class="media-left">
<a href="#">
<img class="media-object" src="/media/{{ article_obj.site.userinfo.avatar }}/" alt="..." width="80">
</a>
</div>
<div class="media-body" style="padding:10px">
{{ article_obj.desc }}
</div>
<br>
<div>
<span><a href="/{{ article_obj.site.userinfo.username }}/"> {{ article_obj.site.userinfo.username }} </a></span>
<span> {{ article_obj.create_time|date:'Y-m-d H:i:s' }} </span>
<span class="glyphicon glyphicon-thumbs-up"> {{ article_obj.up_num }} </span>
<span class="glyphicon glyphicon-thumbs-down"> {{ article_obj.down_num }} </span>
<span class="glyphicon glyphicon-comment"> {{ article_obj.comment_num }} </span>
</div>
</div>
<hr>
{% endfor %}
{#因为考虑到使用网站的时候,博客,也就是文章会很多,因此需要用分页器进行区分#}
<div class="text-center">{{ page_obj.page_html|safe }}</div>
</div>
<div class="col-md-2">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">重金求子</h3>
</div>
<div class="panel-body">
事后必有重谢:wuyong123
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<h3 class="panel-title">百万大奖</h3>
</div>
<div class="panel-body">
恭喜你幸运儿:zhanghong321
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">广告招租</h3>
</div>
<div class="panel-body">
旺铺吃一辈子:suiyuanli222
</div>
</div>
</div>
{% endblock %}
</div>
</div>
<!--内容区域结束-->
<script>
$('#setBtn').click(function () {
$.ajax({
url:'/set_pwd/',
type:'post',
data:{
'old_pwd':$('#old_pwd').val(),
'new_pwd':$('#new_pwd').val(),
'confirm_pwd':$('#confirm_pwd').val(),
'csrfmiddlewaretoken':'{{ csrf_token }}',
},
success: function (args) {
if (args.code === 10000) {
window.location.href = args.url
}else {
$('#error').text(args.msg)
}
}
})
})
</script>
{% block js %}
{% endblock %}
</body>
</html>
同时视图层中的代码也可以进行优化,我们只需要把username传给前端,就可以用提前准备好的侧边栏代码了。
username虽然是根据路由接收来的,但是这里还是说一下他的作用,我们的侧边栏代码虽然包出去了,但是他需要使用username来筛选数据并不是说那些操作不用做了。
views.py代码
def site_func(request, username, **kwargs):
# 查询个人站点是否存在
site_obj = models.Site.objects.filter(site_name=username).first()
if not site_obj:
return render(request, 'errorPage.html')
# 查询个人站点下所有的文章
article_queryset = models.Article.objects.filter(site=site_obj)
# 通过查看kwargs是否接受了参数,我们来判断当前网页是个人页,还是标签分类页
if kwargs:
# 在路由那,中间是分类的类别,最后是分类所用字段的id和创建时间,因此我们用condition来分辨他们的分类依据
condition = kwargs.get('condition')
params = kwargs.get('params')
# 这里是根据不同的分类得到筛选后的对象,返回给前端展示
if condition == 'category':
article_queryset = article_queryset.filter(category_id=params)
elif condition == 'tag':
article_queryset = article_queryset.filter(tags__pk=params)
else: # 年-月
year, month = params.split('-')
article_queryset = article_queryset.filter(create_time__year=year, create_time__month=month)
'''如果文章较多也应该添加分页器'''
return render(request, 'sitePage.html', locals())
ps:这里我提前整合了这个自定义侧边栏,正常推导流程中是在搭建文章详情页面的时候发现左侧的侧边栏需要重复使用代码展示,同时后端也要重复传对象给前端,比较麻烦,因此有了这个操作
2、文章详情页搭建
现在我们进入到了编写文章详情页的步骤,关于这一步,他的路由也是需要我们研究的,我们可以发现博客元的文章路由是这个样式的:
站点名称/article/数据主键值
因此我们的路由可以这样设计:
# 文章详情页
path('<str:username>/article/<int:article_id>/', views.article_detail_func),
接着我们创建视图函数和对应的html文件,这里的文章详情页也需要用继承的方式编写,后端返回给前端的对象是筛选后的文章对象,修改好后,我们还要会去修改个人站点和主页中展示的标题(这些标题都套上了a标签,这些标签都需要我们套上路由转到对应的文章详情页面去)
网页上文章内容展示代码如下
<h2 class="text-center">{{ article_obj.title }}</h2>
{{ article_obj.content|safe }}
主页html处,标题的href修改
href="/{{ article_obj.site.userinfo.username }}/article/{{ article_obj.pk }}/"
个人站点标题的href修改
href="/{{ article_obj.site.userinfo.username }}/article/{{ article_obj.pk }}/"
文章内容页的左侧侧边栏依旧是上面说的方式导入使用
ps:在给文章添加内容的时候我们如果觉得麻烦,可以点开浏览器的检查页面,然后点击检查界面左上角的箭头去选中网页上文章的主体部分,这时候我们就能看到一个post类的标签,点开他我们会看到一个类为postbody的标签,这里我们鼠标放在postbody上方右键,然后点击copy,接着点击copy outerhtml就可以获得这篇文章的html代码进行拷贝,接着我们在只要对这个字符串类型的html设置safe让浏览器把他当html代码执行就可以获得原来的文章样式
ps:不同的网站postbody的表现形式可能不一样
3、点赞点踩样式搭建
在点赞和评论两个功能搭建的时候,如果遇到功能复杂的我们就单独给他开设一个路由,开设的时候需要注意动态匹配的路由如果在静态匹配路由的上面,就会导致动态匹配的路由优先被匹配。因此跟函数的变量一样,需要我们进行区分,简单的写在前面。
在编写点赞点踩样式的时候我们检查博客园的网页直接偷他的样式即可。但是偷的时候除了偷他的html代码,我们还需要偷他检查界面右边的css样式代码。
当我们给后端传输点赞点踩的数据的时候,如果正常方式接受判断比较麻烦,需要分两种情况处理,但是我可以在前端设置一个事件,给两个点赞点踩的按钮添加相同的一个类,当这个类代表的按钮被点击的时候我们想定义一个变量判断是不是点击了点赞按钮,给他传入一个布尔值,这样我们传到后端的数据只需要写一个变量,后端进行判断的时候用json模块反序列化就可以得到布尔值类型的值。
往下就是一些逻辑判断,首先是判断是不是文章作者给自己点赞,然后判断是否已经点过赞或踩,接着判断是进行了点赞还是点踩,跟前面的登陆注册一样,用back_dict保存返回数据给前端,让前端根据code的值进行判断是否成功点赞了。
当操作结束后我们还想让网页上的数字也跟着变化,这里我们用的是ajax提交的数据,当接收到返回的结果后我们直接对对应的标签进行值的加减(需要先转换类型,通常都是字符串类型)
4 、文章评论功能
搭建评论框的样式有鸡哥需要注意的点,我们需要搭建一块文本框,同时上面要有一块区域,在文章有评论的时候需要给他展示出所有的评论,用几楼几楼的形式展示出来。
根据博客园的样式,我们添加评论的时候应该让评论展示区在添加后就可以展示出来,然后评论提交的方式也是用ajax提交的,但是跟点赞点踩一样,这里的url我们要自己额外开一个路由,因为评论的功能比较复杂,需要我们在视图函数中做很多的处理。
这里的评论我们只搭建到根评论,明天再对子评论进行设计。
文章内容页的网页代码
{% extends 'homePage.html' %}
{% block css %}
<link rel="stylesheet" href="media/css/{{ site_obj.site_theme }}/">
<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.gif') no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url('/static/img/downdown.gif') no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.clear {
clear: both;
}
.diggword {
margin-top: 5px;
margin-left: 0;
font-size: 12px;
color: #808080;
}
</style>
{% endblock %}
{% block title %}
{{ site_obj.site_title }}
{% endblock %}
{% block content %}
<div class="col-md-2">
{% load mytag %}
{% mymenu username %}
</div>
<div class="col-md-10">
<h2 class="text-center">{{ article_obj.title }}</h2>
{{ article_obj.content|safe }}
{# 文章点赞点踩样式开始#}
<div class="clearfix">
<div id="div_digg">
<div class="diggit upordown">
<span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span>
</div>
<div class="buryit upordown">
<span class="burynum" id="bury_count">{{ article_obj.down_num }}</span>
</div>
<div class="clear"></div>
<span style="color: red" id="d1"></span>
<div class="diggword" id="digg_tips">
</div>
</div>
</div>
{# 文章点赞点踩样式结束#}
{# 文章评论楼的渲染开始#}
<div class="comment_list">
<ul class="list-group">
{% for comment_obj in comment_list %}
<li class="list-group-item">
<span><a href="#">#{{ forloop.counter }}楼</a></span>
<span>{{ comment_obj.comment_time|date:'Y-m-d H:i' }}</span>
<span><a href="/{{ comment_obj.user.username }}/">{{ comment_obj.user.username }}</a></span>
<p>
{{ comment_obj.content }}
</p>
</li>
{% endfor %}
</ul>
</div>
{# 文章评论楼的渲染结束#}
{# 文章评论样式开始#}
{% if request.user.is_authenticated %}
<div class="comment_area">
<p><span class="glyphicon glyphicon-comment"></span>发表评论</p>
<textarea name="" id="comment" cols="30" rows="10" class="form-control"></textarea>
<button class="btn btn-primary" id="commentBtn">提交评论</button>
</div>
{% else %}
<p>
<a href="/register/">注册</a>
<a href="/login/">登录</a>
</p>
{% endif %}
{# 文章评论样式结束#}
</div>
{% endblock %}
{% block js %}
<script>
// 给点赞点踩图标绑定点击事件
$('.upordown').click(function () {
let currentEle = $(this);
let isUp = $(this).hasClass('diggit') // 判断标签是否含有某个class值 从而二选一区分赞和踩
// 发送ajax请求
$.ajax({
url: '/up_or_down/', // 点赞点踩有一定的逻辑 单独开设接口处理
type: 'post',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'article_pk': '{{ article_obj.pk }}',
'is_up': isUp,
},
success: function (args) {
if (args.code === 10000) {
currentEle.children().first().text(Number(currentEle.children().first().text()) + 1)
}
$('#d1').html(args.msg)
}
})
})
// 给提交评论的按钮绑定点击事件
$('#commentBtn').click(function () {
$.ajax({
url: '/comment/',
type: 'post',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'article_pk': '{{ article_obj.pk }}',
'content': $('#comment').val(),
},
success: function (args) {
}
})
})
</script>
{% endblock %}
对应的视图函数
def site_func(request, username, **kwargs):
# 查询个人站点是否存在
site_obj = models.Site.objects.filter(site_name=username).first()
if not site_obj:
return render(request, 'errorPage.html')
# 查询个人站点下所有的文章
article_queryset = models.Article.objects.filter(site=site_obj)
# 通过查看kwargs是否接受了参数,我们来判断当前网页是个人页,还是标签分类页
if kwargs:
# 在路由那,中间是分类的类别,最后是分类所用字段的id和创建时间,因此我们用condition来分辨他们的分类依据
condition = kwargs.get('condition')
params = kwargs.get('params')
# 这里是根据不同的分类得到筛选后的对象,返回给前端展示
if condition == 'category':
article_queryset = article_queryset.filter(category_id=params)
elif condition == 'tag':
article_queryset = article_queryset.filter(tags__pk=params)
else: # 年-月
year, month = params.split('-')
article_queryset = article_queryset.filter(create_time__year=year, create_time__month=month)
'''如果文章较多也应该添加分页器'''
return render(request, 'sitePage.html', locals())
def article_detail_func(request, username, article_id):
# 筛选谋篇具体的文章对象
article_obj = models.Article.objects.filter(site__site_name=username).filter(pk=article_id).first()
site_obj = models.Site.objects.filter(site_name=username).first()
'''这里也可以添加健壮性校验 防止用户自己瞎传数据'''
# 获取当前文章所有的评论数据
comment_list = models.Comment.objects.filter(article=article_obj)
return render(request, 'articleDetailPage.html', locals())
def up_or_down_func(request):
"""
1.校验用户是否登录
2.校验当前文章是否是当前用户自己的
3.校验当前文章是否已经被当前用户点过
4.创建点赞点踩记录(不要忘记文章表中的优化字段 同步自增)
"""
print(request.POST)
back_dict = {'code': 10000, 'msg': ''}
if request.method == 'POST':
if request.user.is_authenticated:
article_pk = request.POST.get('article_pk')
is_up = request.POST.get('is_up') # true 普通的字符串
article_obj = models.Article.objects.filter(pk=article_pk).first()
if not article_obj.site.userinfo == request.user:
is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
if not is_click:
is_up = json.loads(is_up) # 自动转换成python中布尔值
if is_up:
models.Article.objects.filter(pk=article_pk).update(up_num=F('up_num') + 1)
back_dict['msg'] = '点赞成功'
else:
models.Article.objects.filter(pk=article_pk).update(down_num=F('down_num') + 1)
back_dict['msg'] = '点踩成功'
models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
else:
back_dict['code'] = 10001
back_dict['msg'] = '您已经点过了'
else:
back_dict['code'] = 10002
back_dict['msg'] = '你个臭不要脸的 不能给自己点'
else:
back_dict['code'] = 10003
# from django.utils.safestring import mark_safe
# back_dict['msg'] = mark_safe('请先<a href="/login/">登录</a>')
back_dict['msg'] = '请先<a href="/login/">登录</a>'
return JsonResponse(back_dict)
@login_required
def comment_func(request):
back_dict = {'code': 10000, 'msg': ''}
if request.method == 'POST':
article_pk = request.POST.get('article_pk')
content = request.POST.get('content')
models.Article.objects.filter(pk=article_pk).update(comment_num=F('comment_num') + 1)
models.Comment.objects.create(user=request.user, article_id=article_pk, content=content)
back_dict['msg'] = '评论成功'
return JsonResponse(back_dict)
截至目前的所有路由
urlpatterns = [
path('admin/', admin.site.urls),
# 用户注册功能
path('register/', views.register_func, name='register_view'),
# 用户登录功能
path('login/', views.login_func, name='login_view'),
# 图片验证码相关功能
path('get_code/', views.get_code_func),
# 网址首页
path('home/', views.home_func, name='home_view'),
# 修改密码功能
path('set_pwd/', views.set_pwd_func),
# 注销登录功能
path('logout/', views.logout),
# 文章点赞点踩
path('up_or_down/', views.up_or_down_func),
# 文章评论
path('comment/', views.comment_func),
# 自定义暴露资源接口
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
# 个人站点接口
path('<str:username>/', views.site_func),
# 上述三个路由可以合并成一个路由
re_path('^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<params>.*?)/', views.site_func),
# 文章详情页
path('<str:username>/article/<int:article_id>/', views.article_detail_func),
]
二、作业
连续功能编写
ps:先理清逻辑再动手