Django blog项目《十七》:后台admin《热门文章功能实现》

本文详细介绍了在Django项目中实现热门文章功能的全过程,包括热门文章的展示、删除、修改和添加功能的实现。通过分析业务流程,配置urls,编写views视图逻辑,前端js处理以及html页面填充,实现了对热门文章的CRUD操作。

热门文章功能,实现热门文章的增、删、改、查功能的实现

一、分析

1. 业务流程

1.热门文章展示
  1. 业务流程

    • 从数据库中获取到数据然后传递给前端
    • 前端填充到页面中
  2. 请求方式、地址、参数

    • 请求方式:GET
    • 请求地址:/admin/hot_article/
    • 请求参数:无
2. 热门文章删除功能
  1. 业务流程

    • 从前端传递一个带有热门文章id的delete请求
    • 判断数据库中是否有该数据
    • 逻辑删除该热门文章,并返回数据到前端
  2. 请求方式、地址、参数

    1. 请求方式:DELETE

    2. 请求地址:/admin/hot_article/<int:hot_article_id>/

    3. 请求参数:

      参数名类型是否必传备注
      hot_article_idint必传路径参数
3. 热门文章修改功能
  1. 业务流程

    • 判断priority文章优先级是否和以前的一样
    • 判断priority是否在优先级规定的表内
    • 接收前端传来的数据
    • 判断该篇热门文章是否存在
    • 对热门文章的优先级进行修改
    • 返回数据到前端
  2. 请求方式、地址、参数

    1. 请求方式:PUT

    2. 请求地址:/admin/hot_article/<int:hot_article_id>/

    3. 请求参数:

      参数名类型是否必传备注
      hot_article_idint必传路径参数
      priorityint必传请求体里
  3. 前端功能实

    • 获取到优先级priority的值
    • 判断priority文章优先级是否和以前的一样
    • 判断priority是否在优先级规定的表内
    • 发起ajax PUT请求
      • 修改该条热门文章的信息
  4. 后端逻辑处理

    • 接收前端传来的数据
    • 判断该篇热门文章是否存在
    • 判断priority文章优先级是否和以前的一样
    • 判断priority是否在优先级规定的表内
    • 对该篇热门文章的优先级进行修改
    • 返回数据到前端
4. 热门文章添加功能
  1. 业务流程

    • 发送文章类型、热门文章优先级到前端
    • 构造点击文章类型时出现对应的所有该类型的文章
    • 判断该篇文章是否存在
    • 判断优先级是否满足要求
    • 判断优先级是否在优先级列表中
    • 创建新的热门文章
    • 返回数据到前端
  2. 请求方式、地址、参数

    1. 请求方式:POST

    2. 请求地址:/admin/hot_add/

    3. 请求参数:

      参数名类型是否必传备注
      article_idint必传请求体里
      priorityint必传请求体里
  3. 前端处理

    • 获取到文章id和热门文章优先级
    • 发送ajax POST请求
      • 跳转到热门文章列表页
  4. 后端逻辑

    • 构造文章类型、热门文章优先级到后端
    • 获取到前端传来的数据
    • 判断文章是否存在
    • 判断文章优先级是否满足要求
    • 判断热门文章优先级是否在优先级列表中
    • 创建热门文章
    • 返回数据到前端

二、热门文章展示、删除、修改功能实现

1. urls配置
from django.urls import path

from admin import views

app_name = "admin"

urlpatterns = [
   
    path("hot_article/", views.HotArticleView.as_view(), name="hot_article"),
    path("hot_article/<int:hot_article_id>/", views.HotArticleView.as_view(), name="hot_article_change"),
   
]
2. views视图逻辑处理
import json

from django.views import View
from django.shortcuts import render
from django.contrib.auth.mixins import PermissionRequiredMixin

from news import models
from utils.res_code.res_code import Code, error_map
from utils.res_code.json_function import to_json_data


# 热门文章删改查
class HotArticleView(PermissionRequiredMixin, View):
    """
    hot article add/view/delete/edit function view
    route:/admin/hot_article/  :view
    route:/admin/hot_article/<int:article_id>/  :delete and edit
    permissions:view_hotarticle/change_hotarticle/delete_hotarticle
    """
    # 设置权限
    permission_required = ("news.view_hotarticle", "news.change_hotarticle", "news.delete_hotarticle")
    raise_exception = True

    
    def get(self, request):
        """
        hot article view
        :param request:
        :return:
        """
        hot_articles = models.HotArticle.objects.only("id", "priority", "article__title",
                                                      "article__tag__name").filter(is_delete=False).order_by("priority")
        return render(request, 'admin/news/hot_article.html', locals())

    
    def put(self, request, hot_article_id):
        """
        hot article priority change
        :param request:
        :return:
        """
        # 获取亲阿杜那传来的数据
        try:
            json_data = request.body
            if not json_data:
                return to_json_data(errno=Code.PARAMERR, errmsg="参数错误")
            dict_data = json.loads(json_data)
        except Exception as e:
            return to_json_data(errno=Code.UNKOWNERR, errmsg="未知错误")
        
        # 获取到优先级
        try:
            priority = int(dict_data.get("priority"))
            if priority not in [num for num, word in models.HotArticle.PRI_CHOICES]:  # 判断输入的优先级是否在优先级表中
                return to_json_data(errno=Code.UNKOWNERR, errmsg="设置的优先级不在优先级表中")

        except Exception as e:
            logger.info("热门文章优秀级异常:{}".format(e))
            return to_json_data(errno=Code.PARAMERR, errmsg="热门文章优秀级设置错误")
        
        # 判断文章是否存在
        hot_article = models.HotArticle.objects.only("id").filter(id=hot_article_id).first()
        if not hot_article:
            return to_json_data(errno=Code.PARAMERR, errmsg="该热门文章不存在")
        
        # 判断优先级是否有修改
        if hot_article.priority == priority:
            return to_json_data(errno=Code.PARAMERR, errmsg="优先级未修改")
        
        # 保存
        hot_article.priority = priority
        hot_article.save(update_fields=["priority"])
        return to_json_data(errmsg="优先级修改成功")

    
    def delete(self, request, hot_article_id):
        """
        hot article delete
        :param request:
        :return:
        """
        hot_article = models.HotArticle.objects.only("id").filter(is_delete=False, id=hot_article_id).first()

        if hot_article:
            hot_article.is_delete = True
            hot_article.save()
            return to_json_data(errmsg="热门文章删除成功")
        else:
            return to_json_data(errno=Code.PARAMERR, errmsg="参数错误")
3. js前端逻辑实现

js里面使用到了两个js插件 fsweetalert.jsmessage.js

$(function () {  //增删改查
    let $editBtn = $(".btn-edit");
    let $delBtn = $(".btn-del");

    //热门文章删除
    $delBtn.click(function () {
        let _this = this;
        let sHotArticleId = $(this).parents("tr").data("id");
        let sHotArticleName = $(this).parents("tr").data("name");
        fAlert.alertConfirm({  //弹窗
           title:"确定删除" + sHotArticleName + "热门文章吗?",
            type:"error",
            confirmButtonText:"确认删除",
            cancelButtonText:"取消",
            confirmCallback:function confirmCallback() {
                $.ajax({
                    url:"/admin/hot_article/" + sHotArticleId +"/",
                    type:"DELETE",
                    dataType:"json",
                })
                    .done(function (res) {
                        if (res.errno === "200"){
                           message.showSuccess("成功删除 "+ sHotArticleName + " 热门文章!");
                            $(_this).parents("tr").remove()
                        }else{
                             swal.showInputError("删除失败:" + res.errmsg);
                        }
                    })
                    .fail(function () {
                        alert("服务器超时,请重试!")
                    })
            }
            }
        )
    });


    //热门文章修改
    $editBtn.click(function () {
        let _this = this;
        let sHotArticleId = $(this).parents("tr").data("id");
        let sHotArticleName = $(this).parents("tr").data("name");
        let sHotArticlePriority = $(this).parents("tr").data("priority");
        fAlert.alertOneInput({
            title:"编辑热门文章优先级",
            text:"您正在编辑 " + sHotArticleName + " 优先级",
            placeholder: "请输入优先级",
            value:sHotArticlePriority,
            confirmCallback:function callfirmCallback(inputval) {
                if (inputval === sHotArticlePriority){
                    swal.showInputError("优先级没有改变");
                    return false
                }else if($.inArray(inputval,["1", "2", "3"])){
                    swal.showInputError("优先级必须在1,2,3中选择");
                }

                let sDataParams={
                    "priority":inputval,
                };

                $.ajax({
                    url:"/admin/hot_article/" + sHotArticleId + "/",
                    type:"PUT",
                    data:JSON.stringify(sDataParams),
                    contentType: "application/json;charset=utf-8",
                    dataType:"json",
                })
                    .done(function (res) {
                        if (res.errno==="200"){
                            $(_this).parents("tr").find("td:nth-child(3)").text(inputval);  //更新标签
                            // $(_this).parent("tr").data("name",inputval); //更新data-name的值没有解决
                            swal.close();
                            message.showSuccess("优先级修改成功");
                        }else{
                           swal.showInputError("优先级修改失败:" + res.errmsg);
                        }
                    })
                    .fail(function () {
                        alert("服务器超时,请重试!")
                    })


            }
        })
    });




    // get cookie using jQuery
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            let cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                let cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }

    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    // Setting the token on the AJAX request
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
             if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
             }
        }
    });





});
4. html页面填充
{% extends 'admin/base/base.html' %}
{% load staticfiles %}

{% block title %}
    热门文章管理页面
{% endblock %}


{% block content_header %}
    热门文章管理
{% endblock %}

{% block content_header_brief %}
    热门文章增删改查
{% endblock %}


{% block content %}
    <div class="content">
        <div class="container-fluid">
            <div class="row">
                <div class="col-md-12 col-xs-12 col-sm-12">
                    <div class="box box-primary">
                        <div class="box-header"><a href="
{% url 'admin:hot_add' %}">
                            <button class="btn btn-primary fa-pull-right" id="btn-add">添加热门文章</button></a>
                        </div>
                        <br>
                        <br>
                        <div class="box-body">
                            <table class="table table-bordered table-hover">
                                <thead>
                                <tr>
                                    <th>文章名称</th>
                                    <th>文章标签</th>
                                    <th>优先级</th>
                                    <th>操作</th>
                                </tr>
                                </thead>
                                <tbody>
                                {% for hot_article in hot_articles %}
                                    <tr data-id="{{ hot_article.id }}"
                                        data-name="{{ hot_article.article.title }}" data-priority="{{ hot_article.priority }}">
                                        <td><a href="{% url 'news:article_detail' hot_article.id  %}"
                                        >{{ hot_article.article.title }}</a></td>
                                        <td>{{ hot_article.article.tag.name }}</td>
                                        <td>{{ hot_article.priority }}</td>
                                        <td>
                                            <button class="btn btn-xs btn-warning btn-edit">
                                                编辑
                                            </button>
                                            <button class="btn btn-xs btn-danger btn-del">
                                                删除
                                            </button>
                                        </td>
                                    </tr>
                                {% endfor %}

                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
{% endblock %}



{% block script %}
    <script src="{% static 'js/admin/news/hot_article.js' %}"></script>
{% endblock %}

三、热门文章添加功能实现

1. urls配置
from django.urls import path

from admin import views

app_name = "admin"

urlpatterns = [
   
    path("hot_add/", views.HotArticleAddView.as_view(), name="hot_add"),
    path("tags/<int:tag_id>/article/", views.ArticleCategoryView.as_view(), name="article_add"),
   
]
2. views视图逻辑处理
import json
from collections import OrderedDict  # 转化为字典

from django.views import View
from django.shortcuts import render
from django.http import JsonResponse
from django.contrib.auth.mixins import PermissionRequiredMixin

from news import models
from utils.res_code.res_code import Code, error_map
from utils.res_code.json_function import to_json_data


# 热门文章增:需要单独写一个类视图来处理
class HotArticleAddView(PermissionRequiredMixin, View):
    """
    hot article add view
    route:/admin/hot_article/add/
    permissions:view_hotarticle/add_hotarticle
    """
    permission_required = ("news.view_hotarticle", "news.add_hotarticle")
    raise_exception = True

    def get(self, request):
        """
        传递给前端:tag、article、priority
        :param request:
        :return:
        """
        tags = models.Tags.objects.only("id", "name").filter(is_delete=False).order_by("-id", "-update_time")
        # priority_dict = {k: v for k, v in models.HotArticle.PRI_CHOICES}    # 转化为字典
        priority_dict = OrderedDict(models.HotArticle.PRI_CHOICES)

        return render(request, 'admin/news/hot_add.html', context={"tags": tags, "priority_dict": priority_dict})

    def post(self, request):
        try:
            json_data = request.body
            if not json_data:
                return to_json_data(erno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

            dict_data = json.loads(json_data.decode("utf-8"))
        except Exception as e:
            logger.info("热门文章添加未知错误:{}".format(e))
            return to_json_data(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])

        try:
            article_id = int(dict_data.get("article_id"))
        except Exception as e:
            logger.info("热门文章id错误:{}".format(e))
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

        if not models.Articles.objects.only("id").filter(is_delete=False, id=article_id).exists():
            return to_json_data(errno=Code.PARAMERR, errmsg="文章不存在")

        try:
            priority = int(dict_data.get("priority"))
        except Exception as e:
            logger.info("热门文章优先级错误:{}".format(e))
            return to_json_data(errno=Code.PARAMERR, errmsg="热门文章优先级设置错误")

        priority_ls = [index for index, word in models.HotArticle.PRI_CHOICES]

        if priority not in priority_ls:
            return to_json_data(errno=Code.PARAMERR, errmsg="热门文章优先级设置错误")

        # 创建热门文章
        hot_article_tuple = models.HotArticle.objects.get_or_create(article_id=article_id)
        hot_article, is_create = hot_article_tuple
        hot_article.priority = priority
        hot_article.save(update_fields=["priority"])

        return to_json_data(errmsg="热门文章优先级添加成功")


    
# 文章分类:关联点击文章分类时显示对应的文章
class ArticleCategoryView(PermissionRequiredMixin, View):
    """
    article category show
    route:/admin/tags/<int:tag_id>/article/
    permissions:view_tags/view_articles
    """

    permission_required = ("news.view_tags","news.view_articles")
    raise_exception = True

    def get(self, request, tag_id):
        articles = models.Articles.objects.values("id", "title").filter(is_delete=False, tag_id=tag_id)
        if not articles:
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

        article_ls = [article for article in articles]
        data = {
            "articles": article_ls
        }

        return to_json_data(data=data)
3. js前端逻辑实现
$(function () {
    let $tagSelect = $("#category-select");   // 获取选择分类标签元素
    let $articleSelect = $("#article-select");      // 获取选择文章标签元素
    let $saveBtn = $('#save-btn');            // 获取保存按钮元素

    // 选择文章不同类别,获取相应的文章
    $tagSelect.change(function () {
        // 获取当前选中的下拉框的value
        let sTagId = $(this).val();
        if (sTagId === '0') {
            $articleSelect.children('option').remove();
            $articleSelect.append(`<option value="0">--请选择文章--</option>`);
            return
        }
        // 根据文章分类id向后端发起get请求
        $.ajax({
            url: "/admin/tags/" + sTagId + "/article/",  // url尾部需要添加/
            type: "GET",
            dataType: "json",
        })
            .done(function (res) {
                if (res.errno === "200") {

                    $articleSelect.children('option').remove();
                    $articleSelect.append(`<option value="0">--请选择文章--</option>`);
                    res.data.articles.forEach(function (article) {
                        let content = `<option value="${article.id}">${article.title}</option>`;
                        $articleSelect.append(content)
                    });

                } else {
                    // swal.showInputError(res.errmsg);
                    fAlert.alertErrorToast(res.errmsg);
                }
            })
            .fail(function () {
                message.showError('服务器超时,请重试!');
            });
    });
    

    // 点击保存按钮执行的事件
    $saveBtn.click(function () {
        
        let priority = $("#priority").val();// 获取优先级
        // 获取下拉框中选中的文章标签id 和 文章id
        let sTagId = $tagSelect.val();
        let sArticleId = $articleSelect.val();
    //     // 打印值
    //     console.log(`
    //       priority(优先级): ${priority}
    //       tagId(文章标签id): ${sTagId}
    //       newsId(文章id): ${sArticleId}
    // `);
        // 判断是否为 0, 表示在第一个 未选择
        if (sTagId !== '0' && sArticleId !== '0' && priority !== '0') {

            let sDataParams = {
                "priority": priority,
                "article_id": sArticleId
            };

            $.ajax({
                url: "/admin/hot_add/",  // 请求地址
                type: "POST", // 请求方式
                data: JSON.stringify(sDataParams), 
                contentType: "application/json; charset=utf-8",// 请求内容的数据类型(前端发给后端的格式)
                dataType: "json",// 响应数据的格式(后端返回给前端的格式)
            })
                .done(function (res) {
                    if (res.errno === "200") {
                        message.showSuccess("热门文章创建成功");

                        setTimeout(function () {
                            window.location.href = '/admin/hot_article/';
                        }, 800)
                    } else {
                        // swal.showInputError(res.errmsg);
                        message.showError("热门文章创建失败" + res.errmsg);
                    }
                })

                .fail(function () {
                    message.showError('服务器超时,请重试!');
                });

        } else {
            message.showError("文章分类、文章以及优先级都要选!");
        }
    });

// get cookie using jQuery
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            let cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                let cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }

    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

// Setting the token on the AJAX request
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
            }
        }
    });

});


4. html页面填充
{% extends 'admin/base/base.html' %}
{% load staticfiles %}

{% block title %}
    添加热门文章
{% endblock %}

{% block content_header %}
    添加热门文章
{% endblock %}

{% block content_header_brief %}
    创建热门文章
{% endblock %}


{% block content %}
    <div class="content">
        <div class="container-fluid">
            <div class="box box-primary">
                <div class="box-body">
                    <div class="form-horizontal">
                        <div class="form-group">
                            <label for="category-select" class="col-md-2 col-sm-2 control-label">选择文章</label>
                            <div class="col-md-2 col-sm-3">
                                <select name="category" id="category-select" class="form-control input-md">
                                    <option value="0">--请选择文章分类--</option>

                                    {% for tag in tags %}
                                        <option value="{{ tag.id }}">{{ tag.name }}</option>
                                    {% endfor %}

                                </select>
                            </div>
                            <br>
                            <div class="col-md-8 col-sm-7">
                                <label for="article-select" style="display: none;"></label>
                                <select name="article" class="form-control input-md" id="article-select">
                                    <option value="0">--请选择文章--</option>
                                </select>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="priority" class="col-md-2 col-sm-2 control-label">选择优先级</label>

                            <div class="col-md-2 col-sm-3">
                                <select name="priority" id="priority" class="form-control input-md">
                                    <option value="0">--请选择优先级--</option>

                                    {% for key, value in priority_dict.items %}
                                        <option value="{{ key }}">{{ value }}</option>
                                    {% endfor %}

                                </select>

                            </div>
                        </div>
                    </div>
                    <div class="box-footer">
                        <a href="javascript:void(0);" class="btn btn-primary fa-pull-left" id="save-btn">保存</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}


{% block script %}

    <script src="{% static 'js/admin/news/hot_add.js' %}"></script>
{% endblock %}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值