BBS项目day04 文章详情页、点赞点菜、评论功能

一、路由

from django.contrib import admin
from django.urls import path, re_path
from app01 import views
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    # 注册
    path('register/', views.register),
    # 登录
    path('login/', views.login),
    # 验证码
    path('get_code/', views.get_code),
    # 首页路由
    path('home/', views.home),
    # 退出系统
    path('logout/', views.logout),
    # 修改密码
    path('set_password/', views.set_password),
    # 放开media文件夹
    re_path('media/(?P.*)', serve, {'document_root': settings.MEDIA_ROOT}),

    # 文章详情页
    re_path('(?P\w+)/(?P\d+)', views.article_detail),

    # 点赞点彩
    path('up_and_down/', views.up_and_down),
    # 评论功能
    path('comment/', views.comment),

    # re_path('(?P\w+)/category/(\d+)', views.site),
    # re_path('(?P\w+)/tag/(\d+)', views.site),
    # re_path('(?P\w+)/archive/(\w+)', views.site),

    # 优化以上三个路由
    re_path('(?P\w+)/(?Pcategory|tag|archive)/(?P.*)', views.site),
    re_path('(?P\w+)', views.site),
]


二、文章详情页

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            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;
        }

        .clearfix:focus {
            content: '';
            display: block;
            clear: both;
        }
    style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类div>
            <div class="panel-body">
                {% for category in category_list %}
                    
                    <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})a>p>
                    
                    {#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})a>p>#}
                {% endfor %}
            div>
        div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <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 %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})a>p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}
                        ({{ date.count_article_nums }})a>p>
                {% endfor %}
            div>
        div>
    div>

    <div class="col-md-9">
        <h3 style="color: #399ab2;">{{ article_detail.title }}h3>
        <div class="content">
            {{ article_detail.content|safe }}
        div>
    div>

{% endblock %}

{% block js %}
    <script>
        // 11647089,'Digg'分别表示文章id和标志flag
        // 思路1:
        {% comment %}function votePost(id, flag) {
            is_up = flag === 'Digg' ? 0 : 1;
        }{% endcomment %}

        // 思路2:
        $(".active").click(function () {
            let is_up = $(this).hasClass('diggit');
            // 文章id
            var article_id = '{{ article_detail.pk }}';
            var _this = $(this);
            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function () {
                    // 将标签放入文本,要么是text,要么是html
                    if (res === 200) {
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞就让点赞数加1,如果是点踩就让点踩数加1
                        // 注意:在text()中加数据就是赋值,空就是获取值
                        let old_num = _this.children().text();
                        // 此时的 old_num 是string类型,需要转类型
                        {#_this.children().text(parseInt(old_num)+1);#}
                        //或者使用 Number
                        _this.children().text(Number(old_num) + 1);
                    } else {
                        $("#digg_tips").html(res.msg);
                    }
                },
            });
        });

    script>
{% endblock %}


2.后端

# 文章详情页
def article_detail(request, username, article_id):
    print(article_id)  # 1

    user_obj = models.UserInfo.objects.filter(username=username).first()
    print(user_obj)

    if not user_obj:
        '''
            图片防盗链:通过 Referer参数判断,
            通过这个参数就可以知道你当前的地址是从哪个网页调过来的,然后做验证   
        '''
        return render(request, '404.html')

    # 查询用户自己的所有文章(过滤当前站点的文章)
    blog = user_obj.blog
    article_detail = models.Article.objects.filter(pk=article_id).first()

    category_list = models.Category.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    tag_list = models.Tag.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    date_list = models.Article.objects.annotate(
        month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
        count_article_nums=Count('pk')).values('month', 'count_article_nums')

    # 查询所有的评论列表
    comment_list = models.Comment.objects.filter(article_id=article_id).all()

    return render(request, 'article_detail.html', locals())


三、点赞点菜

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            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;
        }

        .clearfix:focus {
            content: '';
            display: block;
            clear: both;
        }
    style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类div>
            <div class="panel-body">
                {% for category in category_list %}
                    
                    <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})a>p>
                    
                    {#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})a>p>#}
                {% endfor %}
            div>
        div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <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 %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})a>p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}
                        ({{ date.count_article_nums }})a>p>
                {% endfor %}
            div>
        div>
    div>

    <div class="col-md-9">
        <h3 style="color: #399ab2;">{{ article_detail.title }}h3>
        <div class="content">
            {{ article_detail.content|safe }}
        div>

        
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit active" onclick="votePost(11647089,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}span>
                div>
                <div class="buryit active" onclick="votePost(11647089,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}span>
                div>
                <div class="clear">div>
                <div class="diggword" id="digg_tips" style="color: red;">div>
            div>
        div>
        

    div>

{% endblock %}

{% block js %}
    <script>
        // 11647089,'Digg'分别表示文章id和标志flag
        // 思路1:
        {% comment %}function votePost(id, flag) {
            is_up = flag === 'Digg' ? 0 : 1;
        }{% endcomment %}

        // 思路2:
        $(".active").click(function () {
            let is_up = $(this).hasClass('diggit');
            // 文章id
            var article_id = '{{ article_detail.pk }}';
            var _this = $(this);
            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function () {
                    // 将标签放入文本,要么是text,要么是html
                    if (res === 200) {
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞就让点赞数加1,如果是点踩就让点踩数加1
                        // 注意:在text()中加数据就是赋值,空就是获取值
                        let old_num = _this.children().text();
                        // 此时的 old_num 是string类型,需要转类型
                        {#_this.children().text(parseInt(old_num)+1);#}
                        //或者使用 Number
                        _this.children().text(Number(old_num) + 1);
                    } else {
                        $("#digg_tips").html(res.msg);
                    }
                },
            });
        });

        
    script>
{% endblock %}


2.后端

# 点赞点彩
def up_and_down(request):
    '''
    分析点赞点彩的实现逻辑:
        1.必须判断用户是否登陆了。如果没有则在前端页面显示登录
        2.若是第一次登录:
            2.1 点赞数加 1
            2.2 在页面上显示点赞成功
        3.如果已经点击过,就提示不让他再点了
        4.如果是第一次点击,应该在处理哪些逻辑
            4.1 肯定需要在点赞点彩表中增加一条记录
            4.2 还需要更新文章中的up_num或者down_num字段
        5. 取消点赞或者点彩功能-----》收藏
    :param request:
    :return:
    '''
    if request.method == 'POST':
        back_dict = {'code': 200, 'msg': '支持成功'}

        # 1.接收参数
        is_up = request.POST.get('is_UP')  # str
        is_up = json.loads(is_up)
        article_id = request.POST.get('article_id')

        # 2.判断用户是否登录
        if not request.session.get('username'):
            back_dict['code'] = 1400
            back_dict['msg'] = '请先登录'
            return JsonResponse(back_dict)

        # 3.验证参数
        # 4.判断是否已经点赞过了
        res = models.UpAndDown.objects.filter(article_id=article_id, user_id=request.session.get('id')).first()
        if res:
            back_dict['code'] = 1401
            back_dict['msg'] = '你已经支持过了'
            return JsonResponse(back_dict)
        # 5.处理业务逻辑
        # 操作up_and_down, article
        if is_up:
            models.Article.objects.create(pk=article_id).update(up_num=F('up_num') + 1)
        else:
            models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
            back_dict['msg'] = '支持成功'

        # 查询出来要么点赞,要么点踩
        models.UpAndDown.objects.create(is_up=is_up, article_id=article_id, user_id=request.session.get('id'))
        return JsonResponse(back_dict)

四、评论功能

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            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;
        }

        .clearfix:focus {
            content: '';
            display: block;
            clear: both;
        }
    style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类div>
            <div class="panel-body">
                {% for category in category_list %}
                    
                    <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})a>p>
                    
                    {#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})a>p>#}
                {% endfor %}
            div>
        div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <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 %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})a>p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}
                        ({{ date.count_article_nums }})a>p>
                {% endfor %}
            div>
        div>
    div>

    <div class="col-md-9">
        <h3 style="color: #399ab2;">{{ article_detail.title }}h3>
        <div class="content">
            {{ article_detail.content|safe }}
        div>

        
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit active" onclick="votePost(11647089,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}span>
                div>
                <div class="buryit active" onclick="votePost(11647089,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}span>
                div>
                <div class="clear">div>
                <div class="diggword" id="digg_tips" style="color: red;">div>
            div>
        div>
        

        
        <div class="comment_list">
            <h3><span class="glyphicon glyphicon-comment">span>评论列表h3>
            <ul class="list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <span style="margin-right: 10px;"># {{ forloop.counter }}楼span>
                        <span style="margin-right: 10px;">{{ comment.comment_time }}span>
                        
                        {#                        <span style="margin-right: 10px;">{{ comment.parent.user.username }}span>#}
                        
                        <span style="margin-right: 10px;">{{ comment.user.username }}span>
                        
                        <span style="margin-right: 10px;" class="pull-right">
                            <a href="javascript:;" comment_username="{{ comment.user.username }}"
                               comment_id="{{ comment.pk }}" class="repay">回复a>
                        span>
                        <div class="content" style="margin-left: 14px;">
                            {% if comment.parent %}
                                {{ comment.content }}
                            {% else %}
                                
                                <p>@ {{ comment.parent.user.username }}p>
                                <p>{{ comment.content }}p>
                            {% endif %}
                        div>
                    li>
                {% endfor %}
            ul>
        div>
        

        
        <div class="comment">
            <p><span class="glyphicon glyphicon-comment">span>发表评论p>
            <p>
                <textarea name="" id="content" cols="30" rows="10">textarea>
            p>
            <p>
                <button class="btn btn-success comment_content">提交评论button>
            p>
        div>
        

    div>

{% endblock %}

{% block js %}
    <script>
        // 11647089,'Digg'分别表示文章id和标志flag
        // 思路1:
        {% comment %}function votePost(id, flag) {
            is_up = flag === 'Digg' ? 0 : 1;
        }{% endcomment %}

        // 思路2:
        $(".active").click(function () {
            let is_up = $(this).hasClass('diggit');
            // 文章id
            var article_id = '{{ article_detail.pk }}';
            var _this = $(this);
            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function () {
                    // 将标签放入文本,要么是text,要么是html
                    if (res === 200) {
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞就让点赞数加1,如果是点踩就让点踩数加1
                        // 注意:在text()中加数据就是赋值,空就是获取值
                        let old_num = _this.children().text();
                        // 此时的 old_num 是string类型,需要转类型
                        {#_this.children().text(parseInt(old_num)+1);#}
                        //或者使用 Number
                        _this.children().text(Number(old_num) + 1);
                    } else {
                        $("#digg_tips").html(res.msg);
                    }
                },
            });
        });

        // 定义一个全局变量 parent_id
        // 若为空则是根评论,有值就是子评论
        var parent_id = null;

        <!-- 评论功能开始 -->
        $(".comment_content").click(function () {
            // 1.获取参数
            var article_id = '{{ article_detail.pk }}';

            // 2.获取评论的内容
            var content = $('#content').val();

            // 3.判断是根评论还是子评论
            if (parent_id) {
                // 有值就是子评论
                // indexOf 若匹配到了则返回它在字符串中的位置
                let sub_number = content.indexOf('\n' + '');
                // 拿到截取之后的内容
                content = content.slice(sub_number);

            }

            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    article_id: article_id,
                    content: content,
                    parent_id: parent_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    // 做评论内容的临时渲染
                    // 注意:反引号是 ES6语法中的模板语法

                    var username = '{{ request.session.username }}';
                    var html = `
  • ${username}
    ${content}
  • `
    $(".list-group").append(html) //清空内容 $('#content').val('') }, }); }); <!-- 评论功能结束 --> <!-- 子评论功能开始 --> $(".repay").click(function () { // 获取自定义属性comment_username let comment_username = $(this).attr('comment_username'); // 获取子评论的 parent_id parent_id = $(this).attr("comment_id"); // 根据评论框 textarea的id属性,设置回复格式焦点,并且换行 $("#content").val('@' + comment_username + '\n').focus(); }); <!-- 子评论功能结束 -->
    script> {% endblock %}

    2.后端

    
    # 评论功能
    def comment(request):
        '''
        分析评论的逻辑:
            1.登录之后才能评论
            2.评论的内容要入库
                1.操作文章表,
                2.评论表
        :param request:
        :return:
        '''
        back_dict = {'code': 200, 'msg': '支持成功'}
    
        # 1.接收参数
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')
    
        # 2.判断用户是否登录
        if not request.session.get('username'):
            back_dict['code'] = 1404
            back_dict['msg'] = '请先登录之后再评论'
            return JsonResponse(back_dict)
    
        # 加事务
        from django.db import transaction
        try:
            with transaction.atomic():
                # 操作文章表
                models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                # 操作评论表
                models.Comment.objects.create(content=content, article_id=article_id,
                                              parent_id=parent_id, user_id=request.session.get('id'))
        except:
            # 加入日志
            ...
            transaction.rollback()
    
        return JsonResponse(back_dict)
    
    

    你可能感兴趣的:(python01,django,django,python,ajax,windows,pycharm)