15 后台站点一(标签增删改查)

获取静态站点模板

使用需要的组件

  • 源文件非常大,按需索取即可
  • 创建 templates/admin/base 文件夹,将下载的文件夹中 starter.html 页面复制粘贴,放到 base 文件夹中,修改名字为 base.html
  • 将不需要的组件删除
  • 创建 static/js/admin/base 文件夹,static/css/admin/base 文件夹和 static/css/admin/fonts 文件夹,将需要的 js,css,front 文件从下载的源文件夹中分别复制粘贴,放到对应的项目静态文件夹内
  • 创建 static/images/admin/base 文件夹,将用户图像文件放置其中

模板抽取

templates/admin/base/base.html

{% load static %}
<!DOCTYPE html>
<!--
This is a starter template page. Use this page to start your new project from
scratch. This page gets rid of all links and provides the needed markup only.
-->
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>
      {% block titile %}

      {% endblock %}
  </title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
  <link rel="stylesheet" href="{% static 'css/admin/base/bootstrap.min.css' %}">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="{% static 'css/admin/base/font-awesome.min.css' %}">
  <!-- Ionicons -->
  <link rel="stylesheet" href="{% static 'css/admin/base/ionicons.min.css' %}">
  <!-- Theme style -->
  <link rel="stylesheet" href="{% static 'css/admin/base/AdminLTE.min.css' %}">
  <!-- AdminLTE Skins. We have chosen the skin-blue for this starter
        page. However, you can choose any other skin. Make sure you
        apply the skin class to the body tag so the changes take effect. -->
  <link rel="stylesheet" href="{% static 'css/admin/base/skin-blue.min.css' %}">


  <link rel="stylesheet" href="{% static 'css/admin/base/sweetalert.css' %}">
  <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
  <!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
  <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  <![endif]-->

  <!-- Google Font -->
{#  <link rel="stylesheet"#}
{#        href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">#}


    {% block link %}
    {% endblock %}
</head>
<!--
BODY TAG OPTIONS:
=================
Apply one or more of the following classes to get the
desired effect
|---------------------------------------------------------|
| SKINS         | skin-blue                               |
|               | skin-black                              |
|               | skin-purple                             |
|               | skin-yellow                             |
|               | skin-red                                |
|               | skin-green                              |
|---------------------------------------------------------|
|LAYOUT OPTIONS | fixed                                   |
|               | layout-boxed                            |
|               | layout-top-nav                          |
|               | sidebar-collapse                        |
|               | sidebar-mini                            |
|---------------------------------------------------------|
-->
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">

  <!-- Main Header -->
  <header class="main-header">

    <!-- Logo -->
    <a href="{% url 'news:index' %}" class="logo">
      <!-- mini logo for sidebar mini 50x50 pixels -->
      <span class="logo-mini"><b>P</b>y</span>
      <!-- logo for regular state and mobile devices -->
      <span class="logo-lg"><b>Admin</b></span>
    </a>

    <!-- Header Navbar -->
    <nav class="navbar navbar-static-top" role="navigation">
      <!-- Sidebar toggle button-->
      <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
        <span class="sr-only">Toggle navigation</span>
      </a>
      <!-- Navbar Right Menu -->
      <div class="navbar-custom-menu">
        <ul class="nav navbar-nav">
          <!-- User Account Menu -->
          <li class="dropdown user user-menu">
            <!-- Menu Toggle Button -->
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
              <!-- The user image in the navbar-->
              <img src="{% static 'images/taka.jpg' %}" class="user-image" alt="User Image">
              <!-- hidden-xs hides the username on small devices so only the image appears. -->
              <span class="hidden-xs">{{ request.user }}</span>
            </a>
            <ul class="dropdown-menu">
              <!-- The user image in the menu -->
              <li class="user-header">
                <img src="{% static 'images/taka.jpg' %}" class="img-circle" alt="User Image">

                <p>
                  学习使我快乐!!!
                  <small>2019.5.5</small>
                </p>
              </li>

              <!-- Menu Footer-->
              <li class="user-footer">
                <div class="pull-left">
                  <a href="#" class="btn btn-default btn-flat">个人详情</a>
                </div>
                <div class="pull-right">
                  <a href="#" class="btn btn-default btn-flat">登出</a>
                </div>
              </li>
            </ul>
          </li>
        </ul>
      </div>
    </nav>
  </header>
  <!-- Left side column. contains the logo and sidebar -->
  <aside class="main-sidebar">

    <!-- sidebar: style can be found in sidebar.less -->
    <section class="sidebar">

      <!-- Sidebar user panel (optional) -->
      <div class="user-panel">
        <div class="pull-left image">
          <img src="{% static 'images/taka.jpg' %}" class="img-circle" alt="User Image">
        </div>
        <div class="pull-left info">
          <p>Taka</p>
          <!-- Status -->
          <a href="#"><i class="fa fa-circle text-success"></i> happy</a>
        </div>
      </div>


      <!-- Sidebar Menu -->
      <ul class="sidebar-menu" data-widget="tree">
        <li class="header">头部</li>
        <!-- Optionally, you can add icons to the links -->
        <li class="active"><a href="#"><i class="fa fa-user"></i> <span>个人信息</span></a></li>

        <li class="header">文章相关</li>
        <li class="treeview">
          <a href="#"><i class="fa fa-book"></i> <span>文章</span>
            <span class="pull-right-container">
                <i class="fa fa-angle-left pull-right"></i>
              </span>
          </a>
          <ul class="treeview-menu">
            <!-- 文章标签分类 start -->
            <li>
              <a href="{% url 'admin:tags' %}">
{#              <a href="#">#}
                <i class="fa fa-tags"></i>
                <span>文章标签分类</span>
              </a>
            </li>
            <!-- /.文章标签分类 end -->

            <!-- 文章发布 start -->
            <li>
              <a href="{% url 'admin:news_pub' %}">
                <i class="fa fa-newspaper-o"></i>
                <span>文章发布</span>
              </a>
            </li>
            <!-- /.文章发布 end -->

            <!-- 文章管理 start -->
            <li>
              <a href="{% url 'admin:news_manage' %}">
                <i class="fa fa-cogs"></i>
                <span>文章管理</span>
              </a>
            </li>
            <!-- /.文章管理 end -->

            <!-- Hot文章管理 start -->
            <li>
              <a href="{% url 'admin:hotnews' %}">
                <i class="fa fa-rocket"></i>
                <span>Hot文章管理</span>
              </a>
            </li>
            <!-- /.Hot文章管理 end -->

            <!-- 轮播图管理 start -->
            <li>
              <a href="{% url 'admin:banners_manage' %}">
                <i class="fa fa-file-picture-o"></i>
                <span>轮播图管理</span>
              </a>
            </li>
            <!-- /.轮播图管理 end -->
      </ul>
        </li>>

        <li class="header">文档相关</li>
        <li class="active"><a href="{% url 'admin:docs_manage' %}"><i class="fa fa-book"></i> <span>文档管理</span></a></li>
        <li class="active"><a href="{% url 'admin:docs_pub' %}"><i class="fa fa-cog"></i> <span>文档发布</span></a></li>

        <li class="header">在线课堂</li>
        <li class="active"><a href="{% url 'admin:courses_manage' %}"><i class="fa fa-file-movie-o"></i> <span>课程管理</span></a></li>
        <li class="active"><a href="{% url 'admin:courses_pub' %}"><i class="fa fa-film"></i> <span>课程发布</span></a></li>

        <li class="header">权限管理</li>
        <li class="active"><a href="{% url 'admin:groups_manage' %}"><i class="fa fa-group "></i> <span>组管理</span></a></li>
        <li class="active"><a href="{% url 'admin:groups_add' %}"><i class="fa fa-user-plus"></i> <span>组创建</span></a></li>
        <li class="active"><a href="{% url 'admin:users_manage' %}"><i class="fa fa-male"></i> <span>用户管理</span></a></li>
      <!-- /.sidebar-menu -->

      </ul>>
    </section>
    <!-- /.sidebar -->
  </aside>

  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    <section class="content-header">
      <h1>
        {% block content_header %}
        {% endblock %}
        <small>
            {% block header_option_desc %}
            {% endblock %}
        </small>
      </h1>
    </section>

    <!-- Main content -->
    <section class="content container-fluid">

      <!--------------------------
        | Your Page Content Here |
        -------------------------->
        {% block content %}

        {% endblock %}

    </section>
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->

  <!-- Main Footer -->
  <footer class="main-footer">
    <!-- To the right -->
    <div class="pull-right hidden-xs">
      人生苦短,我用python!
    </div>
    <!-- Default to the left -->
    <strong>Copyright &copy; 2019 <a href="#">Company</a>.</strong> All rights reserved.
  </footer>

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<!-- jQuery 3 -->
<script src="{% static 'js/admin/base/jquery.min.js' %}"></script>
<!-- Bootstrap 3.3.7 -->
<script src="{% static 'js/admin/base/bootstrap.min.js' %}"></script>
<!-- AdminLTE App -->
<script src="{% static 'js/admin/base/adminlte.min.js' %}"></script>


<!-- 自定义的导入 -->
<script src="{% static 'js/admin/base/message.js' %}"></script>
<script src="{% static 'js/admin/base/fsweetalert.js' %}"></script>
<script src="{% static 'js/admin/base/sweetalert.min.js' %}"></script>


{% block script %}

{% endblock %}

<!-- Optionally, you can add Slimscroll and FastClick plugins.
     Both of these plugins are recommended to enhance the
     user experience. -->
</body>
</html>

给网站添加 favicon.ico 头像

  • 将 favicon.ico 图片复制粘贴到项目 static/images 文件夹中
  • 在前台和后台站点中的 base 模板页上放加入如下代码
    <link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>

admin app

  • 创建 admin 应用,用于实现后台管理功能

新建 app

python manage.py startapp admin

settings.py

  • INSTALLED_APPS 中添加 admin app,将模板自带的 admin 注释掉
INSTALLED_APPS = [
    # 'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.course',
    'apps.users',
    'apps.news',
    'apps.doc',
    'apps.verifications',
    'haystack',
    'apps.admin'
]

web_prv/urls.py

  • 添加 apps.admin.urls,将自带的 admin 路径注释掉
# from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
#404就是路径问题
urlpatterns = [
    # path('admin/', admin.site.urls),
    path('',include('apps.news.urls')),
    path('users/',include('apps.users.urls')),
    path('doc/',include('apps.doc.urls')),
    path('course/',include('apps.course.urls')),
    path('',include('apps.verifications.urls')),
    path('admin/',include('apps.admin.urls')),

]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

apps/admin/urls.py

from apps.admin import views
from django.urls import path


app_name = 'admin'

urlpatterns = [
    path('',views.IndexView.as_view(),name='index'),
	path('tags/',views.TagManagerView.as_view(),name='tags'),
	path('tags/<int:tag_id>/',views.TagEditView.as_view(),name='tag_edit')

]

apps/admin/views.py

import json

from django.db.models import Count
from django.shortcuts import render
from django.views import View

# Create your views here.
from apps.news import models

from utils.json_fun import to_json_data
from utils.res_code import Code, error_map


class IndexView(View):
    """

    """
    def get(self,request):
        """

        :param request:
        :return:
        """
        return render(request,'admin/index/index.html')


class TagManagerView(View):
    """
    /admin/tag/
    """
    def get(self,request):
        """
        要返回给前端: tag_name news_name
        :param request:
        :return:
        """
        #分组查询 annotate 配合 values 使用     values 指定输出的内容(id: ,name: ),是字典格式
         #按标签名(tag_name)和 id(tag_id) 分组,统计每个标签(Tag)中 news(外键关联,
        # 通过 news的主键 id 统计) 的数量,并且按降序排列
        tags = models.Tag.objects.select_related('news').values('id','name').annotate(num_news = \
                  Count('news')).filter(is_delete=False).order_by('-num_news','update_time')
        return render(request,'admin/news/tag_manage.html',locals())

    def post(self,request):
        """
        get_or_create(defaults=None, **kwargs)

         一个通过给出的kwargs 来查询对象的便捷方法(如果你的模型中的所有字段都有默认值,可以为空),需要的话创建一个对象。
         返回一个由(object, created)组成的元组,元组中的object 是一个查询到的或者是被创建的对象,
         created 是一个表示是否创建了新的对象的布尔值。

        :param request:
        :return:
        """
        # 1. 获取参数
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        #从前端传进来的要修改的标签名
        tag_name = dict_data.get('name')
        if tag_name:
            # tag_name = tag_name.strip()
            # a = tag_name
            #get_or_create 查名为 tag_name 的标签,无则增,有则查 得到的是元组类型    请确保只在 post 请求中使用它
            tag_tuple = models.Tag.objects.filter(is_delete=False).get_or_create(name=tag_name)
            #对得到的元组进行拆包
            tag_instance,tag_boolean = tag_tuple
            #如果 tag_boolean 为 True,即创建了名字为 tag_name 的标签
            # 否则就是查到了名字为 tag_name 的标签,即标签已存在,不创建
            if tag_boolean:
                news_tag_dict = {
                    'id':'tag_instance.id',
                    'name':'tag_instance.name',
                }
                return to_json_data(errmsg='标签创建成功!',data=news_tag_dict)
            else:
                return to_json_data(errno=Code.DATAEXIST,errmsg='标签已存在!')
        else:
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不能为空!')


#传参:
class TagEditView(View):
    """
    /admin/tag/<tag_id>/
    """
    def delete(self,request,tag_id):
        """

        :param request:
        :param tag_id:
        :return:
        """
        tag = models.Tag.objects.only('id').filter(id=tag_id).first()
        if tag:
            tag.is_delete = True
            #只改 is_delete 字段,用于优化
            tag.save(update_fields=['is_delete'])
            #删除后局部刷新,用 ajax,返回给前端 json 格式
            return to_json_data(errmsg='标签删除成功!')
        else:
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')

    def put(self,request,tag_id):
        """

        :param request:
        :param tag_id:
        :return:
        """
        #修改之后的 name 通过 ajax 获取  只有一个参数,用 ajax 即可
        # 1. 获取参数
        json_data = request.body    #byte str
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        # 从前端传进 要修改的 tag
        tag_name = dict_data.get('name')
        # 从数据库中拿到 tag
        tag = models.Tag.objects.only('name').filter(id=tag_id).first()
        if tag:
            #去掉两边的空格
            tag_name = tag_name.strip()
            #如果不加 is_delete 删除的标签再创建或者修改无法实现
            if tag.name == tag_name:
                return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
            if not models.Tag.objects.only('id').filter(name=tag_name, is_delete=False).exists():
            #如果标签没变化,提示报错
                # if tag.name == tag_name:
                #     return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
                tag.name = tag_name
                tag.save(update_fields=['name', 'update_time'])
                return to_json_data(errmsg='标签更新成功!')
            else:
                return to_json_data(errno=Code.PARAMERR,errmsg='标签已经存在!')
        else:
            #有异常的一定会用到 errno
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')

在这里插入图片描述

前端功能实现

templates/admin/index/index.html

{% extends 'admin/base/base.html' %}
{% load static %}
{% block titile %}
    首页
{% endblock %}

{% block content_header %}
    欢迎来到后台管理系统
{% endblock %}


{% block header_option_desc %}
    学习使我快乐
{% endblock %}

templates/admin/news/tags_manage.html

{% extends 'admin/base/base.html' %}

{% load static %}
{% block title %}
  标签管理页
{% endblock %}

{% block content_header %}
  标签管理
{% endblock %}

{% block header_option_desc %}
  标签分类
{% endblock %}


{% block content %}
  <div class="row">
    <div class="col-md-12 col-xs-12 col-sm-12">
      <div class="box box-primary">
        <div class="box-header">
          <button class="btn btn-primary pull-right" id="btn-add-tag">添加标签</button>
        </div>
        <div class="box-body">
          <table class="table table-bordered table-hover">
            <thead>
            <tr>
              <th>标签名称</th>
              <th>文章数量</th>
              <th>操作</th>
            </tr>
            </thead>
            <tbody id="tbody">
            {% for one_tag in tags %}
              <tr data-id="{{ one_tag.id }}" data-name="{{ one_tag.name }}">
                <td>{{ one_tag.name }}</td>
                <td>{{ one_tag.num_news }}</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>
{% endblock %}

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

static/js/admin/news/tags_manage.js

$(function () {


  // 添加标签
  let $tagAdd = $("#btn-add-tag");  // 1. 获取添加按钮
  $tagAdd.click(function () {   // 2. 点击事件
    fAlert.alertOneInput({
      title: "请输入文章标签",
      text: "长度限制在20字以内",
      placeholder: "请输入文章标签",
      confirmCallback: function confirmCallback(inputVal) {
        console.log(inputVal);

        if (inputVal === "") {
          swal.showInputError('标签不能为空');
          return false;
        }

        let sDataParams = {
          "name": inputVal
        };

        $.ajax({
          // 请求地址
          url: "/admin/tags/",  // url尾部需要添加/
          // 请求方式
          type: "POST",
          data: JSON.stringify(sDataParams),
          // 请求内容的数据类型(前端发给后端的格式)
          contentType: "application/json; charset=utf-8",
          // 响应数据的格式(后端返回给前端的格式)
          dataType: "json",
        })
          .done(function (res) {
            if (res.errno === "0") {
              fAlert.alertSuccessToast(inputVal + " 标签添加成功");
              setTimeout(function () {
                window.location.reload();
              }, 1000)
            } else {
              swal.showInputError(res.errmsg);
            }
          })
          .fail(function () {
            message.showError('服务器超时,请重试!');
          });

      }
    });
  });


  // 编辑标签
  let $tagEdit = $(".btn-edit");  // 1. 获取编辑按钮
  $tagEdit.click(function () {    // 2. 点击触发事件
    let _this = this;
    let sTagId = $(this).parents('tr').data('id');
    let sTagName = $(this).parents('tr').data('name');
    fAlert.alertOneInput({
      title: "编辑文章标签",
      text: "你正在编辑 " + sTagName + " 标签",
      placeholder: "请输入文章标签",
      value: sTagName,
      confirmCallback: function confirmCallback(inputVal) {
        console.log(inputVal);
        if (inputVal === sTagName) {
          swal.showInputError('标签名未变化');
          return false;
        }
        let sDataParams = {
          "name": inputVal
        };

        $.ajax({
          // 请求地址
          url: "/admin/tags/" + sTagId + "/",  // url尾部需要添加/
          // 请求方式
          type: "PUT",
          data: JSON.stringify(sDataParams),
          // 请求内容的数据类型(前端发给后端的格式)
          contentType: "application/json; charset=utf-8",
          // 响应数据的格式(后端返回给前端的格式)
          dataType: "json",
        })
          .done(function (res) {
            if (res.errno === "0") {
              // 更新标签成功
              $(_this).parents('tr').find('td:nth-child(1)').text(inputVal);
              swal.close();
              message.showSuccess("标签修改成功");
              setTimeout(function () {
                  window.location.reload();
              },1000)
              //   window.location.reload();
            } else {
              swal.showInputError(res.errmsg);
            }
          })
          .fail(function () {
            message.showError('服务器超时,请重试!');
          });

      }
    });
  });


  // 删除标签
  let $tagDel = $(".btn-del");  // 1. 获取删除按钮
  $tagDel.click(function () {   // 2. 点击触发事件
    let _this = this;
    let sTagId = $(this).parents('tr').data('id');
    let sTagName = $(this).parents('tr').data('name');
    fAlert.alertConfirm({
      title: "确定删除 " + sTagName + " 标签吗?",
      type: "error",
      confirmText: "确认删除",
      cancelText: "取消删除",
      confirmCallback: function confirmCallback() {

        $.ajax({
          // 请求地址
          url: "/admin/tags/" + sTagId + "/",  // url尾部需要添加/
          // 请求方式
          type: "DELETE",
          dataType: "json",
        })
          .done(function (res) {
            if (res.errno === "0") {
              // 更新标签成功
              message.showSuccess("标签删除成功");
              $(_this).parents('tr').remove();
            } else {
              swal.showInputError(res.errmsg);
            }
          })
          .fail(function () {
            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'));
      }
    }
  });

});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值